NoobCoder
NoobCoder

Reputation: 615

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)

Upvotes: 1

Views: 707

Answers (3)

paxdiablo
paxdiablo

Reputation: 881083

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.

Upvotes: 6

Antonin GAVREL
Antonin GAVREL

Reputation: 11219

You have to account for the program's name so you should write if (argc >= 2) instead. See the man

The value of the argc argument is the number of command line arguments. The argv argument is a vector of C strings; its elements are the individual command line argument strings. The file name of the program being run is also included in the vector as the first element; the value of argc counts this element. A null pointer always follows the last element: argv[argc] is this null pointer.

argv[1] means index 1 but in C you start at index 0.


Fixed code

int main(int argc, char **argv)
{
    
    char file_name[30] = "file.txt";
    
    printf("%s",file_name); /* for debugging : doesn't print it */
    
    if (argc >= 2)
        strcpy(file_name, argv[1]);
    else
        return 1;  // exit program with error code.
    WriteToFile(file_name);
    return 0; // exit with success
}

Upvotes: 2

Thomas Jager
Thomas Jager

Reputation: 5265

When argc is 1, argv is treated as an array of 2 pointers to char. The first is a valid pointer (typically to the program name), the second is NULL.

argv[1] accesses the second element of argv. With no arguments supplied, argc will be 1 and argv[1] will be NULL. You're therefore dereferencing a NULL pointer.

The condition 0 != argc should instead be argc >= 2 or argc > 1, and the condition 0 == argc should be argc < 2 or argc <= 1.

Upvotes: 3

Related Questions