C语言 访问临时变量中的数组是否为未定义行为?

pftdvrlh  于 2023-11-16  发布在  其他
关注(0)|答案(2)|浏览(82)
typedef struct test{
   char c_arr[1];
} test;

test array[1] = {{1}};

test get(int index){
 return array[index];
}

int main(){
  char* a =  get(0).c_arr;
  return *a;
}

字符串
在这篇文章中,我们用C++解释了这种行为:在结构体中插入数组会导致clang警告
以上代码在使用gccclang编译时不会导致任何警告或错误。
get(0).c_arr是否返回一个指向临时变量的指针,该变量在表达式的末尾被销毁?如果是,是否解引用并返回其值UB?如果是,那么什么是修复它的好方法,也许是这样?

test* get(int index){
 return &array[index];
}

epfja78i

epfja78i1#

char* a =  get(0).c_arr;
  return *a;

字符串
显然是UB;在return运行时,a所引用的内存已经从堆栈中释放出来,如果出现相反的结果,我会非常恼火。想象一下,如果有人这样写:

while (true) {
  char a =  get(0).c_arr[0];
  //...
  a =  get(0).c_arr[0];
  //...
  a =  get(0).c_arr[0];
  //...
  a =  get(0).c_arr[0];
  //...
  a =  get(0).c_arr[0];
  //...
  }


堆栈的浪费是很糟糕的,嵌入式程序员和内核程序员会为此大吵大闹。
然而,以下在C中是有效的:char a = get(0).c_arr[0];这是因为临时的持久性足够长,可以在表达式中使用。

dsf9zpds

dsf9zpds2#

是的,这是未定义的行为。C17标准的相关章节是“6.2.4对象的存储时间”:
一个对象的 * 生命周期 * 是程序执行的一部分,在这段时间内,存储器被保证为它保留。一个对象存在,有一个恒定的地址,33)并在整个生命周期内保留它最后存储的值。34)如果一个对象在它的生命周期之外被引用,那么这个行为是未定义的。
具有结构或联合类型的非左值表达式,其中结构或联合包含具有数组类型的成员(包括递归地,所有包含的结构和联合的成员)引用具有自动存储持续时间和 * 临时生存期 * 的对象。它的生存期从表达式被求值时开始,并且它的初始值是表达式的值。完整的表达结束。
表达式get(0)不是左值,并且struct test包含c_arr,一个数组类型的成员,所以它有临时生命周期。这意味着return *a;是UB,因为它在生命周期之外访问它。
另外,这是不允许的一个重要提示是,如果你使用char c;而不是char c_arr[1];,那么char* a = &get(0).c;将是一个编译时错误,因为你不能接受右值的地址,你写的东西基本上在道德上等同于试图这样做。
我提交了GCC bug 101358LLVM issue #50346关于没有收到警告。

相关问题