2

以前は別のファイルで定義された関数を宣言してから使用するべきだと思っていましたが、最近はプログラミングの経験から考え方を変えました。3つのファイルの場合、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で関数を使用する場合myprintextern別のファイルで定義されているため、事前にを使用して宣言する必要myprint がありますが、結果はまったく逆になります。main.cがに示しているように。行を追加するextern myprintと、エラーが発生します。ただし、その宣言がなければ、正しい結果が得られます。さらに、main.cmyprintで関数を定義していませんが、なぜその関数を使用できるのですか?事前に宣言してはいけませんか?

4

2 に答える 2

7

プロトタイプなしで関数を呼び出すと、コンパイラーはいくつかの仮定を行い、その関数のパラメーターについて推測します。したがって、それを宣言する必要がありますが、関数として宣言します。

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

main.cmyprintで定義されていない関数ですが、エラーなしで関数を使用できます。これは、コンパイラがオブジェクトファイルの作成中に、作成されたオブジェクトファイルのシンボルに対してNULL値を入力するためです。myprint

このNULL値は、バイナリのすべての場所で、リンクフェーズ中にのみ関数の実際のアドレスに置き換えられます。リンカは、すべてのオブジェクトファイルのシンボルテーブルを参照し、シンボル(参照されている場合)を実際のアドレスで解決します。

確かに、-Werror -Wallへのオプションで警告/エラーが表示されますgccobjdumpただし、次のように使用すると、より多くの洞察を得ることができます。

objdump -D main.o | less

それがあなたの疑問を解消するのに役立つことを願っています。

于 2013-02-15T09:05:09.163 に答える