是否在临时变量未定义行为中访问数组?

Posted

技术标签:

【中文标题】是否在临时变量未定义行为中访问数组?【英文标题】:Is accessing an array within a temporary variable undefined behaviour? 【发布时间】:2021-07-07 03:17:56 【问题描述】:
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++ 解释的:Accessing an array within a struct causes warnings with clang

使用gccclang 编译时,上述代码不会导致任何警告或错误。

get(0).c_arr 是否返回一个指向在表达式末尾被销毁的临时变量的指针?如果是这样,是否取消引用并返回其值 UB?如果是这样,那么修复它的好方法是什么,也许是这个?

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

【问题讨论】:

它不使用临时(正确地自动)变量; array 具有静态存储分配;并且index 从中计算的事实不会改变其分配。它从此计算复制到本地(匿名)位置以引用 c_arr,并没有改变。但是,您正走在危险的道路上;如果您的函数返回 a 而不是 *a,那么它会做错事。 @mevets 当get 通过返回struct test 时,为什么array 具有静态存储分配很重要? 我在这里很困惑,在 C++ 中这是一个临时变量,但在 C 中不是?什么规则使这种差异发生? @DavidC.Rankin 进行实验来证明或反驳 UB 的问题是,UB 的一种表现形式是完全按照您在没有 UB 时所期望的方式工作。所以实验不会证明什么。 呃,等等,很可能是我。函数返回到array 的元素(不是array 中的地址)。现在我看到了问题出在哪里(为什么没有理智的人会这样编码)所以问题是您正在分配基于函数返回本身的c_arr(指向第一个元素的指针)的地址。 (它确实使用了一个真正的临时地址来提供地址——该死的是我:) 【参考方案1】:
  char* a =  get(0).c_arr;
  return *a;

显然是UB; a 引用的内存在return 运行时已从堆栈中释放,相反的结果让我非常恼火。想象一下,如果有人写道:

  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]; 这是因为临时的持续时间足够长,可以在表达式中使用。

【讨论】:

所以temporary 变量的概念也适用于C。 @Dan:当然。还有什么可以返回结构而不立即将其分配给编译到的变量? @user3386109:是的,这是一个临时的,因为它不是任何命名变量。【参考方案2】:

是的,这是未定义的行为。 C17 标准的相关部分是“6.2.4 对象的存储持续时间”:

对象的生命周期是保证存储的程序执行部分 为它保留。一个对象存在,具有一个常量地址,33) 并保留其最后存储的值 34) 如果对象在其生命周期之外被引用,则行为未定义。

具有结构或联合类型的非左值表达式,其中结构或联合包含 具有数组类型的成员(递归地包括所有包含的结构和联合的成员) 指具有自动存储期限和临时生命周期的对象。36)它的生命周期开始 当表达式被计算并且它的初始值是表达式的值时。它的生命周期结束 当包含完整表达式的评估结束时。

表达式get(0) 不是左值,而struct test 包含c_arr,这是一个数组类型的成员,所以它有临时生命周期。这意味着 return *a; 是 UB,因为它在其生命周期之外访问它。

另外,这是不允许的一个重要提示是,如果您使用 char c; 而不是 char c_arr[1];,那么 char* a = &get(0).c; 将是编译时错误,因为您无法获取一个右值,你写的基本上在道德上等同于尝试这样做。

我提交了 GCC bug 101358 和 LLVM bug 51002 关于未收到此警告的信息。

【讨论】:

这就是它的症结所在。干得好,讨论得很好。

以上是关于是否在临时变量未定义行为中访问数组?的主要内容,如果未能解决你的问题,请参考以下文章

学号20175313 《程序设计中临时变量的使用》第八周

尝试以另一种方法访问临时数组

20175314薛勐 程序设计中临时变量的使用(课下测试,选做)

表变量

是否可以在私有方法中定义公共变量?

js 临时笔记