【问题标题】:Is that invoking a function with the stack pointer pointing to the type only visable in other function vaild使用指向仅在其他函数中可见的类型的堆栈指针调用函数是否有效
【发布时间】:2021-10-20 16:44:33
【问题描述】:

我最近在看ISO/IEC 9899:2017,下面是源链接 https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf

让我感到困惑的是以下语句,因为标准说它不是有效的片段(因为联合类型在函数 f 中不可见)。 但是当我运行这段代码时,我得到了正确的返回值。

#include <stdio.h>

struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2){
  if (p1->m < 0)
  p2->m = -p2->m;
  return p1->m;
}

int g(){
  union U{
    struct t1 s1;
    struct t2 s2;
  } u = {-1};
  return f(&u.s1, &u.s2);
}

int main(){
  int i =  g();
  printf("%d\n",i);
}

所以我的问题是: 是否有效解析联合成员的指针,该成员是在文件范围内定义的结构,指向另一个联合类型不可见的函数?

【问题讨论】:

标签: c


【解决方案1】:

但是当我运行这段代码时,我得到了正确的返回值。

接受代码的给定编译器和表现出预期可观察行为的编译程序并不是代码在语言规范方面正确的可靠指标。因此,它们也不能可靠地预测不同编译器是否会接受代码或生成表现出预期可观察行为的程序。

符合标准的编译器会发出有关某些类别的错误的诊断信息,但它们不需要诊断所有错误,并且即使编译器想要,也有一些类别的错误在编译时无法检测到去做。一般来说,编译和/或执行不正确的代码会产生未定义的行为,这很多时候并不涉及任何错误消息,有时甚至是预期的行为。 底线:程序产生正确或预期的结果并不能证明程序是正确的。


至于主要问题,

让我困惑的是以下语句,因为标准说它不是有效片段(因为联合类型在函数 f 中不可见)。

[...]

所以我的问题是:是否有效[传递] 联合成员的指针,该成员是在文件范围内定义的结构,指向另一个联合类型不可见的函数?

您误解了问题的性质。将指针传递给联合成员并不是固有的。相反,它与访问同一个联合对象的多个成员有关。

一般规则源于

最多一个的值 成员可以随时存储在联合对象中

(C17 6.7.2.1/16)

还有所谓的严格别名规则,第6.5/7段:

对象的存储值只能由具有以下类型之一的左值表达式访问:

[与对象有效类型兼容的类型,加/减限定,或上述任一对应的有符号/无符号类型,或]

  • 在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含联合的成员),或
  • 一种字符类型。

给定联合对象的每个成员的存储都与所有其他成员的存储重叠(这就是联合一次只能保存一个的原因),因此...

  union U{
    struct t1 s1;
    struct t2 s2;
  } u = {-1};

...&amp;u.s1&amp;u.s2 指向同一个存储。对于给定的初始化,它的有效类型是struct s1,它是一个可能更大的存储块的初始部分,其有效类型是union U

具有不同标签的结构类型永远不会相互兼容,因此g() 通过指针&amp;u.s2 访问该初始值将违反严格的别名规则,除了规范雕刻出一个特例:

为了简化联合的使用,我们做了一个特殊的保证:如果联合包含多个共享相同初始序列的结构(见下文),并且如果联合对象当前包含这些结构之一,则允许在联合体的完整类型声明可见的任何地方检查它们中任何一个的公共初始部分。

(C17 6.5.2.3/6)

这正是您正在查看的示例的内容。因为struct t1struct t2 有一个共同的初始序列,由它们各自的成员m 组成,并且因为函数g() 中的联合对象最初确实包含其成员s1 的值,类型为struct t1,在g() 中允许访问u.s2.m,包括通过&amp;u.s2 间接访问,即使u.s2 不是当前包含值的成员。

但是,6.5.2.3/6 不适用于函数f(),因为类型union U 在那里不可见。因此,虽然f() 可以访问p1-&gt;m,但它会产生UB 以尝试访问p2-&gt;m。这是您询问的索赔。

【讨论】:

  • 非常感谢您的回复,这才是我真正想看到的!当我看到它提到将指针传递给不可见 U 的函数是无效的时,我真的认为无效意味着某种错误,例如未声明的联合类型,直到我使用您提供的上下文重新阅读 C17 6.5.2.3/6。
猜你喜欢
  • 1970-01-01
  • 2021-10-27
  • 2020-01-03
  • 2010-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-07
相关资源
最近更新 更多