繁体   English   中英

参数传递给函数

[英]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_hellohello.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.ohello.o中的两个名称(或符号)都是print_hello因此它们被链接在一起。 使用nm查询ELF对象或可执行文件中的名称。 当然,在运行时发生的事情是未定义的行为 ,但x86 (和x86-64 )的ABI约定启用了您期望的行为。 重要的是ELF不会在目标文件中保留输入或签名元数据。

使用C ++,它有点不同:编译器通过名称修改来转换函数名称(因此ELF *.o文件中的名称不仅仅是C ++源名称),因此链接器会看到不同的名称(并且会失败)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM