C言語初心者です。よろしくお願いします。

while(*a)のところで質問があります。
文字列aがヌル文字になるまでを、printf文で追いかけたいと思っています。
while(*a)のところで、printf("a = %s¥n", a); の用に書くと、文字列aがヌル文字に
近づいているのが分かるのですが、printf("a = %s¥n", *a);と書くと実行時にエラーになってしまいます。
while(*a)を抜けた後でなら、printf("a = %s¥n", *a);で a = (null)と表示されます。
なぜwhile(*a)内では、printf("a = %s¥n", *a);と書くと実行時にエラーになるのでしょうか。どなたかよろしくお願いいたします。


// mystrcat01.c

#include <stdio.h>
#include <string.h>

char *mystrcat(char *a, char *b);

int main()
{
char str1[16] = "abc";
char *str2 = "def";

printf("%s¥n", mystrcat(str1, str2) );

return 0;
}

char *mystrcat(char *a, char *b)
{
char *aorg;

aorg = a;

while(*a) //文字列aのヌル文字の所まで移動する
{
printf("test¥n");
printf("a = %s¥n", a);
// printf("a = %s¥n", *a);
*a++;
}
printf("¥n");
printf("a = %s¥n", *a);
printf("¥n");
while(*a++ = *b++)
//文字列aのヌル文字の所から文字列bの文字を一つずつ入れていく
//ヌル文字の所からa++としても先のアドレスへは進まない

return aorg;
}

回答の条件
  • 1人5回まで
  • 登録:2009/03/05 00:26:33
  • 終了:2009/03/05 20:22:05

ベストアンサー

id:ardarim No.1

ardarim回答回数892ベストアンサー獲得回数1422009/03/05 01:23:09

ポイント30pt

書式文字列%sを渡す場合には、引数は文字列(char型のポインタ)を指定してあげなくてはなりません。

mystrcat関数内では、仮引数として char *a と定義されていますので a はchar型のポインタです。

従って、

printf("a = %s¥n", a);

は適切なコードです。


ここで、a はchar型のポインタですが、*a はchar型です。(ポインタではありません)

そのため、

printf("a = %s¥n", *a);

と書いてしまうと、%sに渡す値が文字列(char型のポインタ)ではなく文字(char型)になってしまいます。

printf関数がchar型のポインタを期待しているところへ、char型の値を渡してしまうことになるので、printf関数は正しく処理を行えず実行時エラーとなります。


while(*a)を抜けた後は、*a の値はヌル文字(つまり、数値で言えば 0)になりますので、

printf("a = %s¥n", *a);

というコードは

printf("a = %s¥n", 0);

と等価になります。

ポインタで 0 と言う値は特別な意味を持っていて、俗にNULLポインタなどと呼ばれます(空のポインタ)。この場合に限っては実行時エラーとならず、空の文字列を意味する "(null)" を表示します。これは文字列の終端を意味するNULL文字のことではなく、NULLポインタであるという意味です。


なお、while(*a)内で1文字ずつ追いかけたい場合は、%sではなく%cを使用します。

printf("a = %c¥n", *a);
id:gbs01

なるほど。

良くわかりました。

ありがとうございます。

2009/03/05 20:14:24

その他の回答(3件)

id:ardarim No.1

ardarim回答回数892ベストアンサー獲得回数1422009/03/05 01:23:09ここでベストアンサー

ポイント30pt

書式文字列%sを渡す場合には、引数は文字列(char型のポインタ)を指定してあげなくてはなりません。

mystrcat関数内では、仮引数として char *a と定義されていますので a はchar型のポインタです。

従って、

printf("a = %s¥n", a);

は適切なコードです。


ここで、a はchar型のポインタですが、*a はchar型です。(ポインタではありません)

そのため、

printf("a = %s¥n", *a);

と書いてしまうと、%sに渡す値が文字列(char型のポインタ)ではなく文字(char型)になってしまいます。

printf関数がchar型のポインタを期待しているところへ、char型の値を渡してしまうことになるので、printf関数は正しく処理を行えず実行時エラーとなります。


while(*a)を抜けた後は、*a の値はヌル文字(つまり、数値で言えば 0)になりますので、

printf("a = %s¥n", *a);

というコードは

printf("a = %s¥n", 0);

と等価になります。

ポインタで 0 と言う値は特別な意味を持っていて、俗にNULLポインタなどと呼ばれます(空のポインタ)。この場合に限っては実行時エラーとならず、空の文字列を意味する "(null)" を表示します。これは文字列の終端を意味するNULL文字のことではなく、NULLポインタであるという意味です。


なお、while(*a)内で1文字ずつ追いかけたい場合は、%sではなく%cを使用します。

printf("a = %c¥n", *a);
id:gbs01

なるほど。

良くわかりました。

ありがとうございます。

2009/03/05 20:14:24
id:Mook No.2

Mook回答回数1312ベストアンサー獲得回数3912009/03/05 01:33:07

ポイント25pt

C をやる上でポインタは最もはまりやすい部分の一つですね。

今回も printf へ渡すべきものが、ポインタなのか文字その

ものなのかを整理するとよいかと思います。


printf の %s に対して渡すのは文字列のアドレスですから、

*a(これは1文字を指します)は通常エラーになります。

*a が null においては、お使いのコンパイラがたまたま

null を表示するだけであって、これは保障されるものでは

ありません。

おそらくやりたいことは

printf("%c", *a );

のことではないかと思います。


文字を確認する場合は、

   char *aorg;
   aorg = a;

   while(*a) //文字列aのヌル文字の所まで移動する
   {
        printf("ADDRESS = %08x : 文字 = [%c]  :文字列 = %s\n", a, *a, a);
        *a++;
    }
    printf("\n");

    while(*a++ = *b++);
    //文字列aのヌル文字の所から文字列bの文字を一つずつ入れていく
    //ヌル文字の所からa++としても先のアドレスへは進まない

    return aorg;

のようにして確認してみてはどうでしょうか。

id:gbs01

なるほど。

ありがとうございます。

助かりました。

2009/03/05 20:16:55
id:AmaiSaeta No.3

天井冴太回答回数74ベストアンサー獲得回数82009/03/05 01:52:24

ポイント25pt

とりあえず、2つに分けて解説します。

●何故printf("%s",var)が実行時エラーとなるのか

*aの指している文字の文字コードをポインタの値と解釈してしまっている為です。

printf("%s",var);

という文において、varはどのような型である事が求められるでしょうか?それは、『文字列(或いはその途中)を指すポインタ(char *, const char *, char *constの何れか)』です。

質問文にあるaの型は何か、というとchar *です。では*aは? aの指している先、すなわちcharとなります。

仮に、質問文中のコード内の『printf("a = %s¥n", *a);』のコメントを外し実行した場合、1度目のループでは、*aの文字'a'の文字コード0x61が文字列を指すポインタとして扱われてしまいます(※ASCIIコードの場合)。

当然、アドレス0x61番地には(偶然でも起きない限り)意味のある文字列は格納されていません。実行時エラーが発生したとの事なので、恐らく変数用の領域でさえ無いのでしょう(もしかして、発生したエラーは"Access violation"とか、"アクセス違反"とかいう内容ではありませんか?)。

●では、何故whileループを抜けた後ではエラーが起きないのか

aが'\0'を指し、且つそれが数値の0として評価されたからだと思われます。

ループ終了時点で、aは文字列終端の'\0'を指しています。そして、'\0'の文字コードを0として扱う処理系(例: Visual C++等)があり、さらに数値0はNULLポインタとして解釈されます。gbs01さんの使用しているC処理系は恐らく、printf("%s",NULL)の評価時に"(null)"を出力する仕様なのでしょう。

●以降蛇足ながら……

'\0'が0と等価となるかどうかは処理系(『コンパイラの種類』ぐらいの意味で考えて問題ないでしょう)によります。よって、質問文にあるコードが正しく動作するか否かは『'\0'の文字コードが0か否か』に依存します。なので、このコード中にある2つのwhileの条件判断は危ういように思われます。

while(*a)  →  while(*a != '\0')

while(*a++ = *b++)   →  while((*a++ = *b++) != '\0')

とした方が、より『行儀の良い』プログラムとなるのではないかと思います。

id:gbs01

回答ありがとうございます。

助かりました。

2009/03/05 20:18:37
id:rsc96074 No.4

rsc回答回数4388ベストアンサー獲得回数4022009/03/05 03:02:38

ポイント25pt

 %sだから、アドレスのaを使います。どうしても、*aを使いたければ、%sを%cに変更しないといけません。

 それから、間違いではありませんが、普通、whileの中の*a++;→a++;

whileをぬけた後のprintf("a = %s\n", *a);→printf("a = %s\n", a);

・参考URL

http://www.bohyoh.com/CandCPP/C/Library/strcat.html

id:gbs01

なるほど。

ありがとうございます。

2009/03/05 20:20:48

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません