1. 配列とアドレス
ポインタは、変数のアドレスを格納するよりは、配列のアドレスを格納する方がずっと使用頻度は高くなります。
(1) 変数と配列のアドレスの指定の違い
変数と配列のアドレスの指定の違いに注意してください。
変数のアドレス
char a = 123; のとき

a | 変数 a の値(123) |
---|---|
&a | 変数 a のアドレス(1000) |
配列のアドレス
char a[4] = “ABC”; のとき

a | 配列 a の先頭要素のアドレス(1000) |
---|---|
a[0] a[1] a[2] a[3] |
a[0] の値(’A’) a[1] の値(’B’) a[2] の値(’C’) a[3] の値(’\0’) |
&a[0] &a[1] &a[2] &a[3] |
a[0]のアドレス(1000) a[1]のアドレス(1001) a[2]のアドレス(1002) a[3]のアドレス(1003) |
※ このとき a == &a[0]
(2) ポインタの宣言と使い方(配列のアドレスを格納)
ポインタは必ず、
- 宣言
- 値(アドレス)の設定
- 使用
の 3ステップで用います。
配列のアドレスをポインタに設定した場合、配列の各要素をポインタを使ってアクセスすることができます。 この場合、「ポインタの値を変えずにデータを参照」するやり方と、 「ポインタの値そのものを更新してデータを参照」するやり方の2通りがあります。
どちらを選んでもかまいませんが、後者の場合には、ポインタに格納されているアドレスが更新されますので、 後でそのポインタを使おうとしたときに、更新されたことを忘れていると、 思わぬエラーの原因になりますので、注意が必要です。


〇 演習問題
問1
次のプログラムの空欄部を埋めて、プログラムを完成させなさい。
// ポインタが指している文字列を1文字ずつ表示するプログラム
#include <stdio.h>
int main(void)
{
char str[] = "Computer";
______; // ポインタの宣言
______; // ポインタの値設定
while (______ != '\0') { // 文字列終了までループ
putchar(________); // ポインタの指す文字を出力
________; // ポインタの更新
}
putchar('\n');
return 0;
}
【実行結果例】
Computer
問2
文字型配列に “AbcDefGHijk1234lmNOP” という文字列が格納されている。
ポインタを用いて、この文字列の小文字を全て大文字に変換しなさい。
【実行結果例】
str = ABCDEFGHIJK1234LMNOP
問3
文字型配列 str1 に、”ABCDEFGHIJKLMNOPQRSTUVWXYZ” という文字列が格納されている。
ポインタを2つ用いて、文字型配列 str2 に、この文字列を逆順に格納しなさい。
【実行結果例】
str1 = ABCDEFGHIJKLMNOPQRSTUVWXYZ
str2 = ZYXWVUTSRQPONMLKJIHGFEDCBA
解答例
// 問1
#include <stdio.h>
int main(void)
{
char str[] = "Computer";
char *p; // ポインタの宣言
p = str; // ポインタの値設定
while (*p != '\0') { // 文字列終了までループ
putchar(*p); // ポインタの指す文字を出力
p++; // ポインタの更新
}
putchar('\n');
return 0;
}
// 問2
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char str[] = "AbcDefGHijk1234lmNOP";
char *p = str;
while (*p != '\0') {
*p = toupper(*p);
p++;
}
printf("str = %s\n", str);
return 0;
}
// 問3
#include <stdio.h>
int main(void)
{
char str1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char str2[30];
char *p1, *p2;
p1 = str1;
p2 = str2;
while (*p1 != '\0') { // (1)p1をstr1の最後まで進める
p1++;
}
while (p1 > str1) { // p1がstr1を指す間ループ
p1--; // (2)
*p2 = *p1; // (3)p2の指すところにp1の指す値を代入
p2++; // (4)
}
*p2 = '\0'; // (5)'\0'の付加
printf( "str1 = %s\n", str1 );
printf( "str2 = %s\n", str2 );
return 0;
}

2. ポインタのアドレス計算
実は、
「ポインタに 1 加える」ということは
「ポインタに格納されているアドレス + 1番地」ではなく
「ポインタに格納されているアドレス + 型のサイズ」
つまり、同じ「++p」でも
- char型なら サイズ 1バイトなので 1番地
- int型なら サイズ 4バイトなので 4番地
- long型なら サイズ 4バイトなので 4番地
- float型なら サイズ 4バイトなので 4番地
- double型なら サイズ 8バイトなので 8番地
アドレスが増えることになります。(型サイズは処理系に依存します)
何故このようにするのでしょう?それは、型によって増分値を考慮するのは効率が悪いからです。

〇 演習問題
ポインタを用いて以下の処理をプログラムにしなさい。
問1
大きさ10の整数型配列を用意し、下図のように初期設定しなさい。
この配列を順に調べ、奇数の値のみ、別の大きさ10の整数型配列に代入しなさい。
また、配列の中身と、何個格納したかを画面表示しなさい。

【実行結果例】
15
45
9
71
37
格納個数 = 5
問2
整数型の配列 101個に、下図のように、順にその配列の添え字番目までの総和を格納しなさい。

【実行結果例】
0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 210 231 253 276 300 325 351 378 406 435 465 496 528 561 595 630 666 703 741 780 820 861 903 946 990 1035 1081 1128 1176 1225 1275 1326 1378 1431 1485 1540 1596 1653 1711 1770 1830 1891 1953 2016 2080 2145 2211 2278 2346 2415 2485 2556 2628 2701 2775 2850 2926 3003 3081 3160 3240 3321 3403 3486 3570 3655 3741 3828 3916 4005 4095 4186 4278 4371 4465 4560 4656 4753 4851 4950 5050
解答例
// 問1
#include <stdio.h>
int main(void)
{
int data1[10] = {10, 15, 22, 45, 9, 66, 71, 4, 37, 82};
int data2[10], cnt;
int *p1, *p2;
cnt = 0;
p1 = data1; // 配列data1のアドレスをp1に設定
p2 = data2; // 配列data2のアドレスをp2に設定
for (int i = 0; i < 10; i++) {
if ((*p1 % 2) == 1) { // p1の指す内容が奇数なら
*p2 = *p1; // p2の指す中身に代入
printf("%d\n", *p2);
p2++; // ポインタp2の更新
cnt++;
}
p1++; // ポインタp1の更新
}
printf("格納個数 = %d\n", cnt);
return 0;
}
// 問2
#include <stdio.h>
int main(void)
{
int a[101], *p;
p = a; // ポインタpに配列aのアドレス設定
*p = 0; // 先頭に0代入
printf("%d ", *p);
for (int i = 1; i < 101; i++) {
p++; // ポインタpの更新
*p = *(p - 1) + i; // 一つ前の値にiを加えて代入
printf("%d ", *p);
}
printf("\n");
return 0;
}
● 補足1:scanfで文字列を入力する
実は、
char str[80];
scanf("%79s", str);
は、「配列strの先頭要素のアドレスが示すメモリエリアに入力データを文字列として格納」の意味になります。
● 補足2:2次元配列のアドレス
2次元配列の場合のアドレスは次のように表します。
char str[3][7] = {
"ABC", "DEFGHI", "JK"
};

str | 配列 str の先頭要素のアドレス(1000) |
---|---|
str[0] str[1] str[2] |
str[0] の先頭要素のアドレス(1000) str[1] の先頭要素のアドレス(1007) str[2] の先頭要素のアドレス(1014) |
&str[0][0] &str[1][0] &str[2][0] |
str[0][0] のアドレス(1000) str[1][0] のアドレス(1007) str[2][0] のアドレス(1014) |
str[0][0] str[1][0] str[2][0] |
str[0][0] の中身(’A’) str[1][0] の中身(’D’) str[2][0] の中身(’J’) |
このとき str == str[0] == &str[0][0]
コメント