/*test1*/
main()
{
static int data[] = {1,2,3,4,5,6,7,8,9};
int i,ct = 0;
i = 2;
while(i < 8){
ct += data[i];
i++;
}
printf("%d",ct);
}

/*test2*/
main()
{
int i = 0, j =0, ct = 0;
while(i < 5)
ct = ct + (++i) + (j++);
printf("%d",ct);
}

上記のプログラムで、test1は33、test2は25という実行結果になります。私が分からないのは、test1のwhile(i < 8)ではdata[7]まで数え、test2のwhile(i < 5)ではiが5になるまで数える事です。問題集をやっていて疑問に思ったのですが解答だけが載っており説明が載っていなかったので質問しました。理解されてるかたには簡単すぎる質問ですがよろしくお願いします。

A 回答 (5件)

 単項演算子++と--には、「前置」と「後置」という使い方が存在します。



 前置とは「++i」のように変数の前につける方法で、式全体を評価する前に変数の値を変化させます。
 また、後置とは「i++」のように変数の後ろにつける方法で、式全体を評価した後に変数の値を変化させます。

 つまり、
   i = 0;
   a = ++i;
   printf("a=%d, i=%d\n", a, i);
の結果は「a=1, i=1」となり、
   i = 0;
   a = i++;
   printf("a=%d, i=%d\n", a, i);
の結果は「a=0, i=1」となります。

 ただし、1つの式の中で、1つの変数の値を2回変更するような記述をしてはいけません。つまり、
   a = (i++) * (++i);
のような式は、書いてはいけないということです。
 なぜなら、このような使い方をした場合の動作はCの仕様では定義されておらず、処理系によって結果が異なる恐れがあるからです。
(処理系に依存したプログラムは移植性に欠け、見つけにくいバグを生み出します。)
 ですから、複雑な式の中で++などを行う必要がある場合は、確実に意図した通りに実行されるよう式を分解するのが、バグを防ぐ最良の手段です。
    • good
    • 0

ANo3の値がチョッと変なので訂正



・i<8のチェック時の値   ・ctを求めるときの値
ct  i  j        ct  i  j  計(ct)
 0  0  0
               0  1  0   1
 1  1  1       
               1  2  1   4
 4  2  2
               4  3  2   9
 9  3  3
               9  4  3   16
16   4  4       
               16  5  4   25
25   5  5 <―この時のチェックでループが終わる


以上訂正でしたiとjの増えるタイミングの問題です
    • good
    • 0

まずtest1ですが


iは8までカウントされています。

プログラムの流れとして
 ・i<8のチェック
 ・ctの計算
 ・iを増やす
の繰り返しです
iが8になったときは先頭のチェックではじかれるため
ctの計算は通らなくなるので実質data[0]~data[7]の
和が作成されます


test2の時の3つの変数の変化はctを計算しているときに
ct i j
 0 0 0
 0 1 0
 1 2 1
 4 3 2
 9 4 3 *
16 5 4

iとjの増えるタイミングに注意してください
式中の ++i は式の計算よりもインクリメントが優先されます
逆に  j-- は式の計算に使われた後でインクリメントされます。

test1もtest2もiは比較してる数値まで増えているのですが
増えるタイミングによってそのように見えているのです




 
    • good
    • 0

++i



j++
では計算順序が異なるのでね
test2の
ct = ct + (++i) + (j++)
の場合
++iを計算してctに足すけど
j++はctにjの値を足してからjに値を+1してるのですよ。
だから
iの値は1,2,3,4,5と言う値をctに足し
jの値は0,1,2,3,4と言う値をctに足しているから25という答えになるのです。

test1は配列の最初の値はdata[0]に入っているからですね
足す値は3,4,5,6,7,8で33という答えです。

最近Cを使っていないの間違ってたらすまんです。
    • good
    • 0

それは一言で言ってしまうと、


「i++」と「++i」の差です。
i++は、その数値を参照してから1加えてるのに
対し、
++iは、1を加えたものを参照しているということです。
つまり、Test2のプログラムのiの参照の仕方は、
1,2,3,4,5となり、5で抜けるって感じになる
ってことです。

どうでしょうか?後参照にしたのURLを見ていただければ
違いは一目瞭然かと.

参考URL:http://www.kumei.ne.jp/c_lang/intro/no_11.htm
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aと関連する良く見られている質問

Qfor(int i = 100, long n = 1; i > n/3i; i++)

for(int i = 100, long n = 1; i > n/3i; i++)
のように、初期設定で型の違う変数を宣言したいんだけど
C++ではこんなふうに2つ以上の型を宣言してはいけないんですか?

Aベストアンサー

,

コンマ演算子の原理です。
forの初期化文で "," で区切れるのは値を返す文だけです。
よってintステートメントもlongステートメントも値を返さないので、この文では使用できません。

というか、むしろ、intステートメントの第2引数としてlongが認識されてしまいます。
外で

int i; long n;

とし

for(i = 0, n = 0; hoge; hoge)

なら可能です。

Qint i,j; \n i=0,j=5;

int i,j;
i=0;
j=5:
と書いてあるソースは普通ですが、
int i,j;
i=0,j=5:
と書いてあるソースもあります。
後者はC++の正しい書式ですか?

カンマ演算子というのは後者のカンマのことですか?

Aベストアンサー

 正しい書式です。

i=0,j=5;
 における、「,」をカンマ演算子といいます。2項の演算子です。カンマで区切られた演算を「左から順番に」実行し、最後の演算を結果として返します。
 したがって、例の文であれば、i=0を実行し、次にj=5を実行。そして、j=5の結果の5を結果として返します。
 ・・・
 が、本来的には、あまり、例のような使い方はしませんね。よく見られるのは、次のような場合です。

 for (i=0,j=0 ; i < 50 ; ++i,++j) {

 のような形でよく見られます。for文の各式は、一つの式でなければならないので、こんな書き方をするわけです。初期化と更新部が一つにまとまり、ループが読みやすくなるのが利点かな。

Qint nII[10] = { 0 }について

久々にCを使ってプログラムを組んでいるのですが、基本的な構文を思い出せず
いくつか教えていただきたく質問させていただきました。

1)配列すべてを初期化するのに、宣言時に

int nII[10] = { 0 };

で大丈夫だった(全ての要素が0で初期化)と記憶しているのですが、間違いないでしょうか?

2)構造体の初期化は

struct tm tm;
memset(&tm, 0, sizeof(struct tm))

で大丈夫でしょうか?

3)構造体の宣言は

typedef struct{
int a;
}HOGE, *LPHOGE;

HOGE st; // <- struct HOGE stと同じ
LPHOGE pst; // <- struct HOGE* pstと同じ

で問題ないでしょうか?

以上、3つ質問になって申し訳ないのですが、よろしくお願いします。

Aベストアンサー

1)OK
2)たぶんOK
3)HOGEという名前の構造体はない(当該の構造体には名前がない)ので、
// 以下のコメント記述が誤っています。ただし、

HOGE st;
LPHOGE pst;
という定義そのものはOK

Qvoid (*signal(int signum, void (*handler)(int)))(int);

の解釈を教えてください
最後の「(int)」については詳しくお願いします

Aベストアンサー

signalが

(1)1つ目の引数の型:int
(2)2つ目の引数の型:引数がintで戻り値がvoidである関数へのポインタ
(3)戻り値の型:引数がintで戻り値がvoidである関数へのポインタ(2と同じ)

を満たす関数である事を宣言しています。最後の(int)はsignalの戻り値の
関数ポインタがint型の引数を持つ事を示しています。

「引数がintで戻り値がvoidである関数へのポインタ」の型をHANDLERと表すと

HANDLER signal(int signum, HANDLER handler);

となります。

Qtry{}catch(){}とデストラクタの関係を教えてください。

try-catchでメモリ確保を含むクラスをスローした場合、デストラクタはどの時点で働くのか、教えてください。たとえば、↓の使いかたは大丈夫でしょうか?

【1】
try{
 throw(CError(100, "エラー情報"));
}catch(CError& err){
 //ここでerrを参照しても問題ないのでしょうか?
}

【2】
try{
 CError err(100, "エラー情報");
 throw(err); // (1)
}catch(CError& err){
 //ここでerrを参照しても問題ないのでしょうか?
 //まだデストラクタはちゃんと動作するのでしょうか?
 //catchが呼び出し元のメンバであったりしても大丈夫なのでしょうか?
}

宜しくお願いします。

Aベストアンサー

【1】【2】どちらの場合も問題がありません。
コンパイラが必要に応じてerrオブジェクトのコピーを作成します。
デストラクタが呼び出されるタイミングはコンパイラに依存するところもあると思いますが、
例えばVC7.1では【2】は以下のように動作します。
(1) errオブジェクトのコンストラクタが呼び出される
(2) CErrorクラスのテンポラリオブジェクト(以下a)のコピーコンストラクタが呼び出される。
(3) errオブジェクトのデストラクタが呼び出される
(4) catch文まで到達
(5) aオブジェクトのデストラクタが呼び出される。

VC7.1では、【1】は以下のように動作します。
(1) errオブジェクトのコンストラクタが呼び出される
(2) catch文まで到達
(3) errオブジェクトのデストラクタが呼び出される。

コンパイラがオブジェクトのコピーを省略しているようです。


このカテゴリの人気Q&Aランキング

おすすめ情報