末端を超え…メモリのどこかに転がっていた\0までを文字列と認識
エラーで弾かれず読み進めるということに驚き
こうしてCの文字列の定義を再認識するんですね。「C言語 一皮むけばアセンブラ(るばーと心の俳句)」でして、一般的な言語に比べてより生のメモリを相手にする感がありますから、この機会にメモリをダンプして直接見てみると面白いです。
C
1#include <stdio.h>
2#include <string.h>
3#include <ctype.h> // for isprint()
4void mdump(void *vp, int size, const char *msg);
5int main(void)
6{
7 char str1[100] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
8 char str2[1000] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
9 printf("str1は%lu\n", strlen(str1));
10 printf("str2は%lu\n", strlen(str2));
11
12 printf("str1は%p番地から\n", str1); // str1[]のアドレス
13 printf("str2は%p番地から\n", str2); // str2[]のアドレス
14 mdump(str1, 220, "str1[]"); // str1[] からメモリダンプ
15 return 0;
16}
17
18/*======================================================================
19Func Name : void mdump(void *vp, int size, const char *msg);
20Function : Dump memory
21Param Input : void *vp = memory address
22 int size = size to be dumped, 16 bytes align
23 char *msg = message to identify memory block
24Param Output, Return, Input Inf, Output Inf : None
25Note : # How to compile with your 'prog.c',
26 # > cc prog.c mdump.c
27Revision : 1.00 2016/05/14 Created by rubato6809
28======================================================================*/
29#define LINESIZE 16 // 16バイトを一行に表示
30void mdump(void *vp, int size, const char *msg)
31{
32 unsigned char *mem = vp; // 常に無符号char領域としてアクセス
33 int ltop, lnext; // line-top and line-next
34
35 // 識別メッセージを表示
36 if (msg) printf(" '%s'", msg); // つまり、NULLなら表示しない
37 printf("\n"); // でも最初に改行だけはする
38
39 for (ltop = 0; ltop < size; ltop = lnext) { // LINESIZE 毎に繰返す
40 lnext = ltop + LINESIZE; // 次の行の先頭位置
41 printf("%p: ", &mem[ltop]); // 今の行の先頭アドレス
42
43 for (int i = ltop; i < lnext; i++) // メモリの値を16進数で表示
44 printf("%02x ", mem[i]);
45
46 for (int i = ltop; i < lnext; i++) { // 文字表示、ascii dump
47 unsigned char c = mem[i]; // メモリの値
48 printf("%c", isprint(c) ? c : '.'); // 表示不能なら '.' を
49 }
50 printf("\n");
51 }
52}
53
私の実行結果はこうです。
bash
1$ ./a.out
2str1は102
3str2は100
4str1は0x7fff0f8b84b0番地から
5str2は0x7fff0f8b8520番地から
6 'str1[]'
70x7fff0f8b84b0: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
80x7fff0f8b84c0: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
90x7fff0f8b84d0: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
100x7fff0f8b84e0: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
110x7fff0f8b84f0: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
120x7fff0f8b8500: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
130x7fff0f8b8510: 61 61 61 61 12 7f 00 00 4b 06 e2 1d 12 7f 00 00 aaaa....K.......
140x7fff0f8b8520: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
150x7fff0f8b8530: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
160x7fff0f8b8540: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
170x7fff0f8b8550: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
180x7fff0f8b8560: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
190x7fff0f8b8570: 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 aaaaaaaaaaaaaaaa
200x7fff0f8b8580: 61 61 61 61 00 00 00 00 00 00 00 00 00 00 00 00 aaaa............
私の手元の64bit GCCでも「str1は102」と表示するので、質問者も似たような状況だろうと独断して解説を試みます。
0x7fff0f8b8510: 61 61 61 61 12 7f 00 00 4b 06 e2 1d 12 7f 00 00 aaaa....K.......
の表示から
- 「12 7f 00」の部分が +2 バイトだとわかる
- 「12 7f 00 00 4b 06 e2 1d 12 7f 00 00」の12バイトは str1[] と str2[] の間に挟まれた隙間部分で、本来は使う予定の無いメモリ
- 隙間部分を設けた理由は、str2[] を16バイト境界から配置してキャッシュを有効利用したいからだろう
- 「4b 06 e2 1d 12 7f 00 00」は、十中八九 0x7f121de2064b という値のポインタ変数だった
- 同様に、+2 バイト分の「12 7f 00 00」もここにポインタ変数があった痕跡だろうetc.
さらに、たとえば
C
1 char str2[1000]; // ="aaaaa...a";
とすれば、str2[1000] は初期化されない 1000 バイトのメモリ領域となり、そこに在る様々な値、いわゆるゴミですが、見ようによっては美しい模様として眺めることができます。このように実際のメモリの姿を見ておくとC言語の仕組みの一端を具体的に実感としてつかむことができ、デバッグにも役立ちます。
Enjoy !