C言語の後置インクリメントについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ハワルド
記事: 16
登録日時: 9年前

C言語の後置インクリメントについて

#1

投稿記事 by ハワルド » 8年前

現在、2007年に出版された本を使ってC言語を学習しているのですが、本に載っているコードが本に書いてあるとおりの結果にならなかったので、原因を教えて下さい。書いてあるコードは

コード:

#include <ctype.h>
#include <stdio.h>

int main(void)
{
	char str[80], *p;

	printf("文字列を入力してください: ");
	gets(str);
	p = str;

	while(*p)
		*p++ = toupper(*p);

	printf("%s\n", str);

	return 0;
}
というものです。
本に書いてあるとおりに動かないのは、

コード:

while(*p)
	*p++ = toupper(*p);
です。
本には上記の書き方で

コード:

while(*p){
	*p = toupper(*p);
	p++;
}
と同じような動きをすると書いてあるのですが、実行してみると、一番最初の文字がなくなっています。
これは2007年時点では本の通りに動いていたが現在では仕様が変わって本のとおりには動かなくなっているということなのですか?
そうだとしたら、どのように仕様が変わったのでしょうか?

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C言語の後置インクリメントについて

#2

投稿記事 by みけCAT » 8年前

未定義動作なので、どんな結果になってもおかしくないですね。
1999年の規格であるC99に近いN1256を見てみます。

N1256 4. Conformanceより引用
2 If a ‘‘shall’’ or ‘‘shall not’’ requirement that appears outside of a constraint is violated, the
behavior is undefined. Undefined behavior is otherwise indicated in this International
Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition
of behavior. There is no difference in emphasis among these three; they all describe
‘‘behavior that is undefined’’.
N1256 6.5 Expressionsより引用
2 Between the previous and next sequence point an object shall have its stored value
modified at most once by the evaluation of an expression. 72) Furthermore, the prior v alue
shall be read only to determine the value to be stored. 73)
73) This paragraph renders undefined statement expressions such as
i = ++i + 1;
a[i++] = i;
while allowing
i = i + 1;
a = i;

今回の式

コード:

*p++ = toupper(*p);
においては、
pの値が書換えられ、かつpの値がpの新しい値の決定以外(更新前のpが指す場所に書き込む値の計算)に使われ、
その計算はpの更新の前に行われるか後に行われるかが決定できないので、
shallと書かれた条件に違反し、未定義動作になります。
すなわち、本が間違っているということになるでしょう。

参考 これに相当するルールは、2011年の規格であるC11に近いN1570ではわかりやすくなっています。

N1570 6.5 Expressionsより引用
2 If a side effect on a scalar object is unsequenced relative to either a different side effect
on the same scalar object or a value computation using the value of the same scalar
object, the behavior is undefined. If there are multiple allowable orderings of the
subexpressions of an expression, the behavior is undefined if such an unsequenced side
effect occurs in any of the orderings. 84)
3 The grouping of operators and operands is indicated by the syntax. 85) Except as specified
later, side effects and value computations of subexpressions are unsequenced. 86)
代入演算子=の左辺と右辺のどっちが先に評価されるかは決まっていないので、
左辺に含まれるpへの副作用p++と、右辺に含まれるpの値を用いた値の計算*pの順番が定まらず、未定義動作になります。
最後に編集したユーザー みけCAT on 2016年9月29日(木) 11:56 [ 編集 1 回目 ]
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

ハワルド
記事: 16
登録日時: 9年前

Re: C言語の後置インクリメントについて

#3

投稿記事 by ハワルド » 8年前

解答ありがとうございました。
2007年時点ではtoupper(*p)の計算が、pが増える前に行われるか、増えない前に行われるか決定できないので未定義動作となるということで本が間違っているということが分かりました。
現在では = の右辺と左辺の評価の順番が決定していないので未定義動作となるということもわかり、解決しました。

閉鎖

“C言語何でも質問掲示板” へ戻る