while (*s++ = *t++) ;
Ayumiさんの記事で見かけた、K&Rのテキストでのc言語表現。
この一文で、配列tの内容を配列sにコピーするらしい。
whileの条件式のところには、代入式が定義されている、、、
ええっ!どゆこと!
わからないときはまず、実際に動かしてみるに限る
#include <stdio.h>
void testcpy (char *s, char *t) {
while (*s++ = *t++);
}
int main() {
char t1[] = "Hello world,howdy";
char t2[] = "to be overwritten";
printf ("Before copy:\n");
printf (" t1: %s\n", t1);
printf (" t2: %s\n\n", t2);
testcpy (t2, t1);
printf ("After copy:\n");
printf (" t1: %s\n", t1);
printf (" t2: %s\n\n", t2);
return 0;
}
標準関数であるstrcpy()関数の中味として、while (*s++ = *t++); が定義されているらしいのだが、ここでは、testcpy()関数として定義してみた。
実行環境は、cインタプリタ
実行結果
Before copy:
t1: Hello world,howdy
t2: to be overwritten
After copy:
t1: Hello world,howdy
t2: Hello world,howdy
t2に、t1の内容がコピーされました。
gccで警告メッセージがでた
ブラウザのcインタプリタ環境では、特に問題なく動いたけど、gccでコンパイルしたら、下記のような警告メッセージが出てきた。
jm3nrhMac-mini-:c akio$ gcc 20240511.c
20240511.c:4:17: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
while (*s++ = *t++);
~~~~~^~~~~~
20240511.c:4:17: note: place parentheses around the assignment to silence this warning
while (*s++ = *t++);
^
( )
20240511.c:4:17: note: use '==' to turn this assignment into an equality comparison
while (*s++ = *t++);
^
==
1 warning generated.
jm3nrhMac-mini-:c akio$
「代入式の結果を制御条件に括弧をつけずに使っているよ」「このワーニングを黙らせるには、whileの条件式のところを括弧で囲みなさい」「代入式を条件式にかえるなら==を使いなさい」といった内容。
改善案を提案してくれるなんて、とても親切。
提案された通り、whileの条件式のところを括弧で囲んでみた
void testcpy (char *s, char *t) {
while ((*s++ = *t++));
}
括弧をつけることで優先順位を明確にして、まずは代入式を実行し、その結果で制御条件式を判断するということ、、、ですかね。
gccで、警告メッセージが出なくなった
jm3nrhMac-mini-:c akio$ gcc 20240511.c
jm3nrhMac-mini-:c akio$
エラーもワーニングも出ないと、嬉しい。
同じ結果を得られた
jm3nrhMac-mini-:c akio$ ./a.out
Before copy:
t1: Hello world,howdy
t2: to be overwritten
After copy:
t1: Hello world,howdy
t2: Hello world,howdy
jm3nrhMac-mini-:c akio$
cインタプリタの実行結果と同じです。
もう少し、振る舞いを細かくみてみよう
void testcpy (char *s, char *t) {
while ((*s++ = *t++)) {
printf ("*t = %c, *s = %c\n", *t, *s);
}
}
条件が成立した時の*sと*tの内容を表示させてみましょう。
ポインタがどこを指しているのか、よくわかります。
実行結果
Before copy:
t1: Hello world,howdy
t2: to be overwritten
*t = e, *s = o
*t = l, *s =
*t = l, *s = b
*t = o, *s = e
*t = , *s =
*t = w, *s = o
*t = o, *s = v
*t = r, *s = e
*t = l, *s = r
*t = d, *s = w
*t = ,, *s = r
*t = h, *s = i
*t = o, *s = t
*t = w, *s = t
*t = d, *s = e
*t = y, *s = n
*t = , *s =
After copy:
t1: Hello world,howdy
t2: Hello world,howdy
最初の文字'H'を*sに代入して、*sも*tもインクリメントしたので、最初のprintf文を実行する時には*tは次の文字である'e'、*sは’o'を表示している。
文字列最後の'y"をコピーした次は'\n'(NILL)がくるので、whileループが終了するという具合。
なるほどね〜。
洗練され過ぎて、カミソリのようなコード。
ちなみに、「while (*s++ = *t++)」でググってみると、たくさんの参考記事がヒットする。英語記事や中国語記事が多いような印象。
やはり、この洗練されたコードに関心をもつ人は多いということか。
(冒頭の写真は、関門橋。この後、大津まで約10時間のドライブでした。)
ここまで読んでいただき、ありがとうございました。
これまでの収益は全て、それを必要としておられる方々へ、支援機関を通して寄付させていただきました。この活動は今後も継続したいと思っています。引き続きよろしくお願いいたします。