简体   繁体   中英

Struct initialization leads to seg fault

The following code causes a segmentation fault. The executable is named './struct'

#include <stdio.h>
#define VERSION_NUMBER_LEN 32
#define MAX_DESCRIPTION_COUNT 32
#define DESCRIPTION_LEN 128
int main(void)
{
    struct foo {
        char number[VERSION_NUMBER_LEN + 1];
        char description[MAX_DESCRIPTION_COUNT][DESCRIPTION_LEN];
    };

    struct foo asdf = {
        "1.1", { "clap", "clap", "stomp", NULL }
    };

    struct foo hjkl = {
        "1.2", { "clop", "clop", "stamp", NULL }
    };

    int i;
    printf( "%s\n", asdf.number );
    for( i = 0; (asdf.description)[i] != NULL; i++ ){
        printf( "\t%s\n", (asdf.description)[i]);
    }
    printf("\n");
    printf( "%s\n", hjkl.number );
    for( i = 0; (hjkl.description)[i] != NULL; i++ ){
        printf( "\t%s\n", (hjkl.description)[i]);
    }
}

The output looks like this:

1.1
    clap
    clap
    stomp






��
    N���~�����������ջ�����e���t�����������A���P���b���������������̽��㽊����,���V���g���y���������������̾��    ���k�����������Ͽ��迊�
    迊�





    ome/tiger
    56
    y
    vZxy/ssh
    ptop:/tmp/.ICE-unix/2710
    usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:~/bin:~/vitetris-0.3.6:/var/lib/gems/1.8/bin/
    baz
    GNOME_KEYRING_PID=2692
    t \w\n\$
    XDG_DATA_DIRS=/usr/share/gnome:/usr/local/share/:/usr/share/
    9a6bf0ef61ded7872065094fca55d1
    se
Segmentation fault

I ran valgrind:

$ valgrind -v --leak-check=full --track-origins=yes ./struct  

<snip>

==15228== Conditional jump or move depends on uninitialised value(s)
==15228==    at 0x402605B: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Uninitialised value was created by a stack allocation
==15228==    at 0x4060B01: (below main) (libc-start.c:96)
==15228==
==15228== Conditional jump or move depends on uninitialised value(s)
==15228==    at 0x4026067: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Uninitialised value was created by a stack allocation
==15228==    at 0x4060B01: (below main) (libc-start.c:96)
==15228==
==15228== Invalid read of size 1
==15228==    at 0x4026058: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Address 0xbec1007c is not stack'd, malloc'd or (recently) free'd
==15228==
==15228==
==15228== Process terminating with default action of signal 11 (SIGSEGV)
==15228==  Access not within mapped region at address 0xBEC1007C
==15228==    at 0x4026058: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  If you believe this happened as a result of a stack
==15228==  overflow in your program's main thread (unlikely but
==15228==  possible), you can try to increase the size of the
==15228==  main thread stack using the --main-stacksize= flag.
==15228==  The main thread stack size used in this run was 8388608.
==15228== Syscall param write(buf) points to uninitialised byte(s)
==15228==    at 0x4107DC3: __write_nocancel (syscall-template.S:82)
==15228==    by 0x40B0A1E: new_do_write (fileops.c:530)
==15228==    by 0x40B0D35: _IO_do_write@@GLIBC_2.1 (fileops.c:503)
==15228==    by 0x40B181C: _IO_file_overflow@@GLIBC_2.1 (fileops.c:881)
==15228==    by 0x40B2DED: _IO_flush_all_lockp (genops.c:849)
==15228==    by 0x40B3A4F: _IO_cleanup (genops.c:1010)
==15228==    by 0x41670F0: ??? (in /lib/tls/i686/cmov/libc-2.11.1.so)
==15228==    by 0x401F4F3: _vgnU_freeres (vg_preloaded.c:62)
==15228==    by 0xBEC0D5F7: ???
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Address 0x402a054 is not stack'd, malloc'd or (recently) free'd
==15228==  Uninitialised value was created by a stack allocation
==15228==    at 0x4060B01: (below main) (libc-start.c:96)

<snip>

==15228== HEAP SUMMARY:
==15228==     in use at exit: 0 bytes in 0 blocks
==15228==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==15228==
==15228== All heap blocks were freed -- no leaks are possible
==15228==
==15228== ERROR SUMMARY: 7 errors from 4 contexts (suppressed: 12 from 7)
==15228==
==15228== 1 errors in context 1 of 4:
==15228== Syscall param write(buf) points to uninitialised byte(s)
==15228==    at 0x4107DC3: __write_nocancel (syscall-template.S:82)
==15228==    by 0x40B0A1E: new_do_write (fileops.c:530)
==15228==    by 0x40B0D35: _IO_do_write@@GLIBC_2.1 (fileops.c:503)
==15228==    by 0x40B181C: _IO_file_overflow@@GLIBC_2.1 (fileops.c:881)
==15228==    by 0x40B2DED: _IO_flush_all_lockp (genops.c:849)
==15228==    by 0x40B3A4F: _IO_cleanup (genops.c:1010)
==15228==    by 0x41670F0: ??? (in /lib/tls/i686/cmov/libc-2.11.1.so)
==15228==    by 0x401F4F3: _vgnU_freeres (vg_preloaded.c:62)
==15228==    by 0xBEC0D5F7: ???
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Address 0x402a054 is not stack'd, malloc'd or (recently) free'd
==15228==  Uninitialised value was created by a stack allocation
==15228==    at 0x4060B01: (below main) (libc-start.c:96)
==15228==
==15228==

==15228== 1 errors in context 2 of 4:
==15228== Invalid read of size 1
==15228==    at 0x4026058: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Address 0xbec1007c is not stack'd, malloc'd or (recently) free'd
==15228==
==15228==
==15228== 1 errors in context 3 of 4:
==15228== Conditional jump or move depends on uninitialised value(s)
==15228==    at 0x402605B: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Uninitialised value was created by a stack allocation
==15228==    at 0x4060B01: (below main) (libc-start.c:96)
==15228==
==15228==
==15228== 4 errors in context 4 of 4:
==15228== Conditional jump or move depends on uninitialised value(s)
==15228==    at 0x4026067: __GI_strlen (mc_replace_strmem.c:284)
==15228==    by 0x408A79E: vfprintf (vfprintf.c:1617)
==15228==    by 0x40912BF: printf (printf.c:35)
==15228==    by 0x8048910: main (in /home/tiger/dev/development/c/play/struct)
==15228==  Uninitialised value was created by a stack allocation
==15228==    at 0x4060B01: (below main) (libc-start.c:96)
==15228==
--15228--
--15228-- used_suppression:     12 dl-hack3-cond-1
==15228==
==15228== ERROR SUMMARY: 7 errors from 4 contexts (suppressed: 12 from 7)

Ok... so I have a couple of memory locations which valgrind shows as uninitialized, but I don't see how... the structure and the strings inside are defined statically, and each instance of the structure is explicitly declared.

I think that the segmentation fault is occuring when one of the elements in the second structure (hjkl) is accessed.

Ran gdb...

(gdb) p asdf
$1 = {number = "1.1", '\000' <repeats 29 times>, description = {"clap", '\000' <repeats 123 times>, "clap", '\000' <repeats 123 times>, 
"stomp", '\000' <repeats 122 times>, '\000' <repeats 127 times> <repeats 29 times>}}

(gdb) p hjkl
$2 = {number = "1.2", '\000' <repeats 29 times>, description = {"clop", '\000' <repeats   123 times>, "clop", '\000' <repeats 123 times>, 
"stamp", '\000' <repeats 122 times>, '\000' <repeats 127 times> <repeats 29 times>}}

I'm just not seeing what's causing the segmentation fault...

Your test (asdf.description)[i] != NULL is busted and will never be true. Here's the fix:

#include <stdio.h>
#define VERSION_NUMBER_LEN 32
#define MAX_DESCRIPTION_COUNT 32
#define DESCRIPTION_LEN 128
int main(void)
{
    struct foo {
        char number[VERSION_NUMBER_LEN + 1];
        char description[MAX_DESCRIPTION_COUNT][DESCRIPTION_LEN];
    };

    struct foo asdf = {
        "1.1", { "clap", "clap", "stomp", "" }
    };

    struct foo hjkl = {
        "1.2", { "clop", "clop", "stamp", "" }
    };

    int i;
    printf( "%s\n", asdf.number );
    for( i = 0; (asdf.description)[i][0] != 0; i++ ){
        printf( "\t%s\n", (asdf.description)[i]);
    }
    printf("\n");
    printf( "%s\n", hjkl.number );
    for( i = 0; (hjkl.description)[i][0] != 0; i++ ){
        printf( "\t%s\n", (hjkl.description)[i]);
    }
}

The problem is not with your initialization strings per se, but rather with the confusion of character arrays with character pointers.

In particular, this does not do what you think it does:

struct foo asdf = {
    "1.1", { "clap", "clap", "stomp", NULL }
};

You're initializing foo::description[][] with the strings clap, clap, and stomp OK, but then you're assigning the first character of the second string to NULL . This would work if you were assigning to pointers, but you're trying to convert NULL to a character array and not setting a pointer to a character to NULL , if that makes any sense.

You're checking if the pointer is NULL, which it will never be since the array is pre-declared.

So simply changing

for( i = 0; (hjkl.description)[i] != NULL; i++ ){

to

for( i = 0; *(hjkl.description)[i] != NULL; i++ ){

That is to say, checking for a NULL character in the first index of each of the strings in your array instead of trying to check the (impossible) condition of the character pointer itself being NULL .

The final completed code:

#include <stdio.h>
#define VERSION_NUMBER_LEN 32
#define MAX_DESCRIPTION_COUNT 32
#define DESCRIPTION_LEN 128
int main(void)
{
    struct foo {
        char number[VERSION_NUMBER_LEN + 1];
        char description[MAX_DESCRIPTION_COUNT][DESCRIPTION_LEN];
    };

    struct foo asdf = {
        "1.1", { "clap", "clap", "stomp", NULL }
    };

    struct foo hjkl = {
        "1.2", { "clop", "clop", "stamp", NULL }
    };

    int i;
    printf( "%s\n", asdf.number );
    for( i = 0; *(asdf.description)[i] != NULL; i++ ){
        printf( "\t%s\n", (asdf.description)[i]);
    }
    printf("\n");
    printf( "%s\n", hjkl.number );
    for( i = 0; *(hjkl.description)[i] != NULL; i++ ){
        printf( "\t%s\n", (hjkl.description)[i]);
    }
}

Your code would work if the struct looked like this:

struct foo {
    char number[VERSION_NUMBER_LEN + 1];
    char *description[MAX_DESCRIPTION_COUNT];
};

Basically, there's (from what I can see) no real need to hard code the actual description character arrays themselves, only the number of them. The rest of your code as-is would work.

Also, if you look at the warnings during compilation (assuming NULL is defined as ((void *)0) as it is on most modern C compilers), you'll get the following warnings:

test.c:13:43: warning: incompatible pointer to integer conversion initializing
      'char' with an expression of type 'void *';
        "1.1", { "clap", "clap", "stomp", NULL }
                                          ^~~~
/usr/include/stdio.h:82:14: note: expanded from:
#define NULL __DARWIN_NULL
             ^
/usr/include/sys/_types.h:91:23: note: expanded from:
#define __DARWIN_NULL ((void *)0)
                      ^~~~~~~~~~~
test.c:13:43: warning: suggest braces around initialization of subobject
      [-Wmissing-braces]
        "1.1", { "clap", "clap", "stomp", NULL }
                                          ^~~~
/usr/include/stdio.h:82:14: note: expanded from:
#define NULL __DARWIN_NULL
             ^
/usr/include/sys/_types.h:91:23: note: expanded from:
#define __DARWIN_NULL ((void *)0)
                      ^~~~~~~~~~~
test.c:17:43: warning: incompatible pointer to integer conversion initializing
      'char' with an expression of type 'void *';
        "1.2", { "clop", "clop", "stamp", NULL }
                                          ^~~~
/usr/include/stdio.h:82:14: note: expanded from:
#define NULL __DARWIN_NULL
             ^
/usr/include/sys/_types.h:91:23: note: expanded from:
#define __DARWIN_NULL ((void *)0)
                      ^~~~~~~~~~~
test.c:17:43: warning: suggest braces around initialization of subobject
      [-Wmissing-braces]
        "1.2", { "clop", "clop", "stamp", NULL }
                                          ^~~~
/usr/include/stdio.h:82:14: note: expanded from:
#define NULL __DARWIN_NULL
             ^
/usr/include/sys/_types.h:91:23: note: expanded from:
#define __DARWIN_NULL ((void *)0)
                      ^~~~~~~~~~~
4 warnings generated.

While the macro NULL is intended (at least, was by those who first defined it) to be used exclusively as a pointer value, it's often #define d with a simple:

#define NULL 0

This is probably the case for your implementation (not that it matters here, except for illustration; it could be defined as (void *)0 without changing the results below—but this would result in a compile-time complaint about your initializers). Let's expand that first for loop with the above in mind:

for (i = 0; (asdf.description)[i] != 0; i++) {
    printf( "\t%s\n", (asdf.description)[i]);
}

(Side note: The parentheses here are not needed as the binding of the . and subscript operators is already the one forced by the parentheses.) Each asdf.description[i] names one entire array (of size DESCRIPTION_LEN ) of char . You are therefore comparing:

<some array of char> != 0

The "value" of an array object is a pointer to the array's first element, so this has the same meaning as:

&asdf.description[i][0] != 0

Comparison of a pointer value ( &asdf.description[i][0] ) to the integer constant zero tests whether the pointer is NULL (not "the macro NULL but rather "the system's internal representation of a NULL pointer"). The address of a valid pointer never compares equal to 0, so the loop runs (in effect) "forever" (certainly until i >= 32).

Eventually, the call to printf passes a pointer value that results in the segmentation fault you see.

Presumably what you really meant to do was to initialize the array following the last valid one with all-zero-byte char s (or at least an initial zero byte). In that case, the loop test should read:

asdf.description[i][0] != '\0'

You might also consider the possibility that the 32-element array (of arrays of DESCRIPTION_LEN of char ) is completely filled with valid arrays-of-char. In this case, you should check the value of i before looking at asdf.description[i][anything] :

i < MAX_DESCRIPTION_COUNT && asdf.description[i][0] != '\0'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM