我曾经认为我们应该在使用之前声明一个在另一个文件中定义的函数,但最近由于编程经验我改变了我的思维方式。对于三个文件C
和ASM
:
的 main.c中 的
extern test_str;
/*extern myprint*/ --> If I add the line, gcc will report an error: called object ‘myprint’ is not a function
void Print_String() {
myprint("a simple test", test_str);
}
的 kernel.asm 的
extern Print_String
[section .text]
global _start
global test_str
test_str dd 14
_start:
call Print_String
jmp $
的 another.asm 的
[section .text]
global myprint
myprint:
mov edx, [esp + 8]
mov ecx, [esp + 4]
mov ebx, 1
mov eax, 4
int 0x80
ret
编译
nasm -f elf another.asm -o another.o
gcc -c -g main.c -o main.o
nasm -f elf kernel.asm -o kernel.o
ld -o final main.o kernel.o another.o
结果
./final
a simple test
在我看来,如果我想在 main.c 中使用函数myprint
,我应事先使用extern
声明它,因为myprint
是在另一个文件中定义的,但结果正好相反。正如上面显示的 main.c 一样。如果我添加行extern myprint
,我将收到错误消息。但是,如果没有这个声明,我会得到正确的结果。更重要的是,我没有在 main.c 中定义函数myprint
,为什么我可以使用该函数?我不应该事先申报吗?
答案 0 :(得分:7)
当你在没有原型的情况下调用函数时,编译器会对该函数的参数做出一些假设和猜测。所以你应该声明它,但声明它是一个函数:
void myprint(const char *, const char *); /* Or whatever. */
答案 1 :(得分:1)
好吧,您可以使用函数myprint
,虽然它在 main.c 中没有定义函数,但没有错误。这是因为编译器在创建目标文件时会对创建的目标文件中的符号myprint
填充NULL值。
只有在链接阶段,才会在二进制文件中的所有位置替换此NULL值,并使用函数的实际地址。链接器引用所有目标文件中的符号表,并使用实际地址解析符号(无论何处引用)。
当然,您应该会看到-Werror -Wall
gcc
选项的警告/错误。尽管如此,您可以使用objdump
获得更多信息,如下所示:
objdump -D main.o | less
希望这有助于消除您的怀疑。