2

我曾经认为我们应该在使用它之前声明一个在另一个文件中定义的函数,但最近我因为编程经验改变了我的思维方式。对于三个文件,C并且ASM

主程序

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

内核.asm

extern Print_String

[section .text]
global _start
global test_str
test_str    dd  14
_start:
    call Print_String
    jmp $

另一个.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.cmyprint中的函数,我应该事先声明它,因为它 是在另一个文件中定义的,但结果恰恰相反。正如上面的main.c所示。如果我添加 line ,我会得到一个错误。但是,如果没有该声明,我将得到正确的结果。更何况我没有在main.c中定义函数,为什么我可以使用那个函数呢?我不应该提前声明吗?externmyprintextern myprintmyprint

4

2 回答 2

7

当您调用没有原型的函数时,编译器会对该函数的参数做出一些假设和猜测。所以你应该声明它,但将它声明为一个函数:

void myprint(const char *, const char *); /* Or whatever. */
于 2013-02-15T07:36:26.337 回答
1

好吧,您可以使用 function myprint,尽管它没有在main.c中定义,但没有错误。这是因为编译器在创建目标文件时会针对创建的目标文件中的符号填充 NULL 值myprint

仅在链接阶段,此 NULL 值在二进制文件中的所有位置都替换为函数的实际地址。链接器引用所有目标文件中的符号表,并使用实际地址解析符号(无论在何处引用)。

-Werror -Wall当然,您会看到带有选项的警告/错误gcc。虽然,您可以使用objdump以下方法获得更多洞察力:

objdump -D main.o | less

希望这有助于消除您的疑问。

于 2013-02-15T09:05:09.163 回答