見出し画像

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時間のドライブでした。)

ここまで読んでいただき、ありがとうございました。

いいなと思ったら応援しよう!

Akio van der Meer
これまでの収益は全て、それを必要としておられる方々へ、支援機関を通して寄付させていただきました。この活動は今後も継続したいと思っています。引き続きよろしくお願いいたします。

この記事が参加している募集