てけもぐ Tech 忘備録

C言語のリテラルの話

対象読者

C言語初学者

解決すること

リテラルとは?リテラルの保存場所など

内容

C 言語にはリテラルとうのがあります。他のプログラミング言語でも一般的に同じ呼び方なのかと思いますが、実際の文字列や数値などの値のことです。数値なら 3 や 3.14、1.4142 など数値そのもの、文字なら 'A' とか。一番わかり易いのは、文字列ではないかと思います。"Hello World\n"、これは文字列リテラル。

以下で例を見てみます。

ローカル変数でやるとスタックを使いますから、各文字を分解して代入します。なので大域変数を使ってサンプルを見てみると...

int printf(char *fmt, ...);

char *s = "Hello World\n";

int main(){
  printf("%s", s);
}

これが、

...snip...

	.section	.rodata
.LC0:
	.string	"Hello World\n"
	.section	.data.rel.local,"aw"
	.align 8
	.type	s, @object
	.size	s, 8
s:
	.quad	.LC0

...snip...

になります(-masm=intel)。

読み取り専用データ領域に名前を付けられて保存するようになってます。.LC0 の名前でアドレスを読み込んで使ったりします。上の例では s に8バイト分=アドレス長分を s に割り当てて、.LC0 を s に定義してます。

以下のコードがエラーになるのはこのせい。

int printf(char *fmt, ...); 

char *s = "Hello World\n";

int main(){
  s[6] = 'w';     // error !
  printf("%s", s);
  return 0;
}

上のコードをコンパイルして実行すると、読み取り専用データ領域の変更を試みることになるので、コアダンプして終了。

でも、配列にすると...

int printf(char *fmt, ...); 

char s[] = "Hello World\n";

int main(){
  s[6] = 'w';     // OK!
  printf("%s", s);
  return 0;
}

これが、

...snip...

	.globl	s
	.data
	.align 8
	.type	s, @object
	.size	s, 13
s:
	.string	"Hello World\n"

...snip...

となって変更出来るようになります。配列はメモリを割り当ててくれますが(例の char *s では ”Hello World\n" の13バイト)、ポインタでの宣言はアドレスをポインタに代入してくれるだけ。

ちなみに、リテラルは以下の様に使えます。

int printf(char *fmt, ...);

int main(){
  printf("%s", &"Hello World\n"[6]);  // World が表示される
  return 0;
}

Tags