見出し画像

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

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

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

やってみた

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