c - 为什么在使用 extern 时可以使用错误的签名调用我的函数?

标签 c

我有一些示例代码。当我取消对 invalid_call 函数调用的注释时,我收到了预期的编译器错误。不幸的是,使用错误数量的参数调用 my_function 仍然会编译导致未定义的行为 (UB)。

主.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);
}

标题.h

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 语言中,编译器和链接器都不了解程序所有源文件(或内核模块等)中所有函数的全局知识。编译器一次编译一个源文件,它所要做的就是声明——包括原型(prototype)——在一次编译期间是可见的。所以如果原型(prototype)是错误的——你就完蛋了。编译器根据(不正确的)原型(prototype)验证您的(不正确的)调用,并没有发现不匹配,因此生成一个传递一个参数的(不正确的)调用,导致您看到的行为。

出于这个原因,这是一个绝妙的主意:

  • 永远不要将外部原型(prototype)放在 .c 文件中,而是放在 .h 文件中,无论您在哪里调用函数,都应该包含这些文件,并且
  • 还在您定义函数的源文件中包含 .h 文件。

这样一来,编译器不仅会检查调用是否与原型(prototype)匹配,它还会检查原型(prototype)是否与实际定义匹配,而且由于原型(prototype)只有一个副本(在那个 .h 文件),它或多或少不可能不同步并最终不正确。另见 this question .

在您的问题中,您似乎认为错误的调用甚至不可能与双参数接受定义相关联。这在 C++ 中可能是正确的,其中“名称修改”将函数的参数安排为其签名的一部分。但是在 C 中不会发生类似的事情。您的函数的唯一标识——在符号表中,就链接器而言——是名称 my_function,并且在链接时没有任何东西可以阻止一个 - arg-passing 调用与双参数接受定义相匹配。

关于c - 为什么在使用 extern 时可以使用错误的签名调用我的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57618000/

相关文章:

c - 电话簿链接列表

c - 哪些绿色线程库可用于 C,可以匹配 Haskell 绿色线程的性能和易用性?

c - 使用指针对字符串进行排序

命令行参数不返回正确的总数

c - 有没有办法用命令行结束Windows任务(而不是进程)?

c++ - 使用纯 C/C++ 添加注释到 PDF

c++ - 头文件编译成目标文件?

c - 障碍,获取和获取线

c - while() 循环意外地不断迭代

C 凯撒密码函数调用未按预期运行