未使用的 typedef 或 struct 定义是否会导致 C 程序中的行为发生变化?

Can an unused typedef or struct definition ever cause a change in behavior within a C program?

假设有如下代码:

struct Container1
{
   int data;
};

typedef int Container2;

int main ()
{
   // code that does stuff but never
   // utilizes any of the 'Container'
   // types.
}

这个例子既没有使用Container1也没有使用Container2。我应该澄清这是一个通用的例子。我的问题是,在特定编译器 的已知 'undefined behavior' 中是否存在任何情况 C 标准的子句 a明显的编译器错误,会导致未使用的结构对已编译程序的行为产生明显的变化。

在这种情况下,应该忽略警告甚至明显的编译错误,这是在询问编译的可执行文件是否会产生截然不同的结果。阻止构建完成的编译器 warnings/errors 不属于该类别。

我的期望是 NO 的硬性回答,但我相信人们在过去曾顺便提到过他们已经看到由于删除或更改定义或什至更改完全 100% 未使用的 typedef 或结构定义的名称而导致的异常错误。我没有这样的例子,所以我会说我对可能发生的 why/how 持保留意见和一点好奇心。

简短的回答是。并且编译器应该生成相同(或等效)的目标代码,无论是否有未使用的结构。

每个经验丰富的工程师都有一个关于由于更改一些无害的东西而导致的奇怪错误的故事。但是像这样的情况 - 这是因为其他一些错误,而不是来自未使用的数据类型。

The question I have is whether there are ANY circumstances in either known 'undefined behavior' from specific compilers or a clause of the C standard or a blatant compiler bug that would result in the presence of an unused struct having a distinct change to a compiled program's behavior

仅当这些标识符 Container1Container2 与程序其他地方的其他标识符冲突时。

但总有可能想出各种或多或少人为的场景,但这些场景是例外:

  • 有些情况下,在这些定义之前包含的某些头文件中存在语法错误,而这些定义以某种方式修复了错误。示例:

    foo.h

    typedef
    

    foo.c

    #include "foo.h"
    
    struct Container1 
    {
       int data;
    };
    
    int x;
    

    如果删除该结构,x 将成为 int 的类型定义。否则 xint.

  • 类型的变量
  • 删除结构显然会影响 __LINE__ 宏之类的东西。

    #include <stdio.h>
    
    struct Container1
    {
       int data;
    };
    
    int main()
    {
      printf("%s", &"hello world"[__LINE__]); 
    }
    

    输出:d

    #include <stdio.h>
    
    int main()
    {
      printf("%s", &"hello world"[__LINE__]);
    }
    

    输出:world

  • C90 只保证最多 6 个唯一字符的外部标识符。因此,如果您在其他地方有一些代码带有另一个名为 Container2 的结构和一些外部标识符,如 extern struct Container2,那么 Container1 结构定义的 presence/absence 可能会给出不同的结果,因为只有标识符的 Contai 部分必须是唯一的。

  • 等等... C是一门复杂的语言,充满了奇怪的漏洞和特殊情况。

理论上是的。考虑这段代码:

int a, b;
printf("%d\n", &a+1 == &b);

Per C 2018 6.5.9 6,当且仅当 b 发生 紧跟在 a 之后 &a+1 == &b 为真地址 space。这是定义的行为,但 C 实现的行为可能导致结果为 0 或 1。

现在假设 C 实现通过一些奇特的数据结构来管理它的标识符。它读取 a 并记住有关它的信息,记住 b 并记住有关它的信息,等等。稍后,当它为对象分配存储时,它会从其标识符数据库中读取。在没有额外约束的情况下,例如影响它如何分配存储的大小和对齐方式,它可能只是按照数据库在遍历数据库以获取标识符时恰好生成标识符的任何顺序分配存储。

可以想象,简单地改变数据库中的标识符可能会改变从数据库读回时产生其他标识符的顺序。因此,引入一个新的类型名称可能会改变顺序,导致 b 在内存中跟随 a 而不是没有新名称。

换句话说,这里的行为,关于名称的管理方式和存储的分配方式,至少在某种程度上是不受控制的——它没有被设计成这样或那样的特定方式。因此它可能会根据本质上不相关的事情而改变。

a blatant compiler bug

如果您的编译器有问题,那么任何事情都有可能发生。例如,编译器中的错误可能是,如果定义了使用名称 "Container2" 的 typedef,则它会在您的主函数中插入一个 "rm -rf /"。

看,现在未使用的 typedef 起作用了。