配列へのアクセス
まずは、配列の要素を出力してみます。これはPythonやC#しか触ったことがない人でも理解できると思います。
ただし、ここで要素数をsizeof()
で求めています。
これは配列の確保したメモリサイズを配列の1つ目の要素のメモリサイズで割ることで要素数が求まる、というものです。
ここでは、10進数と16進数で出力するようにします。往々にしてC言語では16進数で出力するほうが便利なことがあるためです。
printf("-array1\n"); int arr1[] = {11, 22, 33, 44, 0x123456, 0x1234}; printf("0x - 0d \n"); for (int i = 0; i < sizeof(arr1) / sizeof(int); i++) { printf("%08X - %08d \n", arr1[i], arr1[i]); } /* 0x - 0d 0000000B - 00000011 00000016 - 00000022 00000021 - 00000033 0000002C - 00000044 00123456 - 01193046 00001234 - 00004660 */
配列をByte列としてポインタでアクセス
次に、配列をポインタにキャストして4つまとまり毎に出力してみます。
ポインタにすることによって、Byte列として出力を見ることが出来ます。
これは、C言語が確保したメモリ上の並びを見ていることになります。
ここでは16進数で出力していますが、これをよく見ると、下位の桁から出力されていることが分かります。
これはこのC言語処理系では小さい桁(Byte)から小さいアドレスに格納(し読みだし)する処理系であるためです。
このような小さい桁(Byte)から小さいアドレスに格納(し読みだし)する処理系のことをリトルエンディアンといいます。
Windows PCなどがこれにあたり、逆に大きいByteから小さいアドレスに格納(し読みだし)する処理系をビッグエンディアンといいます。
異なる処理系(例えばwebサーバー等)と通信する場合は、この並びがどうなっているかを考慮して通信する必要があります。
Pythonなどはライブラリで自動的に処理されている気がします。
printf("-array2\n"); int arr2[] = {1, 2, 3, 4, 0x123456, 0x7890}; unsigned char *ptr0 = (unsigned char *)arr2; for (int i = 0; i < sizeof(arr2); i++) { printf("%02x ", *(ptr0 + i)); if (i % 4 == 3) printf("\n"); } /* 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 56 34 12 00 90 78 00 00 */
構造体へのアクセス
次に構造体を考えます。
構造体とは、型の違う変数(同じでもよい)をひとまとめに出来るものです。
ここでも、構造体のByte列を見るためにポインタに格納します。
これを16進数、10進数、charで出力します。
このようにして、構造体のByte列を直接確認することが出来ます。
ここで、数値については10進数であるか16進数であるかと、先ほどのエンディアンが逆であることに気を付けて見てみると、構造体の要素が順番に並んでいますが、隙間があることが分かります。一般的には4byte毎に並べられています。
これはこのほうがアクセスするのに好都合(速度が速くなる)だからだそうです。
address[5]
のように4byteの倍数でない場合は、余計に3byte確保したような見え方になります。
かといって8byte確保しているわけではないはずなので、address[5]
に8文字格納するようなことは行わないほうがよいです。
ここでprintf(”%c", 変数)
とすると、変数のasciiとしての文字を表示します。
#include <stdio.h> #include <cstring> //strcpy,memcpyなど printf("\n-shiro\n"); typedef struct { char name[8]; int age; char address[5]; int no; } man; man shiro; strcpy(shiro.name, "shiro"); shiro.age = 54; strcpy(shiro.address, "tokyo"); shiro.no = 0x12345678; // 構造体の全要素の文字コードを出力する unsigned char *ptr6 = (unsigned char *)&shiro; printf("0x - 0d - char\n"); for (int i = 0; i < sizeof(shiro); i++) { printf("%02X - %03d - %c\n", ptr6[i], ptr6[i], ptr6[i]); } /* 0x - 0d - char 73 - 115 - s 68 - 104 - h 69 - 105 - i 72 - 114 - r 6F - 111 - o 00 - 000 - 00 - 000 - 00 - 000 - 36 - 054 - 6 00 - 000 - 00 - 000 - 00 - 000 - 74 - 116 - t 6F - 111 - o 6B - 107 - k 79 - 121 - y 6F - 111 - o 00 - 000 - 40 - 064 - @ 00 - 000 - 78 - 120 - x 56 - 086 - V 34 - 052 - 4 12 - 018 - */
構造体に含まれる配列も、通常の配列と同様に扱うことが出来ます。
C言語では「文字列型」というものが無く、文字char
の配列として表現されます。
しかしながら、C#等のように要素インデックスでアクセスすることもできます。
char[]型
の配列についてみると、以下のようになります。
配列の1つ目の要素は「配列」と同じものを示します。
ここでprintf(”%d", &変数)
と&を付けるとその変数の格納されたアドレスを示します。
char型
は1byteの変数なのでアドレスは1づつずれることになります。
int型
であれば一般に4byteなので、4づつずれます。
また、本質的にC言語の文字は数値であり、printf(”%d", taro.name[0])
やprintf(”%x", taro.name[0])
とすれば文字コード(asciiコード)を示し、printf(”%c", taro.name[0])
とすれば文字を表示します。
#include <stdio.h> #include <cstring> //strcpy,memcpyなど struct person { char name[4]; int age; int no; }; int main(void) { struct person taro; // char 型の配列へはstrcpyを使う strcpy(taro.name, "taro"); printf("name= %s\n", taro.name); // taro printf("age= %d\n", taro.age); // 10 printf("-Char\n"); printf(" 0x - 0d - char - Address\n"); printf("name= %02x - %04d - %c - %08d\n", *taro.name, *taro.name, *taro.name, &taro.name); // t printf("name= %02x - %04d - %c - %08d\n", taro.name[0], taro.name[0], taro.name[0], &taro.name[0]); // t printf("name= %02x - %04d - %c - %08d\n", taro.name[1], taro.name[1], taro.name[1], &taro.name[1]); // a printf("name= %02x - %04d - %c - %08d\n", taro.name[2], taro.name[2], taro.name[2], &taro.name[2]); // r printf("name= %02x - %04d - %c - %08d\n", taro.name[3], taro.name[3], taro.name[3], &taro.name[3]); // o } /* name= taro age= 0 -Char 0x - 0d - char - Address name= 74 - 0116 - t - 06421980 name= 74 - 0116 - t - 06421980 name= 61 - 0097 - a - 06421981 name= 72 - 0114 - r - 06421982 name= 6f - 0111 - o - 06421983 */
++演算子
ポインタも++
演算子を使うことが出来ます。++
演算子を使うと、以下のようにも文字列にアクセスもできます。
ここで、()はあってもなくても++
でアドレスをインクリメントしてアクセスすることが出来ます。
最後の一回は何も格納されていない部分へアクセスしているので、表示されません。
struct person jiro; strcpy(jiro.name, "jiro"); char *ptr2 = jiro.name; printf("-jiro\n"); printf("name= %c\n", *(ptr2)++); printf("name= %c\n", *ptr2++); printf("name= %c\n", *ptr2++); printf("name= %c\n", *(ptr2)++); printf("name= %c\n", *ptr2); /* name= j name= i name= r name= o name= */