Ugur KAYA
Ugur KAYA

Reputation: 177

C - Last element of char* array

For below sample code;

char* g_commands[]={
    "abcd",
    NULL
};

int main()
{
    
    g_commands[1] = "efg";
    
    char ** tmp = &g_commands[0];
    
    for(; *tmp != NULL; tmp++){
        printf("%s", *tmp);
    }

    return 0;
}

since tmp is pointing to the pointers in g_commands array in a loop, after I assign "efg" to g_commands[1], I expect the loop create a segmentation fault since the last element of g_commands is not null anymore. But the program finishes without exception and prints abcdefg successfully.

Why is it so? Does the compiler add NULL to the end of char* array as well?

Upvotes: 1

Views: 1397

Answers (3)

Vlad from Moscow
Vlad from Moscow

Reputation: 311048

The program has undefined behavior. In particular it means that a program can produce as an expected or as unexpected result.

I expect the loop create a segmentation fault since the last element of g_commands is not null anymore

The program works without a segmentation fault because the array g_commands

char* g_commands[]={
    "abcd",
    NULL
};

is defined in the global namespace and there is no other definition of an object after the array. Such a declaration has static storage duration and compilers usually set this memory to zeroes.

If you will move the definition in main like

#include <stdio.h>
/*
char* g_commands[]={
    "abcd",
    NULL
};
*/
int main()
{
    char* g_commands[]={
        "abcd",
        NULL
    };
    
    g_commands[1] = "efg";
    
    char ** tmp = &g_commands[0];
    
    for(; *tmp != NULL; tmp++){
        printf("%s", *tmp);
    }

    return 0;
}

then the probability that a segmentation fault will occur is very high.

Upvotes: 2

I expect the loop create a segmentation fault since the last element of g_commands is not null anymore. But the program finishes without exception and prints abcdefg successfully.

Why is it so? Does the compiler add NULL to the end of char* array as well?

You invoke undefined behavior as you dereference the pointer to pointer tmp pointing past the end of the array and attempt to printing an indeterminate string with printf("%s", *tmp).

Undefined behavior does not need to provide wrong results. It is a misconception to think that things be right when they appear to be right.

You can't expect anything. It also makes no big sense to explain the reasons and ways of undefined behavior as it is plain irrelevant for you writing production code.

I know some people which like to investigate these and seeing the implementation's behavior, but generally seen these aren't things to focus on deeper if you're interested about writing insusceptible, portable and reliable code.

Upvotes: 3

datenwolf
datenwolf

Reputation: 162289

Let's go through it step by step.

char* g_commands[]={
    "abcd",
    NULL
};

int main()
{
    
    g_commands[1] = "efg";

At this point g_commands was altered as if you'd initilaized it in the following way:

// char* g_commands[]={
//    "abcd",
//    "efg"
// };

Note, that there's no longer a terminating null pointer in g_commands from this point on.

The following

    char ** tmp = &g_commands[0];

could have as well been written as

// char ** tmp = g_commands;

Now when you iterate over the elements of g_commands, you're testing for tmp dereferencing to a null pointer. Unfortunately you did overwrite the last element of g_commands with a non-null pointer previously, so this

    for(; *tmp != NULL; tmp++){
        printf("%s", *tmp);
    }

is running beyond the bounds of the array and invoking undefined behavior.

    return 0;
}

Upvotes: 0

Related Questions