実験1:配列の最小値・最大値をポインタで取得
ソースコード1(アドレス経由の間接代入)
#include <stdio.h>
#define N 5
void input(int arr[], int len);
void output(int arr[], int len);
void find_min_max(int arr[], int len, int *pmin, int *pmax);
int main() {
int a[N];
int min, max;
printf("データを%d個入力:\n", N);
input(a, N);
printf("データは: \n");
output(a, N);
printf("データ処理...\n");
find_min_max(a, N, &min, &max);
printf("結果:\n");
printf("min = %d, max = %d\n", min, max);
return 0;
}
void input(int arr[], int len) {
for (int i = 0; i < len; ++i)
scanf("%d", &arr[i]);
}
void output(int arr[], int len) {
for (int i = 0; i < len; ++i)
printf("%d ", arr[i]);
printf("\n");
}
void find_min_max(int arr[], int len, int *pmin, int *pmax) {
*pmin = *pmax = arr[0];
for (int i = 0; i < len; ++i) {
if (arr[i] < *pmin)
*pmin = arr[i];
else if (arr[i] > *pmax)
*pmax = arr[i];
}
}
ソースコード2(ポインタを返す関数)
#include <stdio.h>
#define N 5
void input(int arr[], int len);
void output(int arr[], int len);
int *find_max(int arr[], int len);
int main() {
int a[N];
int *pmax;
printf("データを%d個入力:\n", N);
input(a, N);
printf("データは: \n");
output(a, N);
printf("データ処理...\n");
pmax = find_max(a, N);
printf("結果:\n");
printf("max = %d\n", *pmax);
return 0;
}
void input(int arr[], int len) {
for (int i = 0; i < len; ++i)
scanf("%d", &arr[i]);
}
void output(int arr[], int len) {
for (int i = 0; i < len; ++i)
printf("%d ", arr[i]);
printf("\n");
}
int *find_max(int arr[], int len) {
int max_idx = 0;
for (int i = 0; i < len; ++i)
if (arr[i] > arr[max_idx])
max_idx = i;
return &arr[max_idx];
}
考察
- ソースコード1では、
find_min_max関数がポインタ経由で最小値・最大値を外部変数に書き込んでいる。
- ソースコード2では、最大値を持つ要素のアドレスを返すことで、呼び出し側で
*pmaxとして値を取得している。
- どちらの手法もポインタを活用しており、関数間でデータを効率的に受け渡せる。
実験2:文字列の交換
ソースコード1(char配列のコピーによる交換)
#include <stdio.h>
#include <string.h>
#define N 80
int main() {
char s1[N] = "Learning makes me happy";
char s2[N] = "Learning makes me sleepy";
char tmp[N];
printf("sizeof(s1) vs strlen(s1):\n");
printf("sizeof(s1) = %zu\n", sizeof(s1));
printf("strlen(s1) = %zu\n", strlen(s1));
printf("\n交換前:\n");
printf("s1: %s\n", s1);
printf("s2: %s\n", s2);
printf("\n交換中...\n");
strcpy(tmp, s1);
strcpy(s1, s2);
strcpy(s2, tmp);
printf("\n交換後:\n");
printf("s1: %s\n", s1);
printf("s2: %s\n", s2);
return 0;
}
ソースコード2(ポインタによる交換)
#include <stdio.h>
#include <string.h>
#define N 80
int main() {
char *s1 = "Learning makes me happy";
char *s2 = "Learning makes me sleepy";
char *tmp;
printf("sizeof(s1) vs strlen(s1):\n");
printf("sizeof(s1) = %zu\n", sizeof(s1));
printf("strlen(s1) = %zu\n", strlen(s1));
printf("\n交換前:\n");
printf("s1: %s\n", s1);
printf("s2: %s\n", s2);
printf("\n交換中...\n");
tmp = s1;
s1 = s2;
s2 = tmp;
printf("\n交換後:\n");
printf("s1: %s\n", s1);
printf("s2: %s\n", s2);
return 0;
}
考察
- ソースコード1では配列要素全体を
strcpyでコピーするため、文字列が長いとコストが高い。ただし内容の実体を交換する。
- ソースコード2ではポインタの指し先を交換するだけで高速だが、元の文字列リテラルは変更不可。なお
sizeof(s1)はポインタ変数自体のサイズ(通常8バイト)を返し、strlenは文字列長を返す。
- ポインタ交換は可能だが、配列の場合には使えない(配列名は先頭要素への定数ポインタのため再代入不可)。
実験3:二次元配列へのアクセス(ポインタのバリエーション)
#include <stdio.h>
int main() {
int x[2][4] = {{1, 9, 8, 4}, {2, 0, 4, 9}};
int i, j;
int *ptr1;
int (*ptr2)[4];
printf("方法1: 配列名+添字で直接アクセス\n");
for (i = 0; i < 2; ++i) {
for (j = 0; j < 4; ++j)
printf("%d ", x[i][j]);
printf("\n");
}
printf("\n方法2: 要素へのポインタptr1で間接アクセス\n");
for (ptr1 = &x[0][0], i = 0; ptr1 < &x[0][0] + 8; ++ptr1, ++i) {
printf("%d ", *ptr1);
if ((i + 1) % 4 == 0)
printf("\n");
}
printf("\n方法3: 行へのポインタptr2(int(*)[4])で間接アクセス\n");
for (ptr2 = x; ptr2 < x + 2; ++ptr2) {
for (j = 0; j < 4; ++j)
printf("%d ", *(*ptr2 + j));
printf("\n");
}
return 0;
}
考察
- 方法2では一次元のポインタを使って全要素を順にアクセスしている。メモリ上の連続性を利用。
- 方法3では行を表すポインタ
ptr2を使い、各行の先頭から列要素にアクセス。添字演算子を使わずポインタ演算で記述。
- いずれも二次元配列を効率的に走査できる方法だが、可読性・保守性を考慮して適切な方法を選ぶ。
実験4:文字列中の特定文字を置換
#include <stdio.h>
#define N 80
void replace(char *str, char old_char, char new_char);
int main() {
char text[N] = "Programming is difficult or not, it is a question.";
printf("元のテキスト:\n");
printf("%s\n", text);
replace(text, 'i', '*');
printf("処理後:\n");
printf("%s\n", text);
return 0;
}
void replace(char *str, char old_char, char new_char) {
while (*str) {
if (*str == old_char)
*str = new_char;
str++;
}
}
考察
- ポインタを使って文字列を走査し、条件に合致する文字を置換している。
- 元の配列を直接書き換えるので、元の文字列が変更不可能なリテラルでは使えない(今回の
textは配列なので問題なし)。
- 文字列中の全ての
iを*に置換する処理。
実験5:指定文字で文字列を打ち切る
#include <stdio.h>
#define N 80
char *str_trunc(char *str, char x);
int main() {
char str[N];
char ch;
while (printf("文字列を入力: "), gets(str) != NULL) {
printf("打ち切り文字を入力: ");
ch = getchar();
printf("打ち切り処理中...\n");
str_trunc(str, ch);
printf("処理後の文字列: %s\n\n", str);
getchar(); // 改行を読み飛ばす
}
return 0;
}
char *str_trunc(char *str, char x) {
char *p = str;
while (*p != '\0') {
if (*p == x) {
*p = '\0';
break;
}
p++;
}
return str;
}
考察
getsを使用しているが、安全でないため本来はfgets推奨。ここでは実験的なコード。
getchar()で打ち切り文字を読んだ後、改行をgetchar()でもう一度読んで消費している点に注意。
str_truncは最初にxが見つかった位置にヌル文字を書き込み、それ以降の文字を切り捨てる。