c++stdatofunistd.h

atof coredump with getopt


I'm writing a C++ application which converts fahrenheit to celsius and kelvin, and kelvin to celsius and fahrenheit, etc. Since it's stupid to write an interative application here, I decided to familiarize myself with the getopt function in unistd.h.

Format: F2C -k 273.15

Output:

FAHR CELSIUS KELVIN

32 0 273.15

Here is my code:

#include <iostream>
#include <stdlib.h>
#include <unistd.h>

#define VERSION 0.1
#define HELP help(argv[0])

#define OPTS "vk:f:c:h"
float ver = (float)VERSION;

void help(char *s);
namespace Fahrenheit
{
    float FK(float F) {
        return ((5.0/9.0) * (F - 32.0) + 273.15);
    }

    float FC(float F) {
        return ((5.0/9.0) * (F - 32.0));
    }

    void printfahr(float F) {
        std::cout << "FAHR\t\tCELSIUS\t\tKELVIN" << std::endl;
        std::cout << F << "\t\t" << FC(F) << "\t\t" << FK(F) << std::endl;
    }      
}

namespace Celsius
{
    float CF(float C) {
        return ((C*(9/5)) + 32);
    }
    float CK(float C) {
        return (C+273.15);
    }
    void printc(float C) {
        std::cout << "FAHR\t\tCELSIUS\t\tKELVIN" << std::endl;
        std::cout << CF(C) << "\t\t" << C << "\t\t" << CK(C) << std::endl;
    }
}

namespace Kelvin
{
    float KF(float K) {
        return (((9.0/5.0) * (K-273.15)) + 32);
    }    
    float KC(float K) {
        return (K-273.15);
    }    
    void printk(float K) {
        std::cout << "FAHR\t\tCELSIUS\t\tKELVIN" << std::endl;
        std::cout << KF(K) << "\t\t" << KC(K) << "\t\t" << K << std::endl;
    }
}
int main(int argc, char *argv[])
{
    char arg = '\0';
    if(argc < 2 && argc == 1 && argc > 0) {
        help(argv[0]);
        exit(1);
    }
    /*** Use function getopt() defined in unistd.h to accept 5 arguments: -v, -h, -k, -f, and -c ***/
    while((arg=getopt(argc, argv, OPTS))!=-1)
    {
        float floatarg = atof(optarg);                                                      
        switch(arg)
        {

            case 'v':
                std::cout << "The current version is:" << ver << std::endl;
            break;

            case 'h':
                HELP;
            break;

            case 'k':
                Kelvin::printk(floatarg);
            break;

            case 'f':
                Fahrenheit::printfahr(floatarg);
            break;

            case 'c':
                Celsius::printc(floatarg);
            break;

            default:
                HELP;
            break;
        }
    }
    return 0;
}

void help(char *s) {
    std::cout << "Usage:\t"<< s << " [-option] [argument]" << std::endl;
    std::cout << "option:\t" << "-c [temperature]: convert a Celsius temperature to Fahrenheit and Kelvin" <<  std::endl;
    std::cout << "\t" << "-f [temperature]: convert a Fahrenheit temperature to Celsius and Kelvin" << std::endl;
    std::cout << "\t" << "-h: show help information" << std::endl;
    std::cout << "\t" << "-k [temperature]: convert a Kelvin temperature to Fahrenheit and Celsius" << std::endl;
    std::cout << "\t" << "-v: show version information" << std::endl;
}

My problem is that whenever I use an option that accepts no arguments (like -v) I get a core dump.

dbx has shown me that the SIGSEV occurs at line 70 (float floatarg = atof(optarg);).

When I run the program like this:

./F2C -k 273.15

The math is done correctly and I get a clear printout. It's only when I use -v or -h that my program SIGSEV's.

Extra information:

This program was compiled with the Sun studio compiler suite, version 5.12.

I'm completely baffled as to why my program SIGSEV's. It is inconsistent and makes no sense. I would appreciate any help available.


Solution

  • Should have done some optarg checking. After all, you can't convert null to a float.

    new main():

    #define FLOATARG atof(optarg)
    
    int main(int argc, char *argv[])
    {
        char arg = '\0';
        if(argc < 2 && argc == 1 && argc > 0) {
            help(argv[0]);
            exit(1);
        }
        /*** Use function getopt() defined in unistd.h to accept 5 arguments: -v, -h, -k, -f, and -c ***/
        while((arg=getopt(argc, argv, OPTS))!=-1)
        {                                                      
            switch(arg)
            {
    
                case 'v':
                    std::cout << "The current version is:  << ver << std::endl;
                break;
    
                case 'h':
                    HELP;
                break;
    
                case 'k':
                    Kelvin::printk(FLOATARG);
                break;
    
                case 'f':
                    Fahrenheit::printfahr(FLOATARG);
                break;
    
                case 'c':
                    Celsius::printc(FLOATARG);
                break;
    
                default:
                    HELP;
                break;
            }
        }
        return 0;
    }