[英]Why can I call my function with the wrong signature when using extern?
我有一些示例代碼。 當我取消注釋invalid_call函數調用時,出現預期的編譯器錯誤。 不幸的是,使用錯誤數量的參數調用my_function仍然會編譯,導致未定義行為(UB)。
main.c
#include <linux/module.h>
#include <linux/kernel.h>
#include "header.h"
extern void my_function(int x);
static int my_init(void)
{
my_function(5);
// invalid_call( 5 ); // doesn't compile
return 0;
}
static void my_exit(void)
{
pr_info("removing module");
}
module_init(my_init);
module_exit(my_exit);
func_source.c
#include <linux/kernel.h>
void my_function(int x, int y)
{
pr_info("%d %d",x,y);
}
頭文件
void invalid_call(int x, int y)
{
return;
}
預期輸出:僅使用一個參數調用my_function()時發生編譯器錯誤。
實際輸出:代碼編譯並打印y的隨機值,本質上是UB。
我知道extern void my_function(int x);
只是另一個聲明,所以我認為編譯器不需要拋出錯誤,但是當我使用單個參數調用my_function時,它應該找不到與任何函數定義匹配的對象。 不幸的是,我遇到了UB,而不是編譯器錯誤。
這是如何運作的? 我知道UB是UB,但為什么會變成UB。 我以為功能簽名不匹配會導致編譯器。 我懷疑是外部聲明,但仍然...
另外,獎金問題。 如何避免再次遇到此問題? 我可以遵循的任何設計模式或實踐嗎?
如果您想自己測試一下,這是Makefile。
obj-m += main.o
example-y := ./src/main.o ./src/func_src.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
資料夾結構:
./Makefile
./src/main.c
./src/my_func.c
提前致謝。
至少在C語言中,編譯器和鏈接器都沒有程序(或內核模塊等)的所有源文件中所有功能的全局知識。 編譯器一次編譯一個源文件,而要做的就是聲明(包括原型),該聲明在一個編譯期間可見。 因此,如果原型是錯誤的,那就注定了。 編譯器針對(錯誤)原型驗證您的(錯誤)調用,沒有發現不匹配,因此生成傳遞一個參數的(錯誤)調用,從而導致您看到的行為。
因此,以下是一個極好的主意:
.c
文件中,而應該放在.h
文件中,無論您在哪里調用函數, .h
它們包括在其中;以及 .h
文件,您可以在其中定義函數。 這樣,編譯器不僅會檢查調用是否與原型匹配,還可以檢查原型是否與實際定義匹配,並且由於原型只有一個副本(在那個.h
文件中),因此或多或少使其無法同步並最終導致錯誤。 另請參閱此問題 。
在您的問題中,您似乎認為,錯誤的調用甚至不可能與接受兩個參數的定義聯系起來。 這在C ++中可能是正確的,在C ++中,“名稱修飾”將函數的參數安排為其簽名的一部分。 但是在C中卻沒有發生這種情況。函數的唯一身份(在符號表中,就鏈接器而言)是名稱my_function
,並且在鏈接時沒有任何東西可以阻止一個單參數傳遞的調用被匹配與接受兩個參數的定義有關。
一些方法可以檢測到此問題:
-Wmissing-prototypes
可能會警告func_source.c
您具有沒有原型的外部可見函數。 您應該在要調用該函數的所有單元以及包含函數定義的單元所包含的頭文件中擁有正確的原型。 后面的警告標志將檢測您是否忘記將原型放入包含定義的單元中(由於原型與定義不匹配,這將導致編譯錯誤)。
OP發布的代碼會導致來自編譯器的以下消息:
gcc -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled.c" (in directory: /home/richard/Documents/forum)
untitled.c: In function ‘invalid_call’:
untitled.c:1:23: warning: unused parameter ‘x’ [-Wunused-parameter]
void invalid_call(int x, int y)
^
untitled.c:1:30: warning: unused parameter ‘y’ [-Wunused-parameter]
void invalid_call(int x, int y)
^
untitled.c: In function ‘my_init’:
untitled.c:15:5: warning: implicit declaration of function ‘my_function’; did you mean ‘myFunction’? [-Wimplicit-function-declaration]
my_function(5);
^~~~~~~~~~~
myFunction
untitled.c: In function ‘my_exit’:
untitled.c:22:5: warning: implicit declaration of function ‘pr_info’ [-Wimplicit-function-declaration]
pr_info("removing module");
^~~~~~~
untitled.c: At top level:
untitled.c:25:1: warning: data definition has no type or storage class
module_init(my_init);
^~~~~~~~~~~
untitled.c:25:1: warning: type defaults to ‘int’ in declaration of ‘module_init’ [-Wimplicit-int]
untitled.c:25:1: warning: parameter names (without types) in function declaration
untitled.c:26:1: warning: data definition has no type or storage class
module_exit(my_exit);
^~~~~~~~~~~
untitled.c:26:1: warning: type defaults to ‘int’ in declaration of ‘module_exit’ [-Wimplicit-int]
untitled.c:26:1: warning: parameter names (without types) in function declaration
untitled.c:28:6: warning: conflicting types for ‘my_function’
void my_function(int x, int y)
^~~~~~~~~~~
untitled.c:15:5: note: previous implicit declaration of ‘my_function’ was here
my_function(5);
^~~~~~~~~~~
untitled.c:20:13: warning: ‘my_exit’ defined but not used [-Wunused-function]
static void my_exit(void)
^~~~~~~
untitled.c:13:12: warning: ‘my_init’ defined but not used [-Wunused-function]
static int my_init(void)
^~~~~~~
Compilation finished successfully.
建議在編譯時啟用警告,然后修復這些警告
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.