C言語の航海日誌(5)〜文字列をポインタで渡す〜

前回の知見と繋がりながらもちょっとズレる発見があったのでメモします。

note103.hateblo.jp

まず前回の復習をしておくと、int型の数値をこんなふうにポインタで渡す、ということをやった場合。

#include <stdio.h>

void foo(int *c, int *d)
{
    printf("*c: %d\n", *c);
    printf(" c: %p\n",  c);
    printf("*d: %d\n", *d);
    printf(" d: %p\n",  d);
}
int main(void)
{
    int a = 33;
    int b = 55;
    printf(" a: %d\n",  a);
    printf("&a: %p\n", &a);
    printf(" b: %d\n",  b);
    printf("&b: %p\n", &b);

    printf("\n");

    foo(&a, &b);

    return 0;
}

実行。

 a: 33
&a: 0x7fff5377a60c
 b: 55
&b: 0x7fff5377a608

*c: 33
 c: 0x7fff5377a60c
*d: 55
 d: 0x7fff5377a608

こんな感じで、メイン関数からはアドレス(&a, &b)を投げているのに、受け取る側のfoo関数では数値(*c, *d)で受け取っているので、直感的じゃないなあ・・という結論だったのですが。

同様の感覚で文字列を渡そうとしたらハマりました。
文字列で同様のことをしようと思ったら、こうなるようです。

#include <stdio.h>

void foo(char *c, char *d)
{
    printf(" c: %s\n", c);
    printf(" c: %p\n", c);
    printf(" d: %s\n", d);
    printf(" d: %p\n", d);
}
int main(void)
{
    char a[] = "apple";
    char b[] = "orange";
    printf(" a: %s\n",  a);
    printf("&a: %p\n", &a);
    printf(" b: %s\n",  b);
    printf("&b: %p\n", &b);

    printf("\n");

    foo(a, b);

    return 0;
}

実行

 a: apple
&a: 0x7fff59a3360a
 b: orange
&b: 0x7fff59a33603

 c: apple
 c: 0x7fff59a3360a
 d: orange
 d: 0x7fff59a33603

ということで、数値の場合は「*c」で受け取ったものが値(「33」)で、「c」はアドレス(「0x7fff5377a60c」)でしたが、今回は値(「apple」)もアドレス(「0x7fff59a3360a」)も「c」でした。

じゃあ、文字列を渡したときの「*c」には何が入ってるの? と思って見てみると、

#include <stdio.h>

void foo(char *c)
{
    printf("*c: %c\n", *c);
}
int main(void)
{
    char a[] = "apple";

    foo(a);

    return 0;
}

実行

*c: a

ということで、「apple」の最初の1文字が入っていました。

これはCにおける文字列がじつはchar型の配列で、その先頭要素が出てきているということだと思いますが、ともあれ問題は、「文字列を参照渡しした場合には、数値のように * が付いた変数に値が入っているわけではなく、* を取ったほうの変数に入っている」ということで、まあそういうものだと覚えればいい気もしますが、あとで回収すべき宿題が増えた感じでもあります。