記事には広告を含む場合があります。

C言語入門講座

【C/C++】配列と文字列を示すポインタの初期化・代入方法

おゆ

組み込み系プログラマとして5年働いていた元エンジニアです。得意言語はC言語とC++。本サイトでは学生および新人組み込み系プログラマに向けてプログラミング知識をわかりやすく解説しています。

ポインタは配列を示すこともできます。

この記事では配列を示すポインタの使い方を解説します。

文字列からなる配列の場合は数字の配列と一部使い方が異なるので、その点についても解説します。

配列を示すポインタの初期化・出力

数字・文字からなる配列を示すポインタ

ポインタの初期化方法

定義時:
データ型 *ポインタ名=配列名;
(またはデータ型 *ポインタ名=&配列名[0];)

代入時:
ポインタ名=配列名;
(またはポインタ名=&配列名[0];)

ポインタまたは配列の要素を取り出す方法

要素番号を指定:
配列名[要素番号]

(またはポインタ名[要素番号])

アドレスを指定:
*(配列名+要素番号)
(または*(ポインタ名+要素番号))

例1:

#include <stdio.h>
void main()
{
    int array[3]={1,2,3};
    int *ptr=array; //*ptr=&array[0]と書いても同じ意味

    printf("array[0]=%d\n",array[0]);
    printf("*array=%d\n",*array);
    printf("ptr[0]=%d\n",ptr[0]);
    printf("*ptr=%d\n",*ptr);

    printf("array[1]=%d\n",array[1]);
    printf("*(array+1)=%d\n",*(array+1));
    printf("ptr[1]=%d\n",ptr[1]);
    printf("*(ptr+1)=%d\n",*(ptr+1));

    printf("array[2]=%d\n",array[2]);
    printf("*(array+2)=%d\n",*(array+2));
    printf("ptr[2]=%d\n",ptr[2]);
    printf("*(ptr+2)=%d\n",*(ptr+2));
}

実行結果

array[0]=1
*array=1
ptr[0]=1
*ptr=1
array[1]=2
*(array+1)=2
*(ptr+1)=2
ptr[1]=2
array[2]=3
*(ptr+2)=3
ptr[2]=3

ポインタは「array[3]={1,2,3};」のように1つ1つの要素を並べて初期化できないので、「*ptr=array;」のように配列の先頭アドレスを代入して初期化します。

配列arrayの先頭アドレスは&array[0]でも表せます。

おゆ
ポインタptrは配列arrayの先頭アドレスを示しているという点がポイントです。

要素を取り出す方法は配列もポインタも同じです。

ポインタに関してはptr++のようにインクリメントすることで次のアドレスを指定することもできます。

例2:

#include <stdio.h>
void main()
{
    int array[3]={1,2,3};
    int *ptr=array;

    //array++; //コンパイルエラー

    ptr++;
    printf("*ptr=%d\n",*ptr);
    ptr++;
    printf("*ptr=%d\n",*ptr);
}

実行結果

*ptr=2
*ptr=3

配列で同じことをやるとコンパイルエラーになります。

ポインタに格納されているアドレスは自由に変えられますが、配列のアドレスは変更不可なのです。

文字列リテラルを示すポインタ

文字列リテラルとは"Hi"のようにコードにべた書きした文字列のことです。

char型ポインタは下の例のstrのように文字列リテラルを使って初期化することもできます。

例3:

#include <stdio.h>
void main()
{
    char array[3]="Hi";
    char *ptr=array; //配列を示すポインタ
    char *str="Hi"; //文字列リテラルを示すポインタ

    printf("array=%s\n",array);
    printf("ptr=%s\n",ptr);
    printf("str=%s\n",str);
    
    printf("str[0]=%c\n",str[0]);
    printf("*str=%c\n",*str);
}

実行結果

array=Hi
ptr=Hi
str=Hi
str[0]=H
*str=H

配列を示すポインタへの値の代入

数字・文字からなる配列を示すポインタ

ポインタまたは配列への値の代入方法

要素番号を指定する方法:
ポインタ名[要素番号]=代入値;

(または配列名[要素番号]=代入値;)

アドレスを指定する方法:
*(ポインタ名+要素番号)=代入値;
(または*(配列名+要素番号)=代入値;)

例4:

#include <stdio.h>
void main()
{
    int array[3]={1,2,3};
    int *ptr=array; //*ptr=&array[0]と書いても同じ意味

    array[0]=0;
    ptr[0]=0;
    printf("array[0]=%d\n",array[0]);
    printf("ptr[0]=%d\n",ptr[0]);
    
    *array=1;
    *ptr=1;
    printf("array[0]=%d\n",array[0]);
    printf("ptr[0]=%d\n",ptr[0]);
    
    *(array+1)=0;
    *(ptr+1)=0;
    printf("array[1]=%d\n",array[1]);
    printf("ptr[1]=%d\n",ptr[1]);
}

実行結果

array[0]=0
ptr[0]=0
array[0]=1
ptr[0]=1
array[1]=0
ptr[1]=0

文字列リテラルを示すポインタへの代入は禁止【配列とポインタの違い】

以下の例のstrのように文字列リテラルで初期化したポインタは要素の書き換えが禁止されています。

例5:

#include <stdio.h>
void main()
{
    char *str="Hi";
    //*str='h'; //異常終了するNGコード
    //str[0]='h'; //異常終了するNGコード
    str="Hello"; //これはOK
    printf("%s\n",str);

    char array[3]="Hi";
    char *ptr=array;
    //array="Hello"; //コンパイルエラーが出るNGコード
    ptr="Hey"; //これはOK
    printf("%s\n",ptr);
}

実行結果

Hello
Hey

「*str='h';」のように要素を1つ指定して書き換えるとコンパイルは通りますが、実行時に異常終了します。

「str="Hello";」のように文字列全体の書き換えは可能です。

strの要素書き換えがNGな理由は、文字列リテラルがメインメモリのテキスト領域に保存されているからです。

テキスト領域について詳しくは以下の記事で解説しています。

参考スタック領域・ヒープ領域・静的領域の仕組みと違いをわかりやすく解説

プログラムを実行するとメインメモリはどうやって使われるの? スタック領域・ヒープ領域・静的領域ってどんな仕組み?それぞれどう違うの? こんな疑問にお答えします。 おゆプログラマーにとってメインメモリの ...

続きを見る

strはテキスト領域に書き込まれた文字列リテラルを示しています。

テキスト領域の値はプログラムの実行中常に存在し、途中で値を書き換えてはいけないというルールがあります。

そのためstrの書き換えは禁止されているのです。

ただし「str="Hello";」は既にある値を書き換えるのではなく、新たな文字列リテラルをテキスト領域に作り、strはそこを示すポインタとなるので問題ありません。

ちなみにarrayやptrの値はスタック領域に保存されます。

スタック領域は書き換え可能なため、各要素は自由に書き換えられます。

ただし「array="Hello";」のように配列全体の値を一度に書き換えるのは禁止です。

おゆ
理由としてはおそらくスタック領域には変数が隙間なく保存されているため、文字列リテラルの代入を許すと変数サイズが変わって他の変数に影響する可能性があるからだと思います。

arrayの文字列を書き換えたいのであればstrcpy関数を使います。(文字数がarrayの要素数を超えないように注意。)

参考【C言語】配列の長さ(要素数)取得方法・配列のコピー方法まとめ

配列を使っているとfor文で列の長さ分だけループさせたい場合や、配列のコピーを作りたい場合があります。 それらの方法を解説します。 記事の内容 配列の長さ(要素数)を求める方法 配列をコピーする方法 ...

続きを見る

一方でptrのようにポインタへの文字列リテラルの代入は可能です。

これはstrにHelloを代入したときと同じでテキスト領域に新たな文字列リテラルを作り、ptrはその文字例リテラルを示すポインタとなるためです。

ちなみに「ptr="Hello";」と書くとptrはstrと同じアドレスを示すようになります。

ポインタを使った配列の要素の入れ替え

2つの配列の要素をすべて入れ替えたいんだけど、for文で1つ1つ入れ替えていると時間が掛かる…ポインタを使って高速で入れ替えできないのかな?
おゆ
残念ながら配列の要素をポインタで高速で入れ替える方法はありません。ここでは代わりの対処法を解説します。

配列が示すアドレスは変更できません。

初期化以外のタイミングで配列に複数の要素を一気に代入することもできないため、要素を入れ替えたいのならfor文で1つ1つ書き換える必要があります。

しかしそれでは時間がかかり過ぎて困るという方向けに、ポインタを使った対処法を提案します。

ポインタが示すアドレスは好きに変更できますよね。

そこで配列を示すポインタを用意し、入れ替えたいタイミングでポインタが示す配列を入れ替えます。

例6:

#include <stdio.h>
void main()
{
    int array1[3]={1,2,3};
    int array2[3]={4,5,6};
    int *ptr1=array1;
    int *ptr2=array2;

    //array1=array2; //コンパイルエラー

    printf("ptr1=%d,%d,%d\n",*ptr1,*(ptr1+1),*(ptr1+2));
    printf("ptr2=%d,%d,%d\n",*ptr2,*(ptr2+1),*(ptr2+2));

    ptr1=array2; //入れ替え
    ptr2=array1; //入れ替え
    printf("ptr1=%d,%d,%d\n",*ptr1,*(ptr1+1),*(ptr1+2));
    printf("ptr2=%d,%d,%d\n",*ptr2,*(ptr2+1),*(ptr2+2));
}

実行結果

ptr1=1,2,3
ptr2=4,5,6
ptr1=4,5,6
ptr2=1,2,3

配列のアドレスは変更不可なのでarray1=array2というコードはコンパイルエラーになります。

そこで配列array1とarray2を示すポインタptr1とptr2を用意します。

配列を入れ替えたいタイミングでポインタが示す配列を入れ替えます。

ポインタの入れ替えは一瞬で済むのでプログラムの実行時間を抑えられます。

ブログランキング参加中。クリックしてもらえると励みになります。

ブログランキング・にほんブログ村へ

-C言語入門講座