C 参数被传递给函数
考虑以下来源: main.c 你好C 参数被传递给函数,c,linux,C,Linux,考虑以下来源: main.c 你好 #include<stdio.h> void print_hello(){ printf("Hello world\n"); } #包括 void print_hello(){ printf(“Hello world\n”); } 编译和链接gcc-o hello.cmain.c后,工作正常,但我预计会出现错误。因为main.c中的print\u hello和hello.c中的print\u hello的签名是不同的。为什么工作正常?您
#include<stdio.h>
void print_hello(){
printf("Hello world\n");
}
#包括
void print_hello(){
printf(“Hello world\n”);
}
编译和链接
gcc-o hello.cmain.c
后,工作正常,但我预计会出现错误。因为main.c
中的print\u hello
和hello.c
中的print\u hello
的签名是不同的。为什么工作正常?您没有告诉编译器文件main.c
中的print\u hello()
函数是什么样子的。如果您有一个头文件hello.h
,例如:
#ifndef HELLO_H_INCLUDED
#define HELLO_H_INCLUDED
extern void print_hello(void);
#endif
在每个文件的顶部包含“hello.h”,然后在编译器使用的main.c
中错误使用print\u hello()
会得到相关的编译器警告,允许将额外的参数传递到被调用函数,而不会产生额外的效果。这在C的调用约定之间是典型的,因为它在此类约定中支持变量参数列表(如printf、scanf等)
- 参数的传递顺序是第一个参数最接近堆栈顶部(如果所有参数都放在堆栈上)或在等效寄存器中,其他参数按顺序放置
- 调用方知道使用的堆栈大小,并在函数调用后恢复堆栈指针
为了防止函数使用和定义之间的不匹配,应该对两者使用相同的声明。通常,在声明或调用此函数的所有源中都包含相同的头文件。除了其他解释之外,假设在Linux上更好: 您应该使用
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)只与名称一起工作:每个函数(更一般地说,每个链接符号)都有一个名称(并且对象文件和可执行文件都在其中,它们定义了名称的表示方式,等等……)如果两个名称相同,则链接器通过使它们引用相同的地址来解析(参见Levine)。因此,由于main.o
和hello.o
中的两个名称(或符号)都是print\u hello
,因此它们是链接在一起的。用于查询ELF对象或可执行文件中的名称。当然,在运行时发生的是,但是(和)的约定启用了您期望的行为。重要的是ELF不会在对象文件中保留键入或签名元数据
<> P> C++ C++,有点不同:编译器正在转换函数名(在ELF <代码> *.O/COD>文件中的文件不是简单的C++源名称),所以链接器会看到不同的名称(并且会失败)。请给我一个链接,指向有关此约定的
C
标准条款。由于缺少这些约定,我无法给你一个指向C标准中约定的链接。C标准是为比寄存器机器、基于堆栈的机器等更广泛的条件而设计的,它唯一规定的是,如果向所有使用它的源提供函数声明,则不会出现奇怪的效果。所有公约细节都是特定于平台的,尽管其中大多数原则相似。
main.c: In function 'main':
main.c:2:5: warning: implicit declaration of function 'print_hello'
[-Wimplicit-function-declaration]
print_hello("str");
^