Search code examples
cfilesegmentation-faultfopenargv

Segmentation fault when file is not found even if I try to create it in C


I'm writing a code that appends text to a file. It uses fopen in the beginning of WriteToFile so even if the file does not exist, it creates it.

But, what happens when I enter no file at all? no arguments at all? Just ./a.out?

Segmentation fault.

I don't know why, it seems to me I did everything fine to avoid any problems.

int main(int argc, char **argv)
{
    
    char file_name[30] = "file.txt";
    
    printf("%s",file_name); /* for debugging : doesn't print it */
    
    if (0 != argc)
    {
        strcpy(file_name, argv[1]);
    }
    
    WriteToFile(file_name);
    
}

OR (in case I can't really put a string literal into char array):

char file_name[30];
    
    if (0 == argc)
    {
       strcpy(file_name, "file.txt");
    }
    else
    {
        strcpy(file_name, argv[1]);
    }

For both of the cases i'm getting

Segmentation fault (core dumped)


Solution

  • if (0 != argc)
    

    The argc value is normally(a) the count of arguments including the program name. Hence, running ./a.out will have an argc of one rather than zero. And, since argv[argc] is usually NULL, dereferencing it is not going to end well :-)

    If you want to ensure another argument is available, you can use:

    if (argc > 1) {            // Have both argv[0] AND argv[1].
        nowSafeToUse(argv[1]);
    }
    

    In more detail, the C11 standard states:

    If they are declared, the parameters to the main function shall obey the following constraints:

    • The value of argc shall be nonnegative.
    • argv[argc] shall be a null pointer.
    • If the value of argc is greater than zero, the array members argv[0] through argv[argc-1] inclusive shall contain pointers to strings, which are given implementation-defined values by the host environment prior to program startup. The intent is to supply to the program information determined prior to program startup from elsewhere in the hosted environment. If the host environment is not capable of supplying strings with letters in both uppercase and lowercase, the implementation shall ensure that the strings are received in lowercase.
    • If the value of argc is greater than zero, the string pointed to by argv[0] represents the program name; argv[0][0] shall be the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[1] through argv[argc-1] represent the program parameters.
    • The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.

    As an aside regarding this line:

    printf("%s",file_name); /* for debugging : doesn't print it */
    

    If this is not printing, it's probably because standard output is line buffered (default if it's determined to be an interactive device, otherwise fully buffered).

    By not outputting a \n at the end, the characters are probably still sitting in a buffer somewhere, ready to be written. The crash will probably kill off the process without flushing the buffer. So a simple solution may be just to use one of:

    printf("%s",file_name);
    puts(file_name);
    

    As another aside, you're going to get into trouble if the filename you enter is larger than 29 characters since it will overflow file_name, allowing for the \0 at the end as well.

    A better approach may be just to use either your default string or argv[1] directly (without copying), something like:

    int main(int argc, char **argv) {
        char *file_name = (argv > 1)
            ? argv[1]
            : "file.txt";
        printf("%s\n", file_name); // for debugging : probably does print it :-)
        
        WriteToFile(file_name);
    }
    

    (a) Not required by the standard since it allows for implementation-specific differences, but it's the usual case. Specifically, the phrase:

    ... which are given implementation-defined values by the host environment prior to program startup. The intent is to supply to the program information determined prior to program startup from elsewhere in the hosted environment.

    pretty much means it can do whatever it wants :-)

    A related answer (though a non-duplicate question) can be found here.