2

静的に定義された構造体の配列をリンクしようとしています。そのために extern 修飾子を使用しています。extern 構造体のメモリ アドレスを出力すると、実行可能ファイルにあるように見える場所とは異なります。

ここに私が持っているものがあります:

タイプ.h:

typedef struct tableEntry {
     const char *my_str;
     void *my_addr;
     int myint;
} my_struct;

test.c:

include "type.h"

my_struct my_array[] = {
    {"hello", (void*)15, 5000},
    {"world", (void*)15, 3000},
    {"abtest", (void*)15, 2000},
};

main.c:

#include <stdio.h>
#include "type.h"

extern my_struct* my_array;

int main() {
    printf("array location - %p\n", my_array);
    printf("array entry 1 myint - %d\n", my_array[0].myint);
    printf("array entry 1 address - %p\n", my_array[0].my_addr);
    printf("array string location - %p\n", &(my_array[0].my_str));
    printf("array string - %s\n", my_array[0].my_str);
}

次のようにプログラムをコンパイルします。

gcc test.c main.c

実行可能ファイルを実行すると、次の出力が得られます。

array location - 0x4006be
array entry 1 myint - 29811
array entry 1 address - 0x6574626100646c72
array string location - 0x4006be
Segmentation fault (core dumped)

nm 出力での my_array のアドレス:

0000000000601060 D my_array

ご覧のとおり、私の出力は期待したものではなく、my_array が適切にリンクされていません (nm 出力の場所は、実際のプログラムによって出力される場所とは異なります)。

注: test.c ファイルを main.c に含めることはできません。リンクする必要があります。

4

2 に答える 2

4

変化する

extern my_struct* my_array;

extern my_struct my_array[];

externを使用して配列をポインターに変更することはできません。

于 2015-06-05T18:21:40.627 に答える
3

my_array のアドレスの問題は、ポインタに含まれるを出力することです。ただし、配列の場合は、ポインター自体のアドレスが必要です (配列の最初の要素のアドレスになります)。ただし、ポインターとしては、それはまだ間違って宣言されています (なぜコンパイラーが文句を言わないのか不思議です)。

短い:

  • 構造体 x *p; // p は x に格納されている値です: "where it points to"
  • 構造体 xq[]; // q は最初のエントリのアドレス (== 配列のアドレス) です。

p に対して同じ結果を得るには、実際には を取得する必要がありますが&p、それは構造体ではなく、ポインターからポインターへの型になります。これは、配列とポインターの違いの 1 つです。詳細については、規格をお読みください。

したがって、 の実装ファイルで使用されているのと同じ宣言 u を使用しますmain.c。実際には、宣言をヘッダーにパックする必要がありますtype.h(これは標準のヘッダーと混同しやすいことに注意してくださいtype**s**.h)。これは、'type.c' をコンパイルするときに、宣言と定義の不一致に関する警告も生成します。これは、実際にヘッダーの最も重要な用途の 1 つです。

実際のアドレスと論理アドレスレポートの不一致nmについて: ファイルは実行前に RAM にロードされ、ロード アドレスに従って再配置する必要があります。ファイル内のアドレスは、実際には(初期化された変数) または(初期化されていない、デフォルトで 0 の変数) セクションのベース アドレスに対して相対的であり、ファイル内の各セクションでは通常 0 です。ロードされると、RAM 内の開始アドレスがこれらの相対アドレスに追加され、実際のアドレスが取得されます。これが、プログラムがロードされた後に時間がかかる理由の 1 つです (再配置は非常に複雑になる可能性があります)。.data.bss

これには 1 つの例外があります。ベアメタル (完全に成長した OS ではない) 組み込みコントローラーのように、リンカーにプログラムを絶対アドレスに再配置させる場合、nm実行時またはデバッガーと同じアドレスを報告する必要があります。その理由は、リンカが実際に上記のランタイム ローダーのジョブを含んでいるためです。その結果、コードを正しいアドレス範囲 (フラッシュ メモリなど) にロードする必要があります。

于 2015-06-05T18:30:25.130 に答える