Help us understand the problem. What is going on with this article?

文字列をscanf関数で扱うとき

More than 3 years have passed since last update.

当記事をご覧いただきありがとうございます。
昨年末よりプログラミングを本格的に始めた自分の備忘録的な感じで書きます。少しでも他の人の助けにもなるようわかりやすく記述していきます。間違いがあればぜひコメントお願いします。


この記事でお話しすること

  1. scanfの概要について説明します。
  2. scanf関数で、文字列(%s)のときに”&”が不要である理由を説明します。

1. scanfの概要

scanfとは、C言語を学ぶ上で誰もがはじめに通る重要な関数で、”ユーザーに文字や数値を入力させる関数”になります。基本的な書き方については他のページに任せます。

たとえば、次のコードではユーザーに整数の入力を促し、入力された整数を返します。

scanf_1.c
#include <stdio.h>
int main(void) {
    int n;
    printf("整数nを入力してください\n");
    scanf("%d",&n);

    printf("入力された整数は%dです。\n",n);
    return 0;
}

さて、今は整数を扱っているので変換指定が”%d”になっていて、引数nの前に”&”をつけていますね。

文字列を入力させる場合は、変換指定を”%s”にします。しかし、引数nの前に”&”はつけません。これは何故なのでしょうか?

2. 文字列のscanfで”&”が要らない理由

これを考えるために、まずそもそも”&”の役割とは何なのかを見ていきましょう。

ポインタをご存知の人なら理解している話だと思いますが、”&”はアドレス演算子です。変数の前に”&”をつけることによって、変数の値が格納されているメモリのアドレスを知ることができます。

tumblr_inline_nzji3wI8W21t83x5s_540.png

たとえば、先ほどのコードで「13」と入力した場合、もちろん変数nには13が格納されます。「では、13が格納されているアドレス(場所)はどこでしょうか?」という問いに答えてくれるのがアドレス演算子、ということになります。(画像のメモリ番地は、簡単にするために3桁の数字にしています)

scanfで値を格納したいときに、変数の名前を知ってもうれしくありません。知りたいのは格納すべきアドレスの場所なのです。なので、scanfでは&が必要になるのですね。

では、”%s”のときに”&”がいらない理由を説明します。

文字列は配列の形をしています。これを示すために以下のコードを用意しました。

scanf_2.c
#include <stdio.h>
int main(void) {
    char text[50];
    printf("文字列を入力してください\n");
    scanf("%s",text);

    printf("入力された文字列の先頭文字は%cです",text[0]);
    return 0;
}

入力した文字列の先頭文字(text[0])を返すコードです。先ほどと同じように対応させましょう。変数text[0]には先頭文字が格納されています。では、これが格納されているアドレスはどう表せばよいでしょうか?

&text[0]でも正解なのですが、実はこの場合は別解があって、しかもそれが非常に重要なのです…。

実はtext[0]のアドレスはtextが指し示しているのです!

…ん?意味わかんないって?つまりこういうことっすよ!

tumblr_inline_nzji3wdyqQ1t83x5s_540.png

文字列の配列名は、先頭文字のアドレスを指します。この性質があるから、”%s”を指定したときは”&”が不要なのですね。また、画像の中でアスタリスクをしれっと使ってますが、これは「間接演算子」と呼ばれるもので、アドレスを指定してその中身を変数として使うための演算子です。要は、アドレス演算子の逆です。

この本質を理解していると、次のコードも成立することがわかります。

scanf_3.c
#include <stdio.h>
int main(void) {
    char text[50];
    printf("文字列を入力してください\n");
    scanf("%s",text);

    printf("入力された文字列の先頭文字は%cです",*text);
    return 0;
}

printfのところで、間接演算子を使用して先頭文字を呼び出しています。これも先ほどのコードと同じ結果が得られます(どちらのコードも日本語を入力するとバグります!)

3. まとめ

  1. scanfではアドレスを渡す必要があるので、通常”&”が必要になる
  2. 文字列をscanfで扱う場合、配列名が先頭文字のアドレスを指し示しているので、”&”は不要である

間違い指摘や意見大募集です。それでは。

Tsutajiro
DTM / HTML&CSS / C / Ruby 2015年12月から自分で本格的に勉強を始めたばかりなので知識はまだ浅いです。コメントで指摘いただけると非常に助かります。
http://www.tsutajiro.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away