i++ 及び ++i について

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
御津凪

i++ 及び ++i について

#1

投稿記事 by 御津凪 » 17年前

今まで気になっていたことを質問したいと思います。
(内容は雑談的なものなので雑談で立てさせていただきます)

よく、
for(i = 0;i < 100;i++){
	// ループ処理
}
のように、入門書などでは、 i++ がほとんどですが、
for(i = 0;i < 100;++i){
	// ループ処理
}
のような記述はほとんど見られません。(処理の結果は同じ)
ちなみに、
i++ → i を渡してから i に +1
++i → i を +1 してから i を渡す
という意味があります。
(これでわかるでしょうか…)

他の言語では ++i を使えないものもあるようなので、使われていないようなのですが、
皆様は ++i を使っているのでしょうか?

私は両方使っています。

あと、VC++などの新しいコンパイラでは、最適化により
i++ と ++i との速度の差はなくなるようです。
BCC は分かりませんが。

ちなみに、私は専門学校へ行かず(高校卒業まで)、独学でプログラミングを学び、
未成年でゲーム会社入りしました。
なので、専門学校などの授業を知らないので、上記のような質問をさせていただきました。

GPGA

Re:i++ 及び ++i について

#2

投稿記事 by GPGA » 17年前

私が専門学生時代のときはi++で教えられていました。
後置インクリメントにする理由などは、特には言っていませんでした。

今は必要がある場合以外は、すべて前置インクリメントを使用しています。
単独で使用するDATA型の場合、最適化により前置、後置の処理速度は変化しないと思いますが
イテレータやoperator++(int)などの場合、最適化が効きにくく
後置インクリメントのほうが遅くなってしまうので、前置インクリメントで統一するようにしています。

たかぎ

Re:i++ 及び ++i について

#3

投稿記事 by たかぎ » 17年前

どちらにするか迷うのであれば、++iにしておく方が無難です。
理由は、すでに回答が出ていますが、++演算子を多重定義した場合にはi++は効率が悪く、思わぬ副作用もあるからです。
以下に具体的に示します。

例えば、次のようなクラスを考えます。
class counter
{
  unsigned int value_;
public:
  explicit counter(unsigned int value) : value_(value) {}
  unsigned int get() const { return value_; }
  counter& opertator++();
  counter operator++(int);
};
二つの++演算子の実装は次のようになるはずです。
counter& counter::operator()
{
  ++value_;
  return *this;
}

counter counter::operator++(int)
{
  counter temp(*this);
  ++value_;
  return temp;
}
これを見れば分かるように、明らかに後置型の方が効率が悪くなります。

また、上の例では問題ありませんが、コピーコンストラクタが例外を送出する可能性がある場合を考えてみてください。
後置型の++では、最大2箇所でコピーコンストラクタが呼び出されますので、その都度例外が送出される可能性があります。例外が送出されるということは、それだけ実行パスが複雑になることを意味します。
効率の観点からも、複雑さを軽減する観点からも、例外安全の観点からも、理由がなければ++iにする方がよいでしょう。

ただし、(C++ではなく)Cの場合はi++の方が多く使われているのも事実です。
ですから、オペランドが組み込み型であることがはっきりしているのであれば、i++としても特に問題はありません。

御津凪

Re:i++ 及び ++i について

#4

投稿記事 by 御津凪 » 17年前

GPGAさん、たかぎさん、回答ありがとうございます。

そういえば、イテレータや演算子のオーバーライドについては質問時に考えていませんでした。
確かに、C++ではそこで処理速度に差が出ますよね。

私はC++のイテレータや演算子のオーバーライド(インクリメント・デクリメント)は余り使用していなかったので、
上記の回答は大変参考になりました。

ちなみに、この質問は疑問として思ったことなので、別に悩んでいるわけではありません。


ところで、私は両方使うと書きましたが、以下のように使っています。
(Cでの処理が思いつかなかったのでC++で適当に書きました)
class CharStack{
	unsigned int _count;
	char _str[256];
public:
	CharStack( void ){
		_count = 0;
		_str[0] = '\0';
	}
	CharStack( const char* str ){
		_count = strlen(str);
		strcpy(_str,str);
	}

	void push( char c );
	char pop( void );

	const char* str( void )const{return _str;}
};
void CharStack::push( char c ){
	_str[_count++] = c;
	_str[_count] = '\0';
}
char CharStack::pop( void ){
	char c = _str[--_count];
	_str[_count] = '\0';
	return c;
}
前置・後置の使い方を示したクラスなので、バッファオーバーランとかは考えてません。
初心者の方は多分混乱するような書き方だと思いますが、
このように書いている方ってどれくらいいるんでしょうか。
(リファクタリングの話になるかも)

Cではなんで i++ が多く使われているんでしょうかね?
初心者の方々にも聞いてみたいなぁと思ったり。

KEV

Re:i++ 及び ++i について

#5

投稿記事 by KEV » 17年前

i++を常時使ってる初心者です。
多く使われている理由をちらっと考えてみたんですが、

1.入門書のほとんどがi++で書いてる
 これがまぁ一番の理由だと思います。

2.i += 1 と同義なので、見た目似てるi++のほうが覚えやすい
 これは個人的な理由です。
 i++でも++iでもどっちでもいいよ、と言われたからi++を覚えてしまった記憶が。

3.C++がCの延長上にあると思ってる
 CでいけるんだしC++で同じことしても大丈夫だろうと思っている(実は自分も思ってた)。
 皆さんのおっしゃってる実装内容みたらi++で書く気がなくなりますね…。

++iの方がいいよーと言うのは前から聞いてたのですが、
はっきりとした理由が分かってとてもいい勉強になりました。
いい機会だしこちらに乗り換えてみます。

たかぎ

Re:i++ 及び ++i について

#6

投稿記事 by たかぎ » 17年前

> そういえば、イテレータや演算子のオーバーライドについては質問時に考えていませんでした。

× オーバーライド
○ オーバーロード

です。
紛らわしいので、私はJIS X3014の訳語を用いて「多重定義」と呼ぶことにしています。

御津凪

Re:i++ 及び ++i について

#7

投稿記事 by 御津凪 » 17年前

> × オーバーライド
> ○ オーバーロード
>
> です。
> 紛らわしいので、私はJIS X3014の訳語を用いて「多重定義」と呼ぶことにしています。

たかぎさん、ご指摘ありがとうございます。
独学のため、曖昧で覚えていたようで…。
私も多重定義と呼ぶことにします。

>KEVさん

> 1.入門書のほとんどがi++で書いてる
確かに入門書は i++ がほとんどですね。
知っている限りでは ++i の方は見かけないです。
変わりに専門書では見かけますが。

> 2.i += 1 と同義なので、見た目似てるi++のほうが覚えやすい
確かに私も覚えやすいなと思います。

そういえば、 i += 1 と i++ では、 i++ の方が処理が速いらしいです。

> 3.C++がCの延長上にあると思ってる
C++ は C をオブジェクト指向に書けるようにした言語なので、
C++ は C の延長上にあると私は思っています。
オブジェクト指向を擬似的に C で表現できますし。

> ++iの方がいいよーと言うのは前から聞いてたのですが、
> はっきりとした理由が分かってとてもいい勉強になりました。
> いい機会だしこちらに乗り換えてみます。

実はそういうことを気づかせるための雑談スレだったり…(笑

i++ や ++i は一行だけに書くと処理結果はまったく同じなので、
分からないままの方がいるのだと思います。

戻り値や式の中に入れた場合に処理が変わるので。

たかぎ

Re:i++ 及び ++i について

#8

投稿記事 by たかぎ » 17年前

> そういえば、 i += 1 と i++ では、 i++ の方が処理が速いらしいです。

全く最適化が働かず、インクリメント命令が加算命令とは別に用意されているような環境であれば、iの型次第ではそうなる可能性はあります。
ただし、それぐらい最適化がきかない環境だと、++iとi++では実行速度が変わる可能性があります。

> C++ は C をオブジェクト指向に書けるようにした言語なので、

これはちょっと事実誤認かもしれませんね。

Hermit

Re:i++ 及び ++i について

#9

投稿記事 by Hermit » 17年前

今回のような for(i = 0;i < 100;i++) だと、あまり変わらないとは思いますが、
MC68000 では、アセンブラで
(An)+ や、-(An) などのアドレスレジスタ間接モードがありましたが、
+(An) と、(An)- は無いので、
使うときは、i++ か、--i を使う癖がついてましたね。

私の場合は、それが一番大きい。(単なる癖)

J

Re:i++ 及び ++i について

#10

投稿記事 by J » 17年前

こんにちは。

工業系の高校に通っていますが、
インクリメントはやっぱりi++で習いましたね。
独学時代の入門書もやっぱりi++でした。

KEVさんの2に同意です。後置の方が我々の日常感覚にあってますしね。

このスレに来て初めて前置の方が効率が良いと知りました。
どんなときでも前置の方がよろしいのでしょうか?
やっぱり、例外もあるのですか?<!--2

situmon

画像表示

#11

投稿記事 by situmon » 17年前

前は棒人間の画像しか作れませんでしたが時間が余ったときに
新しく画像を作成しました。
(32×64)
今度はきちんと作成しました。
#include "DxLib.h"

typedef struct{
        int x,y,img,muki,walking_flag;
}ch_t;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        
        int image[16];
        char Key[256];
        ch_t ch;

    if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //ウィンドウ化と初期化処理

        ch.x    =320;
        ch.y    =160;
        ch.walking_flag=0;
        ch.muki=3;

        SetDrawScreen( DX_SCREEN_BACK ) ;                                                 //描画先を裏画面に設定
        LoadDivGraph( "char.png" , 16 , 4 , 4 , 32 , 64 , image ) ;//画像を分割してimage配列に保存

    while(!ProcessMessage() && !ClearDrawScreen() && !GetHitKeyStateAll( Key ) && !Key[KEY_INPUT_ESCAPE]){
            //↑メッセージ処理        ↑画面をクリア         ↑キーボード入力状態取得       ↑ESCが押されると終了

        if(ch.x%32==0 && ch.y%32==0){         //座標が32で割り切れたら入力可能
            ch.walking_flag=1;         //歩くフラグを立てる。
            if     ( Key[ KEY_INPUT_UP   ]  == 1 )  //上ボタンが押されたら
                    ch.muki=0;         //上向きフラグを立てる
            else if( Key[ KEY_INPUT_LEFT ]  == 1 )  //左ボタンが押されたら
                    ch.muki=1;         //左向きフラグを立てる
            else if( Key[ KEY_INPUT_DOWN ]  == 1 )  //下ボタンが押されたら
                    ch.muki=2;         //下向きフラグを立てる
            else if( Key[ KEY_INPUT_RIGHT]  == 1 )  //右ボタンが押されたら
                    ch.muki=3;         //右向きフラグを立てる
            else                                    //何のボタンも押されてなかったら
                    ch.walking_flag=0; //歩かないフラグを立てる
        }

        if(ch.walking_flag==1){        //歩くフラグが立っていたら
            if     (ch.muki==0)        //上向きならch.y座標を減らす
                    ch.y--;
            else if(ch.muki==1)        //左向きならch.x座標を減らす
                    ch.x--;
            else if(ch.muki==2)        //下向きならch.y座標を増やす
                    ch.y++;
            else if(ch.muki==3)        //右向きならch.x座標を増やす
                    ch.x++;
        }

        ch.img=image[(ch.x%32+ch.y%64)/8 + ch.muki*4];            //画像をセット

        DrawGraph( ch.x , ch.y , ch.img , TRUE ) ;//画像を描画

        ScreenFlip();
    }

    DxLib_End();
    return 0;
}
で表示させたいのですが表示できませんどうすればいいでしょう。

kazuoni

Re:画像表示

#12

投稿記事 by kazuoni » 17年前

ぱっと見たところ、ソース自体に特に問題はないと思います。あと考えられるのは画像の保存場所だと思うのですが・・・ゲームプログラミングの館の通りに進んでいるのならば、http://dixq.net/g/#1
の通りに保存していますか?

KEV

Re:画像表示

#13

投稿記事 by KEV » 17年前

添付の画像ファイルをダウンロードしたんですが、
大きさが(88×175)になってます。

きっとsitumonさんはこれを4×4に分割したいのだと思うので、
その場合は 横22×縦44に分割するのがいいと思います。
上のプログラムでは(32×64)で分割しようとしているので、
画像読み込みでエラーが出ました。

>>LoadDivGraph( "char.png" , 16 , 4 , 4 , 22 , 44 , image ) ;//画像を分割してimage配列に保存

こう書き換えたら上記のプログラムで十字キーを押したときにキャラが描写されました。

ただ、現時点では
>>if(ch.x%32==0 && ch.y%32==0){ //座標が32で割り切れたら入力可能
で1マスずつが32と指定しているのに対して、
>>ch.img=image[(ch.x%32+ch.y%64)/8 + ch.muki*4]; //画像をセット
で上に行くときは64で割っているため、画像が正常に描写されていないです。

とりあえず、
>>ch.img=image[(ch.x%32+ch.y%32)/8 + ch.muki*4]; //画像をセット
こう書き換えたらそれなりにちゃんと動きました。

しかしまだ上下左右の移動のときの画像がちゃんとあってなさそうだったり、
上下移動量を64にした場合、初期値の(ch.y=160)が64で割れないため
画像が表示されなかったり、まだ変更できる箇所はありそうなので
situmonさんの意図する動作をするように色々と修正していってみてください。

situmon

Re:画像表示

#14

投稿記事 by situmon » 17年前

すいません遅くなりました。
画像のファイルはやはり、128×256ですよ。

管理人

Re:画像表示

#15

投稿記事 by 管理人 » 17年前

画像の保存場所も画像の分割も恐らくあっていると思います。
画像サイズも仰る通りであっています。
間違っているのは画像番号の計算式です。
(ch.x%32+ch.y%64)/8 + ch.muki*4

これですね。まず、何故64で割っているのでしょう?32にすれば直ると思いますが、一応下へ続きを。

最大でいくらになるか考えて見ましょう。
ch.y%64は最大で63だから63/8は7、ch.mukiが3の時は3*4は12となり、19番が示される場合があります。
しかし画像番号は15までしかないはずです。

ch.x++;
}
ch.img=image[(ch.x%32+ch.y%64)/8 + ch.muki*4];            //画像をセット
DrawGraph( ch.x , ch.y , ch.img , TRUE ) ;//画像を描画

と書かれている部分を

ch.x++;
}
int num=(ch.x%32+ch.y%64)/8 + ch.muki*4;
DrawFormatString(0,0,GetColor(255,255,255),"%d",num);
ch.img=image[num];            //画像をセット
DrawGraph( ch.x , ch.y , ch.img , TRUE ) ;//画像を描画
と変更して、どのように画像番号が変更されているか確認してみて下さい。

lbfuvab

Re:画像表示

#16

投稿記事 by lbfuvab » 17年前

プログラム中に使ってる数値((ch.x%32+ch.y%64)/8やch.muki*4)を表示して確かめたらいいと思います。

管理人

Re:画像表示

#17

投稿記事 by 管理人 » 17年前

後、進行方向と画像番号があってないのはいいんでしょうか?

上、左、下、右という向き番号になっているのに対して画像が
正面、右、左、正面となっているので。
とりあえず、ちゃんと動く事がわかったら修正されるのだと思いますが、念の為に。

また、画像の中心がずれているので、スムーズに動いているように見えないと思います。
モーションを作る時、例えば同じような画像を4つ並べるのは難しいと思いますから、
そんなときは、一枚書いたら四方一番はしっこの1ピクセル何か違う色でマークをかいて
画像の端っこがわかるようにしておいて、4つ並べたりした方が解り易いと思いますよb

situmon

Re:画像表示

#18

投稿記事 by situmon » 17年前

ではこういうことですか
#include "DxLib.h"

typedef struct{
        int x,y,img,muki,walking_flag;
}ch_t;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        
        int image[16];
        char Key[256];
        ch_t ch;

    if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //ウィンドウ化と初期化処理

        ch.x    =320;
        ch.y    =160;
        ch.walking_flag=0;
        ch.muki=3;

        SetDrawScreen( DX_SCREEN_BACK ) ;                                                 //描画先を裏画面に設定
        LoadDivGraph( "char.png" , 16 , 4 , 4 , 32 , 64 , image ) ;//画像を分割してimage配列に保存

    while(!ProcessMessage() && !ClearDrawScreen() && !GetHitKeyStateAll( Key ) && !Key[KEY_INPUT_ESCAPE]){
            //↑メッセージ処理        ↑画面をクリア         ↑キーボード入力状態取得       ↑ESCが押されると終了

        if(ch.x%32==0 && ch.y%32==0){         //座標が32で割り切れたら入力可能
            ch.walking_flag=1;         //歩くフラグを立てる。
            if     ( Key[ KEY_INPUT_UP   ]  == 1 )  //上ボタンが押されたら
                    ch.muki=0;         //上向きフラグを立てる
            else if( Key[ KEY_INPUT_LEFT ]  == 1 )  //左ボタンが押されたら
                    ch.muki=1;         //左向きフラグを立てる
            else if( Key[ KEY_INPUT_DOWN ]  == 1 )  //下ボタンが押されたら
                    ch.muki=2;         //下向きフラグを立てる
            else if( Key[ KEY_INPUT_RIGHT]  == 1 )  //右ボタンが押されたら
                    ch.muki=3;         //右向きフラグを立てる
            else                                    //何のボタンも押されてなかったら
                    ch.walking_flag=0; //歩かないフラグを立てる
        }

        if(ch.walking_flag==1){        //歩くフラグが立っていたら
            if     (ch.muki==0)        //上向きならch.y座標を減らす
                    ch.y--;
            else if(ch.muki==1)        //左向きならch.x座標を減らす
                    ch.x--;
            else if(ch.muki==2)        //下向きならch.y座標を増やす
                    ch.y++;
            else if(ch.muki==3)        //右向きならch.x座標を増やす
                    ch.x++;
}
        int num=(ch.x%32+ch.y%32)/8 + ch.muki*4;

        DrawFormatString(0,0,GetColor(255,255,255),"%d",num);

        ch.img=image[num];            //画像をセット

        DrawGraph( ch.x , ch.y , ch.img , TRUE ) ;//画像を描画

        ScreenFlip();
    }

    DxLib_End();
    return 0;
}

situmon

Re:画像表示

#19

投稿記事 by situmon » 17年前

やはりできません。
22掛ける44ならできますが。

lbfuvab

Re:画像表示

#20

投稿記事 by lbfuvab » 17年前

まず、
DrawFormatString(0,0,GetColor(255,255,255),"%d",num);
としていますがその値は正しいですか?

管理人

Re:画像表示

#21

投稿記事 by 管理人 » 17年前

こちらでは正常に表示されていますよ。
画像を保存する場所が違うのではないでしょうか?

LoadDivGraphで返ってきた値をprintfDxなどで表示してみて下さい。
-1が表示されたら保存場所が違います。

situmon

Re:画像表示

#22

投稿記事 by situmon » 17年前

できました!!ありがとうございます。
でもキャラが早く動きません。
早くするにはどうすれば?
それとも処理落ちですか?

管理人

Re:画像表示

#23

投稿記事 by 管理人 » 17年前

移動計算部分の計算式を変えたらいいですよ。
例えば
ch.x--;

ch.x-=2;
にすれば2倍のスピードになりますし、
ch.x-=4;
にすれば4倍のスピードになります。
ここで注意しないといけないのが、32で割り切れる場所を通った時を1単位にして処理していますので、
ch.x-=3;
などの32の倍数を通らない数で加算する時は何か個別の処理をして下さい。

閉鎖

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