web-dev-qa-db-ja.com

Linux GCCで理由もなく「初期化要素が一定ではありません」エラー、Cをコンパイル

Main.cファイルを取得し、Mac OSXでgcc-std = c1x -c main.cを使用してコンパイルすると、エラーなしで正常に動作します。次に、LinuxMintとRaspberry Piでまったく同じことを行いますが、どちらの場合も、「初期化要素が一定ではありません」というエラーが表示されます。

関連するコードを含む問題のある行の一例:

//STATIC GLOBAL CONSTANTS
const unsigned long long LATITUDE = (long) 3600000;
const unsigned long long LONGITUDE = (long) 1810000;
const unsigned long long MAX_COORDINATES_NUMBER = (LATITUDE-1) + LATITUDE*(LONGITUDE-1); //compiler error: initializer element is not constant

算数をさせてくれるはずですよね?それを実際の数値に置き換えるだけでうまくいきますが、そうすると面倒になります。そしてそれはとにかく私のMacでうまく動作します。 Linuxで指定しなければならないGCCのオプションはありますか(Macでは必要のない-std = c1x以外)?

13
sudo

C言語では、静的オブジェクトの初期化子が定数式である必要があります。 (静的オブジェクトの初期化はmainが始まる前に行われるため、実行時の評価を行う場所はありません。)

Cのconstキーワードは「一定」を意味するものではありませんが、単語は明らかに関連しています。 定数式は、コンパイル時に評価できるものであり、場合によっては評価する必要があります。 constは読み取り専用を意味します。たとえば、ブロックスコープ(関数定義内)では、次のようになります。

const int r = Rand();

完全に合法です。明らかに、初期化子はコンパイル時に評価できません。 constは、rが初期化された後に変更できないことを意味します。

あなたが書くとき:

const unsigned long long LATITUDE = (long) 3600000;

LATITUDEへの参照は定数式ではありません。コンパイラは確かにcouldコンパイル時にそのような参照を評価できますが、C標準ではそれを要求していません。 (定数式と非定数式の間の線はどこかに描かれなければならず、言語の作者は、特別な場合をほとんど伴わずに、区別を比較的単純にすることを選択しました。)

LATITUDEが定数式になるように、C言語couldが定義されていることは確かです。それはC++であり、私はCが同様のルールを採用することを主張しました。しかし、現在のCルールではそうではありません。つまり、静的オブジェクトの初期化子でLATITUDEを使用することはできません。

これは、clang(MacOSでgccと入力したときに呼び出されるコンパイラ)は、このエラーの診断に失敗するため、不適合である可能性が非常に高いことも意味します。 。私自身のLinuxシステムでは、-std=c11 -pedanticを指定して呼び出すと、gcc 4.7.2はエラーを正しく診断しますが、clang3.4は診断しません。

2011 ISO C標準(1990および1999標準にも存在する)のセクション6.6パラグラフ10のこの条項のおそらくを除いて:

実装は、他の形式の定数式を受け入れる場合があります。

Clangはこの権限を利用しているため、定数式としてLATITUDEを受け入れると考えられますが、それでも少なくともclang -std=c11 -pedantic -Wall -Wextraからの警告が予想され、何もありません。

[〜#〜] update [〜#〜]:以下をコンパイルすると:

#include <stdio.h>

const unsigned long long LATITUDE = (long) 3600000;

int main(void) {
    switch (0) {
        case LATITUDE:
            puts("wrong");
            break;
        default:
            puts("ok(?)");
            break;
    }
}

オプション-std=c99 -pedanticを使用したclang3.0を使用すると、次のようになります。

c.c:7:14: warning: expression is not integer constant expression (but is allowed as an extension) [-pedantic]
        case LATITUDE:
             ^~~~~~~~
1 warning generated.

Clang 3.4では、警告は次のとおりです。

c.c:7:14: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]
        case LATITUDE:
             ^~~~~~~~
1 warning generated.

したがって、clangはそれが定数式ではないことを認識します。バグは、MAX_COORDINATES_NUMBERの宣言について警告しないことです。

別の更新

問題のコードは次のとおりです。

const unsigned long long LATITUDE = (long) 3600000;
const unsigned long long LONGITUDE = (long) 1810000;
const unsigned long long MAX_COORDINATES_NUMBER = (LATITUDE-1) + LATITUDE*(LONGITUDE-1);

最初の2つの宣言の(long)キャストは役に立ちません。定数3600000および1810000は(おそらく)タイプintです。それらをlongに変換し、その結果を使用してタイプunsigned long longのオブジェクトを初期化します。キャストを削除するだけです。または、より明確にしたい場合は、ULLサフィックスを追加して、定数をunsigned long longにします。

const unsigned long long LATITUDE = 3600000ULL;
const unsigned long long LONGITUDE = 1810000ULL;

問題は、LATITUDELONGITUDEを参照する3番目の宣言にあります。どちらも定数式ではありません。残念ながら、Cはint以外の整数型の名前付き定数を定義する良い方法を提供していません(enum定数にint機能を(乱用)使用できます)。別の方法は、マクロを使用することです。これは機能します:

#define LATITUDE 3600000ULL
#define LONGITUDE 1810000ULL
const unsigned long long MAX_COORDINATES_NUMBER = (LATITUDE-1) + LATITUDE*(LONGITUDE-1);

また、MAX_COORDINATES_NUMBERを定数式にする必要がある場合は、それをマクロにすることもできます。

#define LATITUDE 3600000ULL
#define LONGITUDE 1810000ULL
#define MAX_COORDINATES_NUMBER ((LATITUDE-1) + LATITUDE*(LONGITUDE-1))

(より大きな式でMAX_COORDINATES_NUMBERを使用する場合、演算子の優先順位の問題を回避するために、追加の括弧が必要です。)

24
Keith Thompson