2013-11-23 2 views
2

Увидеть необычное поведение при присвоении имени одному и тому же имени, указанному в его typedef. Когда я определяю _same_names_, декларация в clist_create расширитсяНеобычное поведение: typedef и объект с таким же именем

clist *clist; 

и как-то это, кажется, вызывает Segfault. Если я изменю это объявление на другое имя (clist_base), проблема исчезнет, ​​и программа появится нормально. Я уменьшил его к следующему:

/* 
* naming-test.c 
* Strange behaviour when using the same name for an object as its typedef eg. 
* clist *clist; //where clist is a typedef 
*/ 

#include <stdio.h> 
#include <stdlib.h> 

typedef void *(*allocator_t)(size_t size); 
typedef void (*deallocator_t)(void *obj); 

typedef struct clist 
{ 
    void *data; 
    struct clist *next; 
    struct clist *prev; 
    allocator_t allocate; 
    deallocator_t deallocate; 
}clist; 

/* simple wrapper so we can log the allocd memory */ 
void *log_alloc(size_t size) 
{ 
    void *ptr = malloc(size); 
    fprintf(stdout, "+%p\n", ptr); 
    return ptr; 
} 

/* logs the freed memory */ 
void log_free(void *ptr) 
{ 
    fprintf(stdout, "-%p\n", ptr); 
    free(ptr); 
} 

#ifdef _same_name_ 
#define my_clist_object_name clist 
#else /* different names */ 
#define my_clist_object_name clist_base 
#endif 
clist *clist_create(int objsize, int units_in_block, allocator_t allocator, deallocator_t deallocator) 
{ 
    allocator_t default_allocator = malloc; 
    deallocator_t default_deallocator = free; 
    clist *my_clist_object_name; 

    if(!allocator || !deallocator) 
    { 
    allocator = default_allocator; 
    deallocator = default_deallocator; 
    } 
    my_clist_object_name = allocator( /* line 53 */ 
     sizeof(clist)); 
    if(!my_clist_object_name) 
    { 
    goto err0; 
    } 
    my_clist_object_name->data = 0; 
    my_clist_object_name->next = 0; /* line 60 */ 
    my_clist_object_name->prev = 0; 
    my_clist_object_name->allocate = allocator; /* line 62 */ 
    my_clist_object_name->deallocate = deallocator; 
    return my_clist_object_name; 

err0: 
    return 0; 
} 

void clist_delete(clist *clist_head) 
{ 
    clist *cursor, *next; 
    cursor = clist_head; 
    while(cursor) 
    { 
    next = cursor->next; 
    cursor->deallocate(cursor); /* line 77 */ 
    cursor = next; 
    } 
} 
void test_clist_create() 
{ 
    clist *clist_renamed = clist_create(sizeof(int), 2, &log_alloc, &log_free); 
    if(!clist_renamed)return ; 
    clist_delete(clist_renamed); 
} 

int main() 
{ 
    test_clist_create(); 
    fprintf(stdout, "not crashing anymore!!\n"); 
    return 0; 
} 

Я использую этот Makefile (без жалоб от ССЗ)

#Makefile for naming-test.c 
CFLAGS=-g -Wall -std=c89 

same_names: 
    gcc ${CFLAGS} -D_same_name_ naming-test.c -o same_names 
different_names: 
    gcc ${CFLAGS} naming-test.c -o different_names 
clean: 
    rm -f same_names different_names 

Интересная деталь: same_names является segfaulting и GDB дает следующую трассировку

(gdb) bt 
#0 0x00007ffff7a85475 in *__GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 
#1 0x00007ffff7a886f0 in *__GI_abort() at abort.c:92 
#2 0x00007ffff7ac052b in __libc_message (do_abort=<optimized out>, fmt=<optimized out>) at ../sysdeps/unix/sysv/linux/libc_fatal.c:189 
#3 0x00007ffff7ac9d76 in malloc_printerr (action=3, str=0x7ffff7ba2190 "free(): invalid next size (fast)", ptr=<optimized out>) 
    at malloc.c:6283 
#4 0x00007ffff7aceaac in *__GI___libc_free (mem=<optimized out>) at malloc.c:3738 
#5 0x00000000004006b0 in log_free (ptr=0x601010) at naming-test.c:34 
#6 0x0000000000400788 in clist_delete (clist_head=0x601010) at naming-test.c:77 
#7 0x00000000004007d1 in test_clist_create() at naming-test.c:85 
#8 0x00000000004007e4 in main() at naming-test.c:90 

во время работы different_names дает

$ ./different_names 
+0xb88010 
-0xb88010 
not crashing anymore!! 

Valgrind имеет недействительные доступы для same_names:

$ valgrind ./same_names 
==8707== Memcheck, a memory error detector 
==8707== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==8707== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==8707== Command: ./same_names 
==8707== 
+0x51b9040 
==8707== Invalid write of size 8 
==8707== at 0x40071B: clist_create (naming-test.c:60) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== Address 0x51b9048 is 0 bytes after a block of size 8 alloc'd 
==8707== at 0x4C28BED: malloc (vg_replace_malloc.c:263) 
==8707== by 0x400653: log_alloc (naming-test.c:25) 
==8707== by 0x400700: clist_create (naming-test.c:53) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== 
==8707== Invalid write of size 8 
==8707== at 0x400727: clist_create (naming-test.c:61) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== Address 0x51b9050 is 8 bytes after a block of size 8 alloc'd 
==8707== at 0x4C28BED: malloc (vg_replace_malloc.c:263) 
==8707== by 0x400653: log_alloc (naming-test.c:25) 
==8707== by 0x400700: clist_create (naming-test.c:53) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== 
==8707== Invalid write of size 8 
==8707== at 0x400737: clist_create (naming-test.c:62) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== Address 0x51b9058 is not stack'd, malloc'd or (recently) free'd 
==8707== 
==8707== Invalid write of size 8 
==8707== at 0x400743: clist_create (naming-test.c:63) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== Address 0x51b9060 is not stack'd, malloc'd or (recently) free'd 
==8707== 
==8707== Invalid read of size 8 
==8707== at 0x40076F: clist_delete (naming-test.c:76) 
==8707== by 0x4007D0: test_clist_create (naming-test.c:85) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== Address 0x51b9048 is 0 bytes after a block of size 8 alloc'd 
==8707== at 0x4C28BED: malloc (vg_replace_malloc.c:263) 
==8707== by 0x400653: log_alloc (naming-test.c:25) 
==8707== by 0x400700: clist_create (naming-test.c:53) 
==8707== by 0x4007B9: test_clist_create (naming-test.c:83) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== 
==8707== Invalid read of size 8 
==8707== at 0x40077B: clist_delete (naming-test.c:77) 
==8707== by 0x4007D0: test_clist_create (naming-test.c:85) 
==8707== by 0x4007E3: main (naming-test.c:90) 
==8707== Address 0x51b9060 is not stack'd, malloc'd or (recently) free'd 
==8707== 
-0x51b9040 
not crashing anymore!! 
==8707== 
==8707== HEAP SUMMARY: 
==8707==  in use at exit: 0 bytes in 0 blocks 
==8707== total heap usage: 1 allocs, 1 frees, 8 bytes allocated 
==8707== 
==8707== All heap blocks were freed -- no leaks are possible 
==8707== 
==8707== For counts of detected and suppressed errors, rerun with: -v 
==8707== ERROR SUMMARY: 6 errors from 6 contexts (suppressed: 4 from 4) 

, но не для different_names:

$ valgrind ./different_names 
==11207== Memcheck, a memory error detector 
==11207== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==11207== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==11207== Command: ./different_names 
==11207== 
+0x51b9040 
-0x51b9040 
not crashing anymore!! 
==11207== 
==11207== HEAP SUMMARY: 
==11207==  in use at exit: 0 bytes in 0 blocks 
==11207== total heap usage: 1 allocs, 1 frees, 40 bytes allocated 
==11207== 
==11207== All heap blocks were freed -- no leaks are possible 
==11207== 
==11207== For counts of detected and suppressed errors, rerun with: -v 
==11207== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4) 

Что вызывает такое поведение?

Можно ли установить его, но с тем же именем?

ответ

3

Хотя законно использовать один и тот же идентификатор как для типа, так и для переменной, это не очень хорошая идея. Это вводит в заблуждение для всех, кто читает код, и это может смущать вас, потому что это создает некоторые двусмысленности.

Одним из них является:

sizeof(clist) // line 54 

Если clist является как тип и имя переменной, которая одна используется для sizeof? [1] Это важно, потому что переменная clist имеет тип clist* и поэтому размер переменной довольно немного меньше размера структуры, на которую указывает переменная; вам нужно выделить достаточно места для структуры, которая содержит три указателя и два указателя на функции, и вы выделяете достаточно места для одного указателя. Последующие записи в члены структуры будут перезаписывать случайную память.

В этом случае, вы можете заставить sizeof вернуть размер в STRUCT пути быть явным:

sizeof(struct clist) 

Но лично я хотел бы изменить имя ЬурейеГо к чему-то не создает коллизию имен, как CList.


Примечание 1: Ответ заключается в том, что в этом случае переменная выигрывает, поскольку она объявлена ​​в более локальной области. И имя typedef, и имя переменной находятся в одном и том же пространстве имен, поэтому объявление переменной во внутренней области «скрывает» имя typedef, но только в контексте, в котором оба использования были бы возможны.

+0

спасибо @rici.путем изменения в 'sizeof (* clist)' работает. Я согласен с путаницей, что те же имена заканчиваются, но также и как способность не придумывать новые семантические имена для вещей. хороший улов – amdixon