[英]Argument is passed to function
请考虑以下来源:main.c
int main(){
print_hello("str");
return 0;
}
和你好
#include<stdio.h>
void print_hello(){
printf("Hello world\n");
}
编译和链接gcc -o hello hello.c main.c
它工作正常,但我预计会发生错误。 因为main.c
print_hello
和hello.c
中的print_hello
签名是不同的。 为什么它工作正常?
你没有告诉编译器print_hello()
函数在main.c
文件中是什么样的。 如果你有一个头文件hello.h
例如:
#ifndef HELLO_H_INCLUDED
#define HELLO_H_INCLUDED
extern void print_hello(void);
#endif
你在每个文件的顶部都有#include "hello.h"
,然后你会在main.c
滥用print_hello()
来获得相关的编译器警告。
您的编译器使用调用约定 ,它允许额外的参数传递给被调用的函 这在C的调用约定之间是典型的,因为它支持变量参数列表(如printf,scanf等)。在这些约定中,
在X86调用约定中,这通常被命名为“cdecl”(可选地,带有1或2个下划线)。
这样的约定可以容忍额外的参数,但是,绝对不能用于被调用函数使用的未指定的参数; 后一种情况导致参数中的垃圾。
通常不应该利用这样的运行时功能,但有一些极端情况它是有用的。
为了防止函数使用与其定义不匹配,您应该对两者使用相同的声明。 通常,它会在声明或调用此函数的所有源中包含相同的头文件。
除了其他解释之外,假设在Linux上使用GCC 4.8或更高版本:
您应该使用gcc -Wall -std=c99
进行编译以获取所有警告( -Wall
)并告诉编译器您要遵守的标准( -std=c99
)。 使用这些设置,您会收到警告:
main.c: In function 'main':
main.c:2:5: warning: implicit declaration of function 'print_hello'
[-Wimplicit-function-declaration]
print_hello("str");
^
至于为什么没有给出错误,它们只能在链接器的链接时间给出( ld
,由gcc
调用)。 Unix和Linux链接器(来自GNU binutils )只能使用名称:每个函数(更常见的是每个链接符号)都有一个名称(并且目标文件和可执行文件都在ELF中 ,它定义了名称的表示方式等等... 。)如果两个名称相同,则链接器通过使它们引用相同的地址来解析(参见Levine的链接器和加载器 )。 因此,由于main.o
和hello.o
中的两个名称(或符号)都是print_hello
因此它们被链接在一起。 使用nm
查询ELF对象或可执行文件中的名称。 当然,在运行时发生的事情是未定义的行为 ,但x86 (和x86-64 )的ABI约定启用了您期望的行为。 重要的是ELF不会在目标文件中保留输入或签名元数据。
使用C ++,它有点不同:编译器通过名称修改来转换函数名称(因此ELF *.o
文件中的名称不仅仅是C ++源名称),因此链接器会看到不同的名称(并且会失败)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.