6

在 C 和 C++ 的情况下,我对两个文件中相同函数和全局变量的不同声明有 2 个问题。

  1. 不同的函数声明

    考虑以下代码片段:

    文件_1.c

    void foo(int a);
    
    int main(void)
    {
        foo('A');
    }
    

    文件_2.c

    #include <stdio.h>
    
    void foo(char a)
    {
        printf("%c", a); //prints 'A' (gcc)
    }
    

    正如我们所看到的,原型与位于 file_2.c中的定义不同,但是,该函数会打印预期值。

    如果涉及到 C++,由于foo(int)链接时未定义引用,上述程序无效。它可能是由其他函数签名的存在引起的 - 与 C 相比,其中函数名称不包含任何指示函数参数类型的额外字符。

    但是当涉及到 C 时,那又如何呢?由于具有相同名称的原型具有相同的签名,无论参数的数量及其类型如何,链接器都不会发出错误。但是这里执行了哪些类型转换?它看起来像这样:'A'-> int-> 回到char? 或者这种行为可能是未定义/实现定义的?

  2. 全局变量的不同声明

    我们有两个文件和同一个全局变量的两个不同声明:

    文件_1.c

    #include <stdio.h>
    
    extern int a;
    
    int main(void)
    {
        printf("%d", a); //prints 65 (g++ and gcc)
    }
    

    文件_2.c

    char a = 'A';
    

    在 C 和 C++ 中,输出都是 65。

    虽然我想知道这两个标准对这种情况的看法。

    在 C11 标准中,我发现了以下片段:

    J.5.11 多个外部定义 (附件 J.5 通用扩展)
    一个对象的标识符可能有多个外部定义,无论是否显式使用关键字 extern;如果定义不一致,或者不止一个被初始化,则行为未定义(6.9.2)。

    请注意,它指的是存在两个或多个定义,在我的代码中只有一个,所以我不确定这篇文章在这种情况下是否是一个很好的参考点......

4

4 回答 4

5

Q1。根据 C99 规范,第 6.5.2.2.9 节,这是 C 中未定义的行为:

如果函数定义的类型与表示被调用函数的表达式所指向的(表达式的)类型不兼容,则行为未定义。

表达式“指向”一个采用 的函数int,而该函数被定义为采用char

Q2。变量的情况也是未定义的行为,因为您正在读取或分配intto/from char。假设 4 字节整数,这将访问超过有效内存位置的三个字节。您可以通过声明更多变量来测试它,如下所示:

char a = 'A';
char b = 'B';
char c = 'C';
char d = 'D';
于 2012-08-27T11:17:15.980 回答
2

这就是为什么您将声明放入标头中的原因,因此即使是 C 编译器也能发现问题。

1)

结果几乎是随机的。在您的情况下,“char”参数可能作为 int 传递(例如在寄存器中,甚至在堆栈上以保持对齐,或其他)。或者你很幸运因为字节顺序,它首先保持最低字节。

2)

由于字节顺序和一些添加的“0”字节来填充该段,这可能是一个幸运的结果。再次,不要依赖它。

于 2012-08-27T11:21:08.363 回答
1

正如你所知道的,我无意中在 C11 标准中找到了涵盖这两个问题的段落——它是 6.2.7.2:

所有引用相同对象或函数的声明都应具有兼容的类型;否则,行为未定义。

于 2012-08-30T06:07:36.510 回答
1

C++ 中的重载函数之所以起作用,是因为编译器将每个唯一方法和参数列表组合编码为链接器的唯一名称。这种编码过程称为重整,逆过程称为反重整。

但是C中没有这样的东西。当编译器遇到当前模块中没有定义的符号(变量或函数名)时,它假定它是在其他模块中定义的,生成链接器符号表条目,并将其留给链接器处理。在这里我们没有参数检查。

如果这里没有类型转换。在 main 中,您向 foo 发送一个值。这是汇编代码:

movl    $65, (%esp)
call    foo

并且 foo 通过将其从堆栈中取出来读取它。由于它的输入值定义为char,它将输入值存储在al寄存器中(一个字节):

movb    %al, -4(%ebp)

因此,对于大于 256 的给定输入,您将a在 foo 中看到变量,循环超过 256。

关于你的第二个问题,在 C 中初始化变量和函数的符号被定义为强符号,不允许使用多个强符号,但我不确定 C++ 是否属于这种情况。

于 2012-08-27T12:09:35.580 回答