可以在其定义中采用具有自动存储持续时间的变量的地址吗?

时间:2018-06-03 06:37:48

标签: c language-lawyer c11

是否允许在其定义的右侧获取对象的地址,如下面foo()中所示:

typedef struct { char x[100]; } chars;

chars make(void *p) {
  printf("p = %p\n", p);
  chars c;
  return c;
}

void foo(void) {
  chars b = make(&b);
}

如果允许,是否对其使用有任何限制,例如,打印好了,我可以将它与另一个指针等进行比较吗?

在实践中,它似乎在我测试的编译器上进行编译,大部分时间都是预期的行为(但是not always),但这远非保证。

3 个答案:

答案 0 :(得分:8)

要回答标题中的问题,请记住您的代码示例,是的。 C标准在§6.2.4中说得很多:

  

对象的生命周期是程序执行期间的一部分   保证保留哪个存储空间。存在一个对象,   具有恒定的地址,并始终保留其最后存储的值   它的一生。

     

对于没有可变长度数组类型的对象,   它的生命周期从进入到它所在的区块延伸   关联,直到该块的执行以任何方式结束。

所以是的,你可以从声明的角度获取变量的地址,因为对象在这一点上有地址并且在范围内。一个简明的例子如下:

void *p = &p;

它的用途很少,但完全有效。

关于你的第二个问题,你能用它做些什么。我可以说,在初始化完成之前,我不会使用该地址来访问该对象,因为初始化程序中表达式的评估顺序是未经验证的(§6.7.9)。你可以很容易地找到你的脚射击。

确定这种情况的一个地方是定义需要自引用的各种表格数据结构。例如:

typedef struct tab_row {
  // Useful data
  struct tab_row *p_next;
} row;

row table[3] = {
  [1] = { /*Data 1*/, &table[0] },
  [2] = { /*Data 2*/, &table[1] },
  [0] = { /*Data 0*/, &table[2] },
};

答案 1 :(得分:7)

  

6.2.1标识符范围

     
      
  1. 结构,联合和枚举标记的范围在外观之后开始   声明标记的类型说明符中的标记。每个枚举常量都具有该范围   在枚举器列表中出现其定义的枚举器之后开始。的不限   其他标识符的范围在其声明者完成之后开始。
  2.   

chars b = make(&b);
//    ^^

声明符为b,因此它在自己的初始化程序中。

  

6.2.4对象的存储持续时间

     
      
  1. 对于没有可变长度数组类型的[自动]对象,其生命周期延长   从进入与之关联的块直到该块的执行结束   无论如何。
  2.   

所以在

{ // X
  chars b = make(&b);
}

b的生命周期从X开始,因此在初始化程序执行时,它既活着又在范围内。

据我所知,这实际上与

完全相同
{
  chars b;
  b = make(&b);
}

没有理由你不能在那里使用&b

答案 2 :(得分:0)

问题已经得到解答,但作为参考,它没有多大意义。这就是你编写代码的方法:

typedef struct { char x[100]; } chars;

chars make (void) {
  chars c;
  /* init c */
  return c;
}

void foo(void) {
  chars b = make();
}

或者最好在ADT或类似情况下,返回指向malloc:ed对象的指针。按值传递结构通常不是一个好主意。