2012-02-01 19 views
6

私は何か遭遇しました。私は完全に理解しません。 関数のプロトタイプがあります:GCCの関数ポインタ、アドレスの割り当て

typedef void (* TMain) (void); 

と関数の変数:

TMain myFunc = MyFunc; 
... 
myFunc(); 

もちろんこれは、正常に動作します。それはなぜではありませんか?

MAPファイルから「MyFunc」は場所0x20100にあることがわかりました。 そして今、面白いことです。割り当て後、 "myFunc = MyFunc;"変数 "myFunc" は0x20100という値を含むではなく、むしろ0x20101です!

私の問題は、テーブルからアドレスを知っている関数を呼び出す必要があることです。だから私は、私はそれが動作する

myFunc = (TMain) ((Int8 *) myTable [ 5 ] + 1); 
myFunc(); 

をすれば、私はしかしその

myFunc = (TMain) myTable [ 5 ]; // that would be 0x20100 
myFunc();       // which produces a proper crash 

のようにそれを行うことができると思いました。

ここではどうなりますか?私はいつも1のオフセットを加えなければならないのでしょうか、これは多かれ少なかれ偶然ですか? タスクを達成するためのより良い(そして働く)方法がありますか?

ありがとうございました。 Walter

+2

わかりませんが、これはアーキテクチャに依存する可能性があります。だから、どのアーキテクチャがターゲットアーキテクチャであるかを言いたいかもしれません。 – phimuemue

+1

'TMain []'型の 'myTable'配列はありますか?あなたがそれをキャストしているからではないと推測します。 –

+1

いくつかの質問があります:どのプラットフォームを使用していますか(ARM、x86 ...)?あなたはデバッガで関数の実際のアドレスをチェックしたことがありますか?コンパイラが間接呼び出し "myFunc()"をどのように生成するかを見ることができますか? –

答えて

3

いくつかのCPUアーキテクチャは、特定の目的のために関数の最初のバイトを予約します。 VAXenには、保存レジスタのマスクがあります。 CDCサイバーは返還アドレスをそこに置きます。いくつかは、アドレス間接を示すために "アドレス"のいくつかのビットを使用しています(私はどのビットを記憶できませんが、1970年代からです)。

あなたがしていることを本当に知っていない限り、その種のポインタ演算を行うコードを書くべきではありません。変数に関数名を割り当て、それを使って完了します:これは、すべてのC実装で動作することが保証されています。

3

あなたはARMターゲットを使用しており、プログラムをThumbモードで構築していると思いますか? (ThumbはARM UbuntuまたはLinaroのデフォルトです)

関数のアドレスの最下位ビットは、命令セット内で関数を解釈する必要があることをCPUに伝えます。 0はARMモードです。 1はThumbモードです。したがって、すべてのThumbモードの関数ポインタは奇数になります。

他のアーキテクチャでも、このイディオムを何らかの形で使用します。通常は、アドレスの下位2ビットをゼロにして(4バイト境界に合わせる)、それが関数の実際の位置であると仮定して安全です。

関連する問題