Edited at

static const char*は書き込み可能な変数です

More than 3 years have passed since last update.


よくある間違い

static const char* s_foo = "abc";

Java流なのか、このような文字列定数をよく見かけます。これは「ファイルスコープの変数用意して、ついでに初期化した」ものなので、以下のように後で書き換え可能です。

s_foo = "oops";

これは期待した動作ではないでしょう。読み取り専用にするにはこうします。

static const char* const s_foo = "abc";

配列も同じです。

static const char* s_bad[] = {

"foo",
"bar",
};

static const char* const s_good[] = {
"foo",
"bar",
};

変数なのがキモイなという人向けに、直に文字列を表すこともできます。(終端文字で+1されるとはいえ)sizeofで長さもとれますしね。

const char g_name[] = "hoge";

int func()
{
return sizeof(g_name);
}

externも普通にできます。

extern const char g_name[];

宣言のしかたで、値が確保される場所が変わります。A、B、Cはポインタのサイズが確保され、どこかにある文字列へのアドレスが入ります。D、E、Fは値自体の大きさです。


a.c

static const char*       s_A;        // -> BSS section

static const char* s_B = "b";// -> Data section
static const char* const s_C = "c";// -> Data section
static const char s_D[] = "d";// -> Read only Data section
static char s_E[] = "e";// -> Data section
static char s_F[128]; // -> BSS section

>nm a.so -S

00001f70 00000090 d _DYNAMIC
00002008 A __bss_start
00002008 A _edata
0000208c A _end
00002008 00000004 b s_A
00002004 00000004 d s_B
00001f6c 00000004 d s_C
0000018c 00000002 r s_D
00002000 00000002 d s_E
0000200c 00000080 b s_F

「こんな些細なことはどうでもいいよ!」と思うかもしれませんが、グローバルな変数が使えない環境もあったのですよ。brewとかbrewとか。あとbrewとか。


ヘッダファイルで感染拡大

#includeはただ単に文字列をそこへ挿入するだけなので、ヘッダファイルにstatic const char*の様な物があると、そのヘッダがインクルードされるたびに変数が増えていきます。


faq.h

static const char* sFAQ = "123";

static const unsigned long long sHogeMax = 0xffffffffffffffff;


faq1.c

#include "faq.h"



faq2.c

#include "faq.h"


ここで、faq1.cとfaq2.cをビルドした結果を見てみると・・・

>nm faq.so -S

00001f70 00000090 d _DYNAMIC
00002008 A __bss_start
00002008 A _edata
00002008 A _end
00002004 00000004 d sFAQ
00002000 00000004 d sFAQ
000001a0 00000008 r sHogeMax
00000190 00000008 r sHogeMax

シンボルsFAQ、sHogeMaxが2つずつありますね。まじめに定数をまとめたつもりが変数の大量生産になる可能性があります。


ベストな解決方法

#define FOO "foo"

#define BAR 128
enum {
Hoge = 256,
};

終わり。sizeofもできるよ。


補足: C++の名無しのnamespace

C++ではファイルスコープの指定はstaticの代わりに名無しのnamespaceを使うことが推奨されているようです。staticという名前は紛らわしいですからね。

namespace

{
void foo();
}

また、この機能はクラス定義もファイルスコープに入るので、ファイル内でこっそり使っているクラスが衝突することも避けられます。

例えば以下のように、別のファイルで、同じクラス名、同じメソッドがある場合、衝突したあげく、コンパイラはそのことを教えてくれません。


foo1.cpp

class Foo {

public:
int number()
{
return 1;
}
};

int foo1_number()
{
Foo o;
return o.number();
}



foo2.cpp

class Foo {

public:
int number()
{
return 2;
}
};

int foo2_number()
{
Foo o;
return o.number();
}



main.cpp

#include <stdio.h>


int foo1_number();
int foo2_number();

int main(int argc, char** argv)
{
printf("foo1 = %d\n", foo1_number());
printf("foo2 = %d\n", foo2_number());

return 0;
}


これの出力結果は・・・

foo1 = 1

foo2 = 1

と、両方とも同じになりました。(コンパイラやビルド順によって違う結果になったりエラーになったり、どうなるかはあいまいです。)

クラスの定義をnamespaceで囲めば解決します。

namespace {

class Foo {
public:
int number()
{
return 1;
}
};
}