C言語を学習していると、割と初期のほうに出てくる配列ですが、実は、C言語の壁と言われるポインタと深い関係があります。
この記事ではC言語の配列とポインタの関係と、[]
演算子と*
(間接演算子)の関係をまとめました。ある程度ポインタのことがわかる人向けです。
基本
ptr[i]
と*(ptr+i)
は同じ意味になります。そのため、ソースコード中で相互に書き換え可能です。
(ptr[i][j]
などの2次元配列を扱う場合は、*(*(ptr+i)+j)
と置き換えできます。@SaitoAtsushi さんありがとうございます。)
#include <stdio.h>
int main (void)
{
char arr[10]={'a','b','c','d','e','f','g','h','j','k'};
printf("arr[1]の出力結果:%c\n",arr[1]);
printf("*(arr+1)の出力結果:%c\n",*(arr+1));
return 0;
}
arr[1]の出力結果:b
*(arr+1)の出力結果:b
配列を関数に渡すとき
配列名に[]をつけなかった場合、配列の先頭アドレスを示します。
配列の先頭アドレスを関数に渡すことで、関数中で配列を参照・書き換えすることが可能です。
また、配列に関数を渡す際は、要素数の情報を別途与える必要があります。(sizeof(in_ptr)
では不可能、理由は後述)
#include <stdio.h>
void func1(char *in_ptr, int len);
int main (void)
{
char arr[10]={'a','b','c','d','e','f','g','h','j','k'};
printf("arrの中身:%p\n",arr); /* 配列名に[]を付けない場合は配列の先頭アドレスを示す */
printf("arr[5]書換前:%c\n",arr[5]);
func1(arr, sizeof(arr)/sizeof(char));/* 配列の先頭アドレスを関数に渡す */
printf("arr[5]書換後:%c\n",arr[5]);
return 0;
}
void func1(char *in_ptr, int len)
{
int i;
for(i=0;i<len;i++){
in_ptr[i] = 'x'; /* in_ptr[i]の代わりに*(in_ptr+i)でもOK */
}
}
arrの中身:0x7ffed07d95a6
arr[5]書換前:f
arr[5]書換後:x
配列名と通常のポインタとの違い
配列名は基本的にはポインタと同列に扱うことができますが、以下の違いがあります。
- ポインタの書き換えができない
-
sizeof
したときに配列サイズを持つ
ポインタの書き換えができない
ポインタであれば、以下のようにポインタをインクリメントして、配列の次の要素に移動することができます。
#include <stdio.h>
int main (void)
{
char arr[10]={'a','b','c','d','e','f','g','h','j','k'};
char *ptr;
ptr = arr; /* 配列の先頭アドレスをポインタに格納 */
while (*ptr != 'g'){
putchar(*ptr);
ptr++; /* ポインタのアドレスをインクリメントし次の要素を参照 */
}
return 0;
}
abcdef
しかし、これと同様のことを配列名でやろうとするとエラーになります。読み取り専用のポインタとして考えるのが一番自然かと思います。
while (*arr != 'g'){
putchar(*arr);
arr++; /* ポインタのアドレスをインクリメントし次の要素を参照 */
}
/home/ec2-user/environment/ctest/main.c: In function ‘main’:
/home/ec2-user/environment/ctest/main.c:12:12: error: lvalue required as increment operand
arr++; /* ポインタのアドレスをインクリメントし次の要素を参照
sizeof
したときに配列サイズを持つ
配列名でsizeof
を実施すると、型のサイズ(今回はcharなので1)×要素数(10)の値が返ってきます。一方、通常のポインタの場合は、常に同じ値(64ビット環境であれば8、32ビット環境であれば4)が返ってきます。
配列を関数に渡す際は、あくまでも先頭アドレスを渡すことになるため、sizeofした時の値は通常のポインタと同様の値が返ります。(先述した関数中で配列の要素数を取得できない理由がこれです。)
#include <stdio.h>
void func1 (char *in_ptr);
int main (void)
{
char arr[10]={'a','b','c','d','e','f','g','h','j','k'};
char *ptr;
ptr = arr; /* 配列の先頭アドレスをポインタに格納 */
printf("sizeof(arr):%zu\n",sizeof(arr));
printf("sizeof(ptr):%zu\n",sizeof(ptr));
func1(arr); /* 配列の先頭アドレスを関数に渡す */
return 0;
}
void func1 (char *in_ptr)
{
printf("sizeof(in_ptr):%zu\n",sizeof(in_ptr));
}
sizeof(arr):10
sizeof(ptr):8
sizeof(in_ptr):8
編集履歴
-
@fujitanozomu さんより、size_t型の書式指定子の編集リクエストをいただいたので反映しました。signedかunsignedかを考えず、とりあえず
%d
にするのは私の悪い癖です。ただ、%zu
は初見だったので調べたところ、以下の記事が参考になりました。
printf() > size_t型の書式指定子 > %zu - @SaitoAtsushi さんより、コメントをいただいたので検証して反映しました。