
C言語基礎|ポインタを返す関数
返り値がポインタだと、コードがつながる!―コピーして、そのまま次へ渡すCの書き味を体感しよう。
C言語の関数って「数値を返すもの」という印象が強いかもしれませんが、実はポインタ(アドレス)を返す関数もすごくよく出てきます。
ポインタを返せると何が嬉しいかというと、処理結果(たとえばコピー先の先頭)をそのまま次の関数に渡して、1行でスッとつなげられるんです。
前回の文字列コピー str_copy もまさにそれで、コピー先の先頭を返してくれるから、
- コピーした上で表示する
- 連続でコピーする(処理を鎖みたいにつなぐ)
みたいな書き方ができます。Cらしい“キレの良さ”が出るところですね。

今回扱うポイント(全体像)
この記事で登場する考え方
| 項目 | 何をする話? | 何が得なの? |
|---|---|---|
| ポインタを返す | 関数の返り値がアドレスになる | 結果の場所を次に渡せる |
| 先頭ポインタの保存 | 途中でポインタを進めるので先頭を退避する | 返すべき値を失わない |
| 連続呼び出し | 返り値を次の引数にそのまま渡す | 1行で処理をつなげられる |
表の説明
- ポインタを返す=「結果そのもの」ではなく「結果がある場所」を返す、という感覚です。
- 文字列処理は“先頭を返す”設計が多く、応用が効きます。
サンプルプログラム
「コピーした文字列をそのまま表示」「連続コピー」を両方試せるプログラム例です。
プロジェクト名:chap11-6-1 ソースファイル名:chap11-6-1.c
Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。
#include <stdio.h>
// 文字列srcをdstにコピーし、dstの先頭を返す
char *str_copy(char *dst, const char *src)
{
char *top = dst;
while (*dst++ = *src++) {
// '\0' までコピーしたら終了
}
return top;
}
int main(void)
{
char input[128];
char box1[128];
char box2[128];
printf("合言葉を入力してください:");
scanf("%127s", input);
// コピーして、その返り値(コピー先の先頭)をそのまま表示に渡す
printf("箱1に入れた内容:%s\n", str_copy(box1, input));
// 連続コピー:まず box2 にコピー → その結果(box2先頭)を使って box1 にもコピー
str_copy(box1, str_copy(box2, input));
printf("箱1:%s\n", box1);
printf("箱2:%s\n", box2);
return 0;
}ポインタを返すってどういう意味?
よくある返り値との違い
| 返り値の種類 | 返すもの | 例 | イメージ |
|---|---|---|---|
| int を返す | 数そのもの | 長さ 5 | 答えを返す |
| char * を返す | 文字列の先頭アドレス | コピー先の先頭 | 答えがある場所を返す |
表の説明
- char * を返すと、「このアドレスから文字列がありますよ」と渡せます。
- printf の %s は「渡されたポインタ(先頭アドレス)から '\0' まで」を表示するので、相性が最高です。
なぜ先頭を保存して返す必要があるの?
今回の str_copy は、コピーしながら dst をどんどん進めます。
つまり、最後の方では dst はもう先頭を指していません。
dst が進むので、先頭 top を別で持つ
開始直後
dst -> [ ? ][ ? ][ ? ]...
top -> [ ? ][ ? ][ ? ]...
コピーが進む
dst -> [ ? ][ ? ]...
top -> [ ? ][ ? ][ ? ]...
図の説明
- dst は走査用として移動する
- top は「返したい先頭」を固定して持つ
この二役がポイントです。
関数の書式と、登場する命令の役割
str_copy の書式
- 書式:char *str_copy(char *dst, const char *src);
引数・返り値の役割
| 要素 | 型 | 役割 |
|---|---|---|
| dst | char * | コピー先の先頭を指す。中身を書き換える |
| src | const char * | コピー元の先頭を指す。読むだけ |
| return | char * | コピー先の先頭を返す(dstの先頭) |
表の説明
- const char * は「src 側を変更しないよ」の合図です。
- 返すのは「コピー結果が入った場所の先頭」です。
printf の書式と役割
- 書式:printf(書式文字列, 引数...);
- 何をする命令?:画面に整形して表示する命令
- %s の意味:受け取ったポインタが指す先を「文字列として」表示する('\0' まで)
scanf の書式と役割
- 書式:scanf(書式文字列, 格納先...);
- 何をする命令?:入力を読み取り、指定場所へ書き込む命令
- 今回の %127s:最大127文字まで読み、最後の '\0' 分の余裕を確保する
返り値を利用した「コピーしてそのまま表示」
この形が気持ちいいポイントです。
printf("箱1に入れた内容:%s\n", str_copy(box1, input));
この1行で起きていること
| 順番 | 起きること |
|---|---|
| 1 | str_copy が input を box1 にコピーする |
| 2 | str_copy が box1 の先頭ポインタを返す |
| 3 | printf がそのポインタを受け取り、box1 を文字列として表示する |
表の説明
- 「コピー」と「表示」が自然につながっています。
- printf に渡しているのは box1 そのものではなく、box1 の先頭アドレス(box1[0]の位置)です。
返り値を利用した「連続コピー」
次は、返り値をさらに次の引数へ渡すパターンです。
str_copy(box1, str_copy(box2, input));
連続コピーの流れ(内側→外側)
1) 内側:str_copy(box2, input)
input を box2 にコピー
返り値:box2 の先頭
2) 外側:str_copy(box1, (返り値))
box2 の内容を box1 にコピー
説明
- C では関数呼び出しの入れ子は「内側が先に評価」されます。
- だから「まず box2 を完成させて、その結果を box1 に流し込む」という順序になります。
連続コピーが便利な場面
| 例 | 何が嬉しい? |
|---|---|
| 加工しながら繋ぐ | 変換関数の結果を次の関数へ渡せる。 |
| ログ出力を短くする | 関数の結果をそのまま表示へ渡せる。 |
| 一時領域を使う | 中間結果を経由して最終結果に入れられる。 |
重要な注意(やってはいけない返し方)
「ポインタを返す関数」で一番事故りやすいのはこれです。
危険な返り値と安全な返り値
| 返すポインタ | 安全? | 理由 |
|---|---|---|
| 呼び出し側が用意した配列の先頭 | 安全 | 呼び出し後も生きている。 |
| 文字列リテラルの先頭 | だいたい安全(書き込みは注意) | 静的記憶域にあることが多い。 |
| 関数内のローカル変数のアドレス | 危険 | 関数終了で寿命が切れる。 |
表の説明
- 関数内のローカル配列 char tmp[128]; の先頭を return するのはダメです。
関数が終わった瞬間にその領域は無効になります。
