[英]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.