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;
}
書式文字列%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);
書式文字列%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);
なるほど。
良くわかりました。
ありがとうございます。
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;
のようにして確認してみてはどうでしょうか。
なるほど。
ありがとうございます。
助かりました。
とりあえず、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')
とした方が、より『行儀の良い』プログラムとなるのではないかと思います。
回答ありがとうございます。
助かりました。
%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
なるほど。
ありがとうございます。
なるほど。
良くわかりました。
ありがとうございます。