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) {
printf ("*t = %c (0x%02x) *s = %c (0x%02x)\n", *t, *t, *s, *s);
while ((*s++ = *t++)) {
printf ("*t = %c (0x%02x) *s = %c (0x%02x)\n", *t, *t, *s, *s);
}
}
条件が成立した時の*sと*tの内容を表示させてみましょう。
ポインタがどこを指しているのか、よくわかります。
実行結果
Before copy:
t1: Hello world,howdy
t2: to be overwritten
*t = H (0x48) *s = t (0x74)
*t = e (0x65) *s = o (0x6f)
*t = l (0x6c) *s = (0x20)
*t = l (0x6c) *s = b (0x62)
*t = o (0x6f) *s = e (0x65)
*t = (0x20) *s = (0x20)
*t = w (0x77) *s = o (0x6f)
*t = o (0x6f) *s = v (0x76)
*t = r (0x72) *s = e (0x65)
*t = l (0x6c) *s = r (0x72)
*t = d (0x64) *s = w (0x77)
*t = , (0x2c) *s = r (0x72)
*t = h (0x68) *s = i (0x69)
*t = o (0x6f) *s = t (0x74)
*t = w (0x77) *s = t (0x74)
*t = d (0x64) *s = e (0x65)
*t = y (0x79) *s = n (0x6e)
*t = (0x00) *s = (0x00)
After copy:
t1: Hello world,howdy
t2: Hello world,howdy
文字列最後の'y"をコピーした次は'\n'(0x00)がくるので、whileループが終了するという具合。
なるほどね〜。
洗練され過ぎて、カミソリのようなコード。
ちなみに、「while (*s++ = *t++)」でググってみると、たくさんの参考記事がヒットする。英語記事や中国語記事が多いような印象。
やはり、この洗練されたコードに関心をもつ人は多いということか。
(冒頭の写真は、関門橋。この後、大津まで約10時間のドライブでした。)
ここまで読んでいただき、ありがとうございました。