在C和C ++的情况下,我有两个关于同一函数和全局变量的不同声明的问题。
不同的功能声明
考虑以下代码片段:
file_1.c
void foo(int a);
int main(void)
{
foo('A');
}
file_2.c
#include <stdio.h>
void foo(char a)
{
printf("%c", a); //prints 'A' (gcc)
}
正如我们所看到的,原型与定义中的定义不同 file_2.c ,但该函数会打印预期值。
如果是C ++,由于未定义,上述程序无效
在链接时引用foo(int)
。这可能是由于
存在其他功能签名 - 与C相比,其中
函数名称不包含任何指示的字符
函数参数的类型。
但是当涉及到C然后是什么?由于原型与之相同
无论参数的数量如何,name都具有相同的签名
及其类型,链接器不会发出错误。但是哪种类型
转换是在这里进行的?看起来像这样:'A'
- &gt;
int
- &gt;回到char
?或许这种行为是
undefined / implementation-defined?
全局变量的不同声明
我们有两个文件和两个不同的相同声明 全局变量:
file_1.c
#include <stdio.h>
extern int a;
int main(void)
{
printf("%d", a); //prints 65 (g++ and gcc)
}
file_2.c
char a = 'A';
在C和C ++中输出都是65。
虽然我想知道两种标准对这种情况的评价 情况。
在C11标准中,我发现了以下片段:
J.5.11多个外部定义 (附件J.5通用扩展)
标识符可能有多个外部定义 一个对象,有或没有明确使用关键字extern;如果 定义不同意,或者不止一个被初始化, 行为未定义(6.9.2)。
请注意,它指的是两个或更多定义的存在 我的代码只有一个,所以我不确定这篇文章是否是一个很好的参考点 这种情况......
答案 0 :(得分:5)
Q1。根据C99规范,第6.5.2.2.9节,它是C中未定义的行为:
如果使用与表示被调用函数的表达式指向的类型(表达式)类型不兼容的类型定义函数,则行为未定义。
表达式“指向”采用int
的函数,而函数定义为采用char
。
Q2。变量的情况也是未定义的行为,因为您正在阅读int
或从char
分配char a = 'A';
char b = 'B';
char c = 'C';
char d = 'D';
。假设4字节整数,这将访问超过其有效的内存位置的三个字节。您可以通过声明更多变量来测试这一点,例如:
{{1}}
答案 1 :(得分:2)
这就是你将声明放入头文件的原因,所以即使是C编译器也能解决问题。
1)
这个结果几乎是随机的;在您的情况下,“char”参数可能作为int传递(如在寄存器中,或甚至在堆栈上以保持对齐,或其他)。或者你很幸运,因为endianess,它保持最低顺序字节。
2)
由于endianess可能会成为一个幸运的结果,并且有些人会添加'0'字节来填充该段。再说一遍,不要依赖它。
答案 2 :(得分:1)
C ++中的重载函数有效,因为编译器将每个唯一的方法和参数列表组合编码为链接器的唯一名称。这个编码过程叫做mangling,
和反向过程demangling。
但是在C中没有这样的东西。当编译器遇到一个未在当前模块中定义的符号(变量或函数名)时,它假定它在某个其他模块中定义,生成一个链接符号表条目,并留给链接器处理。在这里,我们没有参数检查。
如果此处没有类型转换。在main中,您将值发送到foo。这是汇编代码:
movl $65, (%esp)
call foo
foo将它从堆栈中取出来读取它。由于它的输入值定义为char,它将输入值存储在al
寄存器(一个字节)中:
movb %al, -4(%ebp)
因此,对于大于256的给定输入,您将在foo中看到变量a
,循环超过256。
关于你的第二个问题,在C中,初始化变量和函数的符号被定义为强,并且不允许使用多个强符号,但我不确定是否是C ++的情况。
答案 3 :(得分:1)
你知道吗,我不小心发现了C11标准中涵盖这两个问题的段落 - 它是6.2.7.2:
引用同一对象或函数的所有声明都应具有 兼容型;否则,行为未定义。