每次将复合文字分配给循环中的指针时,是否创建了一个新对象?

时间:2013-06-11 05:43:57

标签: c c99 compound-literals

根据C99 standard 6.5.2.5 .9代码:

int *p = (int []){2, 4};
  

将p初始化为指向两个整数数组的第一个元素,   第一个具有值2,第二个具有值4。表达式   这个复合文字必须是常数。未命名的对象   有静态存储时间。

但是当我们做这样的事情时会发生什么:

int* arr[100];
for (int a=0; a<100; a++) {
  arr[a] = (int []){2, 4};
}

是一个新的unnamed object创建循环的每次迭代,或者是每次迭代使用的相同的对象?

如果我们做了类似的事情,结果是否会有所不同:

int* ptr = NULL;
for (int a=0; a<100; a++) {
  ptr = (int []){2, 4};
}

两个可能的选项是:每次循环迭代时创建一个新对象,或者每次循环迭代使用相同的对象。

我感兴趣的是,这种情况下的行为能否以某种方式从标准中所写的内容中扣除,还是由编制者决定。

我已使用此代码在gcc 4.1.2下测试了它:

int main(void) {
  int* arr[100];
  for (int a=0; a<10; a++) {
      arr[a] = (int []){2, 4};
      printf("%p ", arr[a]);
  }
  printf("\n");
}

结果是:

  

0x7fff4c0010a0 0x7fff4c0010a0 0x7fff4c0010a0 0x7fff4c0010a0   0x7fff4c0010a0 0x7fff4c0010a0 0x7fff4c0010a0 0x7fff4c0010a0   0x7fff4c0010a0 0x7fff4c0010a0

我写了一些代码来检查caf的答案:

void fillArr(int* arr[]) {
  for (int a=0; a<4; a++) {
    arr[a] = (int []){a, a};
    printf("%p %d |  ", arr[a], arr[a][0]);
  }
}

void fillArr2(int* arr[]) {
  for (int a=0; a<4; a++) {
    int temp[] = { a, a };
    arr[a] = temp;
    printf("%p %d |  ", arr[a], arr[a][0]);
  }
}


int main(void) {
  int* arr[4];
  printf("\nfillarr1 function scope\n");
  fillArr(arr);


  printf("\nfillArr main scope\n");
  for (int a=0; a<4; a++) {
    printf("%p %d | ", arr[a], arr[a][0]);
  }

  printf("\nfillArr2 function scope\n");
  fillArr2(arr);

  printf("\nfillArr2 main scope\n");
  for (int a=0; a<4; a++) {
    printf("%p %d | ", arr[a], arr[a][0]);
  }

  printf("\n");
}

结果是(用valgrind调用它来检测内存错误):

==19110== Memcheck, a memory error detector
==19110== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==19110== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==19110== Command: ./a.out
==19110==

fillarr1 function scope
0x7ff000830 0 |  0x7ff000830 1 |  0x7ff000830 2 |  0x7ff000830 3 |
fillArr main scope
==19110== Use of uninitialised value of size 8
==19110==    at 0x3E33A41B1D: _itoa_word (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A44F44: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x400664: main (literalstest.c:26)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A41B27: _itoa_word (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A44F44: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x400664: main (literalstest.c:26)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A44FBE: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x400664: main (literalstest.c:26)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A4574A: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x400664: main (literalstest.c:26)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A43C49: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x400664: main (literalstest.c:26)
==19110==
0x7ff000830 864144320 | 0x7ff000830 864144320 | 0x7ff000830 864144320 | 0x7ff000830 864144320 |
fillArr2 function scope
0x7ff000830 0 |  0x7ff000830 1 |  0x7ff000830 2 |  0x7ff000830 3 |
fillArr2 main scope
==19110== Use of uninitialised value of size 8
==19110==    at 0x3E33A41B1D: _itoa_word (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A44F44: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x4006B9: main (literalstest.c:34)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A41B27: _itoa_word (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A44F44: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x4006B9: main (literalstest.c:34)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A44FBE: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x4006B9: main (literalstest.c:34)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A4574A: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x4006B9: main (literalstest.c:34)
==19110==
==19110== Conditional jump or move depends on uninitialised value(s)
==19110==    at 0x3E33A43C49: vfprintf (in /lib64/libc-2.5.so)
==19110==    by 0x3E33A4CAF9: printf (in /lib64/libc-2.5.so)
==19110==    by 0x4006B9: main (literalstest.c:34)
==19110==
0x7ff000830 864144320 | 0x7ff000830 864144320 | 0x7ff000830 864144320 | 0x7ff000830 864144320 |
==19110==
==19110== HEAP SUMMARY:
==19110==     in use at exit: 0 bytes in 0 blocks
==19110==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==19110==

因此文字仅在声明它们的函数内部可用,在离开函数后超出范围,之后访问它们是未定义的行为。

1 个答案:

答案 0 :(得分:2)

你误读标准。您提供的示例将启动“文件范围定义... ”,但您的代码无法显示在文件范围内。

§6.5.2.5p6表示如果复合文字出现在函数体中,

  

...它具有与封闭相关的自动存储持续时间   块。

所以,没有歧义。在这种情况下,复合文字具有自动存储持续时间,持续时间直到包含它的循环块结束 - 概念上,为循环的每次迭代创建并销毁的新复合文字,但是这些文字的生命周期不重叠,实现可能会重复使用相同的空间。你写的内容与此没什么不同:

int *arr[100];
for (int a=0; a<100; a++) {
  int temp[] = { 2, 4 };
  arr[a] = temp;
}

...只是在复合文字的情况下,数组是未命名的。寿命是一样的。