【问题标题】:Unusual behaviour: typedef and object of same name异常行为:typedef 和同名对象
【发布时间】:2013-11-23 02:27:03
【问题描述】:

在命名与其 typedef 同名的对象时看到一些不寻常的行为。 当我定义 _same_names_ 时,clist_create 中的声明将扩展为

clist *clist;

不知何故,这似乎导致了段错误。如果我将此声明更改为另一个名称(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(没有来自 gcc 的投诉)

#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 是段错误,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)

但不适用于不同的名称:

$ 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)

是什么导致了这种行为?

是否可以修复但保持相同的名称?

【问题讨论】:

    标签: c segmentation-fault typedef naming c89


    【解决方案1】:

    虽然对类型和变量使用相同的标识符是合法的,但这不是一个好主意。任何阅读代码的人都会感到困惑,你也会感到困惑,因为它会产生一些歧义。

    其中之一是:

    sizeof(clist)   // line 54
    

    如果clist 既是类型又是变量名,sizeof 使用哪一个? [1] 这很重要,因为变量clist 的类型为clist*,因此变量的大小比变量指向的结构的大小要小很多;您需要为包含三个指针和两个函数指针的结构分配足够的空间,并且您只为单个指针分配足够的空间。对结构成员的后续写入将覆盖随机内存。

    在这种情况下,您可以强制 sizeof 通过显式返回结构的大小:

    sizeof(struct clist)
    

    但我个人会将 typedef 名称更改为不会造成名称冲突的名称,例如 CList


    注 1:答案是在这种情况下,变量获胜,因为它是在更局部的范围内声明的。 typedef 名称和变量名称都在同一个命名空间中,因此内部作用域中的变量声明“隐藏”了 typedef 名称,但仅限于两种使用都可能的上下文中。

    【讨论】:

    • 感谢@rici。通过更改为sizeof(*clist) 工作。我同意相同名称最终导致的混淆,但也喜欢不必为事物想出新的语义名称的能力。很好的收获
    猜你喜欢
    • 2017-04-23
    • 1970-01-01
    • 2017-12-30
    • 1970-01-01
    • 1970-01-01
    • 2021-08-26
    • 2015-03-17
    相关资源
    最近更新 更多