13

複数のコンパイル単位でメモリを複製せずに、複数の C ファイルからアクセスでき、その内容をコンパイラでインライン化できる定数配列を宣言したいと考えています。私のアプリケーションではパフォーマンスが重要です。

資料 1:

header.h:
static const int arr[2] = { 1, 2 };

file1.c:
#include "header.h"
void file1() { printf("%d\n", arr[0]); }

file2.c:
#include "header.h"
int file2() { for (int i = 0; i < 2; i++) printf("%d\n", arr[i]); }

その場合、コンパイラは in file1 に置き換えることができarr[0]ます1。ただし、arrが宣言されているためstatic const、そのメモリは両方の C ファイルで複製されます。私の知る限り、C標準では、配列アドレスが両方のファイルで異なる必要があります。アドレスを印刷して、Linuxでこれを確認しました。-fmerge-all-constantsgcc を使用してもリンカーの統合は発生しません。

資料 2:

header.h:
extern const int arr[2];

file1.c:
#include "header.h"
void file1() { printf("%d\n", arr[0]); }

file2.c:
#include "header.h"
const int arr[2] = { 1, 2 };
int file2() { for (int i = 0; i < 2; i++) printf("%d\n", arr[i]); }

その場合、メモリの重複は発生しarr[0]ませんが、インライン化されません。

C 標準で定義されている可視範囲には欠陥があると思います。そのため、C 標準に違反する Linux/gcc での実用的なソリューションは、私には受け入れられます。

4

4 に答える 4

3

あなたの分析は少し間違っていると思います。のアドレスを出力arrすると、コンパイラは 2 つのコピーを保持するように強制されます。これを行わないと、GCC は両方のコピーを削除します。

リンカが除去したものと除去しなかったものを判断するより良い方法は、出力ファイル内の実際のオブジェクトを調べることです。Linux では、nmプログラムがこれを教えてくれます。

あなたのコード (exhibit 1) を「gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1」でコンパイルすると:

gcc -std=c99 -g3 -O6 -fmerge-all-constants file1.c file2.c main.c

次にnm -a a.out | grep '\<arr\>'、シンボル テーブルでそれを探します。

$ nm -a a.out|grep '\<arr\>'|wc -l
0

実際、 で検索しようとしてもgdb、何も見つかりません。

(gdb) b file1
Breakpoint 1 at 0x400540: file /usr/include/x86_64-linux-gnu/bits/stdio2.h, line 105.
(gdb) r
Starting program: a.out 
Breakpoint 1, file1 () at file1.c:5
5   void file1() { printf("%d\n", arr[0]); }
(gdb) print arr
$1 = <optimized out>

コンパイラはそれを完全に最適化しました。

andprintf("%p\n",arr);の先頭に追加して同じ方法でコンパイルすると、 への2 つの参照が返されます。file1()file2()nm -a a.out|grep '\<arr\>'arr

$ nm -a a.out|grep '\<arr\>'|wc -l
2
$ nm -a a.out|grep '\<arr\>'
00000000004006c8 r arr
00000000004006d0 r arr
于 2012-07-09T22:40:37.477 に答える
2

あなたが試みるかもしれない1つのこと:

const int arr[2] __attribute__((weak)) = { 1, 2 };

現在、配列はすべての* .oオブジェクトに存在しますが、これらのオブジェクトがプログラムでリンクされている場合、GNUldはそれらを1つの共通のデータチャンクに縮小します。

まだそのようなものを持っていない場合は、いくつかの一般的なヘッダーファイルが必要になる場合があります。

#ifndef __GNUC__
#define __attribute__(x)
#endif
于 2012-07-09T22:17:32.023 に答える
0

selectany 変数属性を使用して、配列に外部リンケージを与えます(つまり、宣言しないでくださいstatic)。これにより、配列値がヘッダーに保持され、適切にインライン化できるようになります。また、selectany属性により、リンカーは、定義の1つを任意に選択して実際の定義にし、他の定義を破棄するように指示されます(すべて同じであるため、それは問題ではありません)。

例えば:

const int arr[] __attribute__((selectany)) = {1, 2};

編集:これは明らかにWindowsターゲットでのみ機能します。代わりに、CygwinのGCCで行ったクイックテストでは、結果のデータセグメントに配列の複数のコピーが生成されるという点で、このweak属性は機能しませんでした。

于 2012-07-09T22:18:33.053 に答える