C言語再履修シリーズ。
今日は軽めにしておこう。
ARR34-C. 式中の配列の型は適合していることを保証する
タイトルと内容に差違はなく、特にいうことはない。
私は、ポインタの配列、とか、配列のポインタ、とか、そういう紛らわしいものが苦手だから、違反コードと適合コードを真面目に見ておこう(私が書くんだったら、2次元配列ではなく1次元にして、添字で2次元を制御するだろう)。
int a[ROWS][COLS];
メモリとしては、a[row]から始まるアドレスにint型がCOLS個つながっている。
確認しておこう。
01: #include <stdio.h> 02: 03: enum { ROWS = 10, COLS = 15 }; 04: 05: int main(void) 06: { 07: int a[ROWS][COLS]; 08: 09: printf("sizeof(int)=%ld\n", sizeof(int)); 10: for (int lp = 0; lp < COLS; lp++) { 11: printf("[%d]%p\n", lp, &a[0][lp]); 12: } 13: return 0; 14: }
結果
sizeof(int)=4
[0]0x7fffef42ab30
[1]0x7fffef42ab34
[2]0x7fffef42ab38
[3]0x7fffef42ab3c
[4]0x7fffef42ab40
[5]0x7fffef42ab44
[6]0x7fffef42ab48
[7]0x7fffef42ab4c
[8]0x7fffef42ab50
[9]0x7fffef42ab54
[10]0x7fffef42ab58
[11]0x7fffef42ab5c
[12]0x7fffef42ab60
[13]0x7fffef42ab64
[14]0x7fffef42ab68
アドレスが4ずつずれているので、2番目の添字に対してメモリが順番に並んでいることが分かる。
だから見かけ(?)と違い、int[COLS]が[ROWS]ある、ということになる。
では、適合コードを確認しよう。
01: #include <stdio.h> 02: 03: enum { ROWS = 10, COLS = 15 }; 04: 05: int main(void) 06: { 07: int a[ROWS][COLS]; 08: int (*b)[COLS] = a; 09: 10: for (int lp2 = 0; lp2 < ROWS; lp2++) { 11: for (int lp1 = 0; lp1 < COLS; lp1++) { 12: a[lp2][lp1] = lp2 * 100 + lp1; 13: //printf("[%d][%d] = %d\n", lp2, lp1, a[lp2][lp1]); 14: } 15: } 16: for (int lp = 0; lp < COLS; lp++) { 17: printf("[%2d]a=%d, b=%d\n", lp, a[5][lp], (*(b + 5))[lp]); 18: } 19: return 0; 20: }
結果
[ 0]a=500, b=500
[ 1]a=501, b=501
[ 2]a=502, b=502
[ 3]a=503, b=503
[ 4]a=504, b=504
[ 5]a=505, b=505
[ 6]a=506, b=506
[ 7]a=507, b=507
[ 8]a=508, b=508
[ 9]a=509, b=509
[10]a=510, b=510
[11]a=511, b=511
[12]a=512, b=512
[13]a=513, b=513
[14]a=514, b=514
1000-100の位をrow、10-1の位をcolで表した。
row=5なので、bはポインタに加算することになる。
なお、最初試したときは*bに括弧を付け忘れたので、値があわなかった。
printf("[%2d]a=%d, b=%d\n", lp, a[0][lp], *b[lp]);
とすると、
[ 0]a=0, b=0
[ 1]a=1, b=100
[ 2]a=2, b=200
[ 3]a=3, b=300
[ 4]a=4, b=400
[ 5]a=5, b=500
[ 6]a=6, b=600
[ 7]a=7, b=700
[ 8]a=8, b=800
[ 9]a=9, b=900
[10]a=10, b=-1765474560
[11]a=11, b=0
[12]a=12, b=0
[13]a=13, b=0
[14]a=14, b=-1051150057
みたいに、全然違うところを指している。
Segmentation Faultにならなかっただけ、というところか。
*b[lp]
は、
*(b + lp)
という扱いになるので、0~9までは大丈夫なのだな。
で、私が試したかったのはそういうのではなく、もうちょっと単純なことだ。
unsigned charとuint8_tは同じ型として扱ってくれるのだろうか?という疑問である。
コンパイラの認識がどうなっているのか、だな。
01: #include <stdio.h> 02: #include <stdint.h> 03: 04: int main(void) 05: { 06: unsigned char a[10]; 07: uint8_t *b = a; 08: 09: for (int lp = 0; lp < 10; lp++) { 10: a[lp] = lp; 11: } 12: printf("b=%d\n", *b); 13: 14: return 0; 15: }
うん、gccでもg++でも、-Wや-Wallしても何も出なかった。
unsigned intみたいにすると、gccはwarning、g++はerrorだった。
やっていて気付いたのだが、longって8byte扱いなのね・・・。
てっきり、int=longで、8byteはlong longだけかと思っていた。
64bitのWSLでも、64bitのUbuntu18.04でもそうだった。
strtol()とstrtoll()で悩んだりしたけど、どっちでも同じなのか。
いっそのこと、strtod32()とかstrtod64()とかにしてほしいものだ。
0 件のコメント:
コメントを投稿
コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。
注: コメントを投稿できるのは、このブログのメンバーだけです。