質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.86%

  • C

    3321questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • プログラミング言語

    643questions

    プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

if文を使うべきか否か

解決済

回答 10

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 804

namnium1125

score 1745

既出な内容でしたらすみません。特に過去ログは調べていません。

文字配列の中身を比較するコード(それぞれの要素で等しければ+1、等しくなければ何もせず)の中で、

以下のようなC言語のコードを見かけたのですが、

while (*s && *t){
  count += (*s++ == *t++);
}

2行目でif文を使わないのは、実行速度を速くするためなのでしょうか?

仮にそうだとしても、等価演算子は1を返すためのものではないですから、実行速度を犠牲にしてでも、

if(*s++ == *t++) count++;

と書いた方がいいような気がします。

皆様はどう思われますか?

等価演算子で1を返す言語はC言語ぐらいといえばそうかもしれませんが、説明に他の言語を絡めても構いません。

なんていうか、くだらない質問ですみません…皆様のご意見を伺いたいです。よろしくお願いします。m(_ _)m

 追記

皆さま回答ありがとうございます。m(_ _)m

raccy様からいただいた検証コードを試してみましたので、この場を借りて掲載させてください。(2回分です。)

                       user     system      total        real
count_eq           0.000000   0.000000   5.060000 (  5.073679)
count_if           0.000000   0.000000   5.040000 (  5.045016)
count_if_after     0.000000   0.000000   5.160000 (  5.170517)
count_if_null      0.000000   0.000000   5.130000 (  5.135678)
count_r_eq         0.000000   0.000000   5.200000 (  5.206062)
count_r_if         0.000000   0.010000   5.160000 (  5.151430)
count_r_if_after   0.000000   0.000000   5.090000 (  5.103209)
count_r_if_null    0.000000   0.000000   5.080000 (  5.088278)
result -> line: 10000, total count: 204632007

                       user     system      total        real
count_eq           0.000000   0.000000   5.140000 (  5.150710)
count_if           0.000000   0.000000   5.040000 (  5.050327)
count_if_after     0.000000   0.000000   5.100000 (  5.104275)
count_if_null      0.000000   0.000000   5.190000 (  5.203628)
count_r_eq         0.000000   0.000000   5.110000 (  5.111663)
count_r_if         0.000000   0.010000   5.150000 (  5.137537)
count_r_if_after   0.000000   0.000000   5.100000 (  5.118537)
count_r_if_null    0.000000   0.000000   5.080000 (  5.077878)


パッとみた感じではifの方が速く見える部分もあります。

環境

  • MacBook Air (13-inch)
  • Intel Core i5
  • macOS High Sierra (10.13.1)
  • gcc 4.2.1
  • ruby 2.3.3p222 (2016-11-21 revision 56859) [universal.x86_64-darwin17]

皆様回答ありがとうございました。

BA悩みましたが、検証コードまで書いてくださったraccy様をBAとさせていただきます。

改めてありがとうございました。m(_ _)m

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 10

checkベストアンサー

+10

パフォーマンスは推測より実測です。

ということで、パフォーマンスを図るためのコード類を作りました。長くなったので下記のgistにあげています。

https://gist.github.com/raccy/c7a7cdd9ff61f43768705076a5a737ca

RubyとGCCがある環境でruby count_bench.rbと実行すれば、コンパイルして、テスト用のテキストを作って、速度を測ってくれます。

うちの環境の結果

$ ruby count_bench.rb
                       user     system      total        real
count_eq           0.000000   0.000000   0.000000 (  6.268309)
count_if           0.000000   0.000000   0.000000 (  6.683875)
count_if_after     0.000000   0.000000   0.000000 (  6.206465)
count_if_null      0.000000   0.000000   0.000000 (  6.046037)
count_r_eq         0.000000   0.016000   0.016000 (  6.874686)
count_r_if         0.000000   0.000000   0.000000 (  6.486357)
count_r_if_after   0.000000   0.000000   0.000000 (  5.993386)
count_r_if_null    0.000000   0.000000   0.000000 (  6.013234)
result -> line: 10000, total count: 204655626

モバイル向けCPUであるCore i7-7Y75なのでブレが大きくなるときがありますが、ほぼほぼ一緒です。ただ、これは内の環境であって、異なるCPU、異なるコンパイラなら結果が違ってくることもあり得ます。コードとして処理量が必ず多いとか、全体の計算量が異なるアルゴリズムであると言った場合を除き、実際に計らないことにはこっちのほうが必ず速くなるなどと言う話にはならないと私は思っていますし、計った場合もその環境での結果であって、別の環境でも必ずしも同じとは限らないですし、逆の結果になる環境もあり得ると思います。

ということで、パフォーマンスの話をするのであれば、まず計れ、話はそれからだと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/27 11:11

    こんなに凄い検証をわざわざありがとうございます!!m(_ _)m

    (ruby触ったことないんですけど、今回のコードからいろいろと学ばせていただきました。。)

    論より証拠(ただし環境依存)なんだなと強く感じました。

    raccy様の環境では(*ristrictだと)ifの方が速いんですね、私の方は現在手元にPCがないのでまだ検証できてないのですが、

    ifの方が遅いと誤解していました。。

    ただ、だからと言って、例え私が使う全ての環境でifを使った方が速かったとしても、やっぱり「ifが絶対」ではないんじゃないかなと、今回の回答全体を見て思います。

    というのは生成される機械語の内容や、(人によっては)分岐を挟まないことによる簡潔さが生まれることなど、速さ以外の面でも、「こっちが良い」と断定できるほどのものがないように感じたからです。(なんか偉そうにすみません。。あくまでも率直な感想です。)

    それでも具体的にどう考えてきたかがわかったので、とても勉強になりました。

    改めて回答ありがとうございます。m(_ _)m

    キャンセル

+5

if(*s++ == *t++) count++;

の方が読みやすいですね。
こう書きたいです。
モダンな言語は(無理やりキャストなどしない限り)「敢えて」こういう書き方しかできないようになっています。

C 言語は TRUE == 1 なので真の値は 1 をとることが多くこういう書き方もできますが、どの関数でも必ず真の値として 1 を返すとは限らないためバグの原因となります。
if 文を使った方がいいと思います。

追記

実際にソースがどうコンパイルされるかはコンパイラによっても違いますし、そのコードが走るプラットフォームによっても最適化の方法は違います。
特定のプラットフォーム、特定のコンパイラに最適化したコーディングをすることは確かにありますが、それはコーディングの基本ではなく特定の場面に特化したテクニックの一つだと思っています。

GCC でコンパイルしてパイプラインの有効な CPU で動かすことを前提にしたコーディングは、場面に合わせて行う様々な最適化の一つです。優先して行うべきものではなく、成果物が現場の要件に合わなかった時(問題があり、それで問題が解決する時)に調整として行うものだというのが私の考えです。
異論はあると思います。

もっと言えば、実装の隠蔽によって複数のプラットフォームにソースを使いまわせるのは高レベル言語の長所の一つだと思っていますので、低レベルな処理を意識的に行うならばその部分にインラインアセンブラを使うのが好ましいと思っています。そうすればコンパイラを変えても大丈夫です。

あるいはコメントとして残しておくべきでしょう。そうすれば後任による書き換えを防ぐこともできます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/25 21:54

    今時のコンパイラであれば、どちらで書いても最適化されればほぼ同じ機械語となって、実行時間の有意差はないと思います。

    キャンセル

  • 2017/12/25 21:59

    そうですね。
    関数の戻り値であれば違う機械語になるでしょうが、この演算であればおそらく同じコードにコンパイルされて実行時間は変わらないでしょうね。
    しかし仮に遅くなることがあるとしても真偽値を足したり引いたりするのではなく if 文を使いたいです。

    キャンセル

  • 2017/12/25 22:12

    やっぱり可読性や意味の方が大切ですよね、、

    回答ありがとうございます。m(_ _)m

    キャンセル

  • 2017/12/26 11:42

    追記ありがとうございます。

    では他の回答でも議論になってはいますが、やっぱり処理系が不明で(または複数の処理系を想定していて)特に問題がない場合は高級言語のようにifを使って書くべき、ということですよね?

    キャンセル

  • 2017/12/26 11:49 編集

    人によって色々と書き方があると思いますが、私は真を 1 と決めつけない方がいいと思います。
    この場合は `==` なので 1 と見なして問題なくても、次にソースをいじる人がそこまではっきりと熟知していたり体調が良かったりするとは限りませんから。
    真値として -1 を返す関数はあり得ます。
    クリティカルな場面ではきちんとコメントを残し、そうでない場面では可能な限り真値と 1 は区別したいと思っています。

    キャンセル

  • 2017/12/26 13:49

    返信ありがとうございます。

    例えば「配列の添字と区別するため」という意味で1を返さない関数があるならば、納得できますね。
    (Zuishin様の仰る関数はもっと別種のものかもしれませんけど)

    イメージできました。ありがとうございます。

    キャンセル

+3

if文でない書き方はアリです。まず、一行に、簡潔に書けることがメリットです。if文で書く場合、私のコーディング規約ではこうなります。

    if (*s++ == *t++) {
        count++;
    }


たったこれだけの事に3行も(笑)使わなければなりません。言及された方がいる三項演算子なら

    a = (条件) ? b : c;

    if (条件) {
        a = b;
    } else {
        a = c;
    }


一行が5行にもなる。ソースコードの凸凹(インデント)も、何か見苦しく感じられる。行数が増えれば、一画面で見渡せる情報が減り、ソースコードの見通しを悪くする要因になります。そもそも、簡潔に書くことはプログラミング言語を発展させてきた大きな動機だったはず。

*s++ == *t++自体が複雑すぎ」。念の為、

    while (*s != '\0' && *t != '\0') {
        if (*s == *t) {
            count++;
        }
        s++;
        t++;
    }


わかりやすくなった、誤解の余地が無くなった、けれども、冗長にも感じられます。難しすぎるトリッキーな書き方には、いわばイディオムもあります。慣れが要るのです。C言語には、こうしたイディオムを作ってきた歴史があり、当時の世界中のプログラマが、それを支持したことも普及の要因だと思っています。

イディオムに慣れた人が多ければ通用する書き方、即ちそれは、イディオムに慣れた人が少なければ通用しない・・・私も堅気の仕事(笑)では慎重になります。でも互換性が求められるCコンパイラは、今後もこうしたイディオムをコンパイルし続けます。トリッキーなコードを積極的に書く必要は無いけれど、読めたほうが安心、と思っていれば良いのでは。

次に、生成される機械語の質の良さです。x86, 64bit用GCC 5.4.0で、まずif文をコンパイルしてみた、その肝心部分は、こうです。

    cmpb    %al, %cl
    jne    .L2
    addl    $1, -4(%rbp)
.L2:


2文字を比較(cmpb命令)、条件分岐(jne命令)と、当たり前のコードですが、既に多くの方が言及されているように、分岐命令はパイプラインを乱す要因です。
count += (*s++ == *t++); をコンパイルした場合、肝心の部分はこうです。

    cmpb    %al, %cl
    sete    %al
    movzbl    %al, %eax
    addl    %eax, -4(%rbp)


sete 命令を見慣れない方は多いと思うので念の為:「set + e」という意味の命令名で、e は Equal の e、「比較した結果が等しいならレジスタに1を、等しくなければ0をセットする」。C言語の比較演算子そのものです。ソースコードの違いに対応する違いがあるのですね。

ここに分岐命令はありません。パイプラインを乱す要因を無くすことができたのです。「この命令を実行したら、次はこの命令…」と流れ作業がスムーズに回る様子が目に浮かぶようです。この感覚、どう言ったら良いか、軽快・スッキリ感ですかね。常に速く動作する、とは言いませんが、ハードウェアを無駄なく上手に使っている感じがします。

何と言っても、C言語はハードウェア寄りの言語だから。C言語レベルで不可解なことが、アセンブリ言語レベル・ハードウェアレベルで理解できる事は少なくありません。

もちろん、こうしたことまで考えてプログラムする事は重要でなくなった、それこそが技術の進歩なのは皆様が仰る通りです。でもゼロになったとは思いません。現に、上に示した通り、コンパイラやCPUの技術に反映されてきているし、例えばDSP(Digital Signal Processor)を使う現場では1クロックでも減らす努力があるのでは、と想像します。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/26 10:55

    同じ文を 3 行も 5 行も使わず書く方法がありますよ。わざわざ増やしておいて増えるのがイヤだというのはどういうことなのかさっぱりわかりません。

    キャンセル

  • 2017/12/26 10:58

    念の為、繰り返しになりますが、私が採用しているコーディング規約に従ってif文を書けばこうなる、です。

    キャンセル

  • 2017/12/26 10:59 編集

    行が増えるのはオレオレコーディング規約のせいだということです。つまり全くの別問題です。
    パフォーマンスに絞って回答された方が良いかと。

    キャンセル

  • 2017/12/26 11:01

    まあ、そうです(笑)

    キャンセル

  • 2017/12/26 11:36

    回答ありがとうございます。アセンブラではこうなるんですね、、イディオムとしてのこだわりを感じました。

    rubato6809様のコーディング規約も、このアセンブラコード(jne命令?)に則ってインデント必須にしておられるなら、なるほどと感じました。

    しかしこう考えていくと、真の意味ではC言語は他の最近の言語とは似ても似つかない感じがします。

    「アセンブラを気にして書くべき」と思う人と、「現在の主流な言語に寄せて書くべき」と思う人の2種類がいるって感じなのでしょうか?

    私は後者です。

    キャンセル

  • 2017/12/26 12:37 編集

    「少なくありません」とは書いたけど、多くの人にとって、アセンブリレベルを意識するのは、全体から見ればあくまで例外的な事であって、「主流な言語に寄せて書く」が原則だと思います。

    ただし、メモリの実際の姿を意識することは、他の言語以上に重要だと思います。特に入門レベルの様々なつまづきを乗り越えるには必須だと私は考えます。バイト毎に連続したアドレスが振られていること、種々の型の変数、配列、文字列、構造体、ポインタ等がメモリ上に、どう格納されるのか、2進数、エンディアン、できればスタックも。こうした事をできるだけ具体的にイメージする事が大事だと思います。抽象化したイメージだけだと、どこかで誤解やミスを犯します。不可解な現象もメモリ上でどうなるか、それを考えれば解決することが結構あります。

    メモリを意識するのはアセンブリ言語に触れなくても可能です。
    逆にお尋ねしたいのですが、namnium1125さんは、Cプログラムを読み書きする時、メモリ上にある変数・配列など、その「姿」を意識しないでしょうか。

    その上で、モヤモヤを解消したい、真髄(?)を具体的に理解したい、ならアセンブリ言語に触れることです。でもCを扱う人が全員そうしなければならないとまでは考えません。

    キャンセル

  • 2017/12/26 13:58

    返信ありがとうございます。

    int型は4バイトで10進数では-2^31〜2^31までの範囲を表せられるとか、配列は要素の型のサイズ*個数分だから要素数はsizeof(arr)/sizeof(要素の型)になる、みたいなことは確かに意識しますが、

    具体的にどのような基準でどこに値を格納するのかとか、if文を書くと機械語ではこうなる、みたいなことはあまり意識したことがなかったので、今回の質問で得られた答えは自分にとって新鮮なものが多かったです。

    やっぱりアセンブリやってみたいと思います。

    キャンセル

+2

等価演算子は1を返すためのもの

Cに於いてはそうですよ。
他の言語ではそうとは限らない(そうで無いことが多い)ので、
そういう言語で、count += bool2int(s[i] == t[i]);のように書くのはやり過ぎだと思います。

実行速度を速くするためなのでしょうか? 

速度より、コンパクト(簡潔)に書くためだと思います。
だらだら書くのが嫌いなのでしょう。この程度だとわかりにくくなるわけじゃ無いので。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/25 22:14

    回答ありがとうございます。

    ぶっちゃけ1文字しか変わらない気がするのですが、、(^ ^;

    人によってはif文は煩わしいのでしょうか?

    キャンセル

  • 2017/12/25 22:23

    いや、別に文字数のことは言ってません。「コンパクト」という語が良くなかったですね。括弧内の「簡潔」が意図です。

    > 人によってはif文は煩わしいのでしょうか?
    分岐なので、この場合はここを実行、そうでないとここを実行ということで、簡潔では無いです。

    キャンセル

  • 2017/12/25 22:33

    返信ありがとうございます。関係ないかも知れませんけど、なんとなく三項(条件)演算子が頭によぎりました…

    確かに三項演算子のように、単一の処理として書く目的で書いているのでしたら、納得できるような気がします。

    (トンチンカンなことを言ってるかもしれません…その時はすみません)

    キャンセル

  • 2017/12/25 22:52

    > 2行目でif文を使わないのは、実行速度を速くするためなのでしょうか?
    が質問のポイントだと思って、「おそらく、分岐を避けて簡潔に書きたかったんだろう」という回答をしたのですが、「どちらの書き方が良いか?」というのが質問のポイントなのだったら、ifでしょうね。

    > 確かに三項演算子のように、

    count += (*s++ == *t++) ? 1 : 0;
    はあまりにも無駄無駄感が強すぎなので、これと比べるなら、
    count += (*s++ == *t++) ;
    の方が良いですね。

    キャンセル

  • 2017/12/26 09:00

    返信ありがとうございます。

    三項演算子は今回のケースでも使えるのではないか?という意味ではなくて、

    「if文を使わない理由」として、同じく見た目上の「分岐(というよりはif)を避ける」目的なのかなと思い、書いた次第です。(結局分岐ですけどね(^ ^; )

    質問の論点は、(実行速度に限らず)件の記述をする理由が知りたかったので、「簡潔に書きたかったから」が欲しかった答えの1つです。ありがとうございます。

    キャンセル

  • 2017/12/26 09:11

    三項演算子も私のイメージでは分岐ですね。これは人の感じ方でしょうか。
    あくまで見た目なので、比較演算子も、単体テストのケースを挙げるときは分岐扱いで真の場合、偽の場合それぞれのテストケースを作らないといけないと思うので、見た目が分岐かどうかはあまり重要でない気がしてきました。

    キャンセル

  • 2017/12/27 11:57

    他の人へのコメントに対するコメントですが、本筋から外れるので、こちらに書きますね。

    > なんとなく「ifを書きたくない」というのがわかるようなわからないような…

    かなり違うジャンルですが、デザインパターンに、「nullObjectパターン」という、ifを避けるパターンがあります。これは実行速度を犠牲にしてでもifを避けるといいうことです。

    キャンセル

  • 2017/12/27 14:55

    返信ありがとうございます。m(_ _)m

    まだあまり深く調べてはいないのですが、`if(変数 == null){}`という例外処理を避ける、ということですよね?

    それ以外のifまで避けるのでしょうか?

    キャンセル

  • 2017/12/27 16:52

    ご認識の通りです。nullのチェックを省くための方法です。

    キャンセル

  • 2017/12/27 17:21

    nullObjectパターンを教えてくださった返信を読んだ直後はどんなifも避けるパターンなのかと思ってしまいましたよ(^ ^;

    でも確かに例外処理とかそれに関わるifはなるべく消したいというのはわかります。

    キャンセル

+2

ご質問における、if 文を使わないコードを(a)とし、if 文を使うコードを(b)とすると、
問題とされるのは以下の3点ではないでしょうか。

  1. (a)が正しいかどうか
  2. (a)が好ましいかどうか
  3.  自分ならどうするか

1.については、「特定の条件下なら正しい」ので、それを良しとする人からすれば正しく、
それをグレーもしくは良しとしない人からすれば誤りとなります。
何をもって正しいとするか、という判断を伴う問題とも言えますね。

2.については、「人による」と言わざるを得ません。
移植性、可読性、俊敏性、流動性など、重視される面は様々です。
また、「好ましいかどうかを判断する人は誰なのか」にもよりますね。
(もちろん 1.の時点で正しくないと判断されれば(b)を採用するでしょうが)

最後に 3.について、私は以下のように考えました。
1.については、自分はさておき、誤りであると判断する人が少なからずいるため、グレー。
2.については、1.の判断により、好ましくないと考える。
したがって、新規のソースコードに採用するのは(b)。
ただし、限定された条件の元であえて(a)を採用する必要がある場合は、その意図をコメントに記載する。
なお、既存のソースコードに(a)があり、意図が不明であればそのまま残す。
(もし可能なら、その意図を確認した上で再度判断する。)

いずれにしても、きちんと機能することは大前提ですが、
誰(未来の自分も含む)が見てもその意図が伝わることが肝要かと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

解決済みですが参考までに。

少なくともCにあっては、多少の記述の違いはコンパイラの最適化によって吸収される範疇です。ですので、速度に違いが出ることはそうそうありません。ほとんどの場合、可読性の方が重要視されます。(いまやコンピュータの計算速度は充分すぎるほど高速であり、大抵の場合、さらなる高速化を目指すよりもメンテナンスのしやすさに注力したほうがメリットが大きい)

といっても、可読性はプログラマの主観によるところが大きく、個人差の影響が出るという事実もあります。私の場合は、基本は一文一式の考えでプログラムを書きます。前置、後置インクリメント/デクリメントを使うことで一文二式以上にすることもありますが、二つ以上インクリメント/デクリメントを使うのは好きではありません。

たとえば

 x++ + x++


というように同じ変数を対象とした場合なんかが怖いからです。

そして、最後に一言

count += (*s++ == *t++);


ここで、==が成立する場合、countの加算値は「0以外の整数値」となります。(1とは限りません)つまり、この計算の結果は確定しません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/31 22:07

    回答ありがとうございます!m(_ _)m

    一文一式、一文二式、、そういう考え方があるんですね!確かに複数の意味を含めすぎると複雑になりますよね。わかります。

    最後の一言についてですが、「0以外の整数値になる処理系もある」という意味でしょうか?

    それとも「(1ではなく)『0以外の整数値』とみなさなければならない」という意味でしょうか?

    キャンセル

  • 2017/12/31 22:38

    > ここで、==が成立する場合、countの加算値は「0以外の整数値」となります。(1とは限りません)

    C11ドラフトn1570の6.5.9-3(p96)では`==`演算子が返すのはint型の1(真の場合)と0(偽の場合)のみと書いていますが、真の場合に1にならない環境があるのでしょうか?C99以前では1とは限らない仕様である、標準に準拠していないとあるコンパイラでは違う値を返すと言ったことがあるのであれば、後学のために記載されている公式な資料やコンパイラを教えていただければ幸いです。

    キャンセル

  • 2018/01/01 10:58

    失礼しました。少なくともC11以降では、関係演算子の結果は0または1のようですね。自分の勘違いのようです。K&Rとかの世代なら可能性もあるかもしれませんが、そこまで考えて投稿したものではないです。

    キャンセル

  • 2018/01/01 11:24

    とても知りたいので、質問としてあげました。
    https://teratail.com/questions/107206
    何か知っている方がいれば、回答いただけると幸いです。

    キャンセル

+1

こんにちは。

C言語はメンテナンス性より性能を取る言語なので微妙ですが、私の場合は通常は後者ですね。
速度に差があり、かつ、速度優先な処理やライブラリの場合なら前者を取ることもあるかもしれませんが、そこまでギチギチな最適化が必要なケースは事実上ないです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/25 22:18

    > C言語はメンテナンス性より性能を取る言語なので微妙ですが、
    C言語に深い知識がないので見当違いな指摘でしたら申し訳ないのですが、前者は *s++ == *t++ が false(0) でも count += 0 が働く為、前者の方が性能が低く読めます。
    C言語では特有の考え方があるのでしょうか。

    キャンセル

  • 2017/12/25 22:19

    回答ありがとうございます。m(_ _)m

    ということは、C言語では、性能的な面から今回みたいなハック?を使うこと自体は、可能性としてありえるのでしょうか?

    キャンセル

  • 2017/12/25 22:37

    think49さん。パイプラインの効果でjmp命令が入るより速い場合もあります。が、実際には最適化が働くので同等だろうと予想します。

    namnium1125さん。性能差が有意な場面では有りと思います。
    例えば1クロックでも速くしたい時に前者なら1ループで1クロック速くなるなら十分有りかと。

    キャンセル

  • 2017/12/25 22:57 編集

    To: Chironian さん
    すみません、私の知識の及ばない問題に口を出してしまったかもしれません。
    「パイプラインの効果でjmp命令が入るより速い場合」が全く理解できておりません…。
    私は次のように解釈していて、

    (前者) 「*s++ == *t++」を評価 -> 「count +=」を評価
    (後者) 「*s++ == *t++」を評価 -> 前式が 1 の場合のみ「count++」を評価

    後者が「count++」を評価しなくて良いケースがあるので、後者の方が速いと判断していました。
    あるいは、前者は一つの Statment で済み、後者は2つの Statement を使うので、前者の方が速い、という理屈なのでしょうか。

    キャンセル

  • 2017/12/25 23:09

    think49さん。
    1命令に1クロック以上かかる場合はその通りと思います。
    パイプラインにより1命令が1クロック未満で処理されるケースはそうでもないのです。パイプライン処理では途中でジャンプ命令が入ると効率が落ちやすいのでジャンプさせない方が高速になるケースが少なくありません。
    https://ja.wikipedia.org/wiki/%E5%88%86%E5%B2%90%E5%91%BD%E4%BB%A4

    キャンセル

  • 2017/12/25 23:43

    To: Chironian さん
    完全に理解したとまではいえませんが、「1クロックにおさまる命令」という同一条件下ではジャンプ命令が入ると効率が落ちやすい、というところまでは理解しました。
    「1クロックにおまさる命令とは何か」というところが今後の課題ですが、そういう考え方が必要なことまで理解できてよかったと思っています。
    解説ありがとうございました。

    キャンセル

  • 2017/12/25 23:55

    パイプライン処理#命令パイプライン
    https://ja.wikipedia.org/wiki/%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3%E5%87%A6%E7%90%86#%E5%91%BD%E4%BB%A4%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3
    パイプライン処理#制御ハザード(分岐ハザード)
    https://ja.wikipedia.org/wiki/%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3%E5%87%A6%E7%90%86#%E5%88%B6%E5%BE%A1%E3%83%8F%E3%82%B6%E3%83%BC%E3%83%89%EF%BC%88%E5%88%86%E5%B2%90%E3%83%8F%E3%82%B6%E3%83%BC%E3%83%89%EF%BC%89
    条件判断が入る場合に関しては、こっちのほうがわかりやすいですかね。
    アセンブリ言語レベルで、1つの命令の処理が完全に終わってから次の命令の処理を始めるよりは、可能な範囲内で次の命令の処理を先取りしたほうが効率が良くなります。
    しかし、条件判断の結果に応じてcount++;を実行するかどうか変わる場合は「この命令は実行するの?実行しないの?」が判明するまでは先取り処理できないか、あるいは条件判断の結果を予測して仮の先取り処理をしておいて、予測が外れた場合は先取り処理をキャンセルすることになります。すなわち、効率が落ちる、と。
    ただ、本当にcount += (*s++ == *t++);のほうが速いのか、速いとしてどれぐらいの違いになるのかは一概には言えないでしょう。

    キャンセル

  • 2017/12/26 00:38

    To: okrt さん
    補足ありがとうございます。
    Wikipediaによれば、EX で「分岐先の計算を行う」とあるので、if文の分岐先命令「count++」はここで処理されることになるのでしょうか。
    「一概には言えない」については、「使用しているCPUやコンパイラに依存する」と読みました。
    質問者さんは速度を気にしておられますが、最終的には質問者さんの環境でベンチマークをとってみる以外に結論は出ないのではないか、と思いました。

    キャンセル

  • 2017/12/26 09:19

    皆様ありがとうございます。m(_ _)m

    正直に申しますと、アセンブラ言語がわからないので、細かいところは全くついていけていないです(苦笑)

    環境依存というのは了解しました。私は完全に素人の趣味レベルですから、そこまで速度を追求するような立場ではありませんので、ここでは、

    「他人に見せるコードなら」どうかというのを基準にしたいです。

    …と書くとif文一択の気がしますけどね…(^ ^;

    ただこれを基準にした場合でも、「どの処理系でも速くなるから」こう書くべき、という意図が書いた人にあるならば、具体的な環境がなくとも十分参考になると思いました。

    が、今回はそうではないみたいですね…

    キャンセル

  • 2017/12/26 14:08

    ですね。

    キャンセル

+1

質問の意図とずれているかもしれませんけど。C言語のプロでも情報処理のプロでもない、プログラミングは実務での必要性に鑑みて独学で習得した人間の一人として言わせていただくなら、

*s++ == *t++


もうこの時点で複雑すぎて話にならないです。

大昔のマイコンならいざ知らず、現代のプログラミング環境においては、余程特殊な要件がない限り、プログラムのチューニング目的で裏技的なコーディング技術を駆使する必要性は低いと認識しています。基本的には人間の目から見た見やすさを優先すべきと考えます。見た目のスタイルが似ているperlをよく使いますが、私はインクリメント・デクリメント演算子は必ず単独で使います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/26 09:51 編集

    回答ありがとうございます。m(_ _)m
    仰る意味わかる気がします。

    カウント変数を用意するのが面倒だったのでこう書いてるんじゃないかとは思いますが、

    #include <stdio.h>

    int check_a_num(char *s){
    int count = 0;
    for(int i = 0;s[i] != '\0';i++){
    if(s[i] == 'a') count++;
    }
    return count;
    }

    int main(void) {
    char s[] = "aieueoauaioue";
    printf("%d\n",check_a_num(s));
    return 0;
    }

    普通こんな風にカウント変数iを用意したりして処理するべきですよね…(汚いコードですみません。)

    最低でもcount += (*s == *t);s++;t++;みたいに分けろというのも納得できます。

    ただ、これらはカウント変数や記述を増やしたくないという意図だろうと考えられますけど、count +=の方はわざわざこんな書き方をする理由がいまいちわかりませんでした。

    キャンセル

+1

関数の戻り値はC99で標準入りしていたbool型を使っているなら1に変換されるのでifを書かなくてもいいとは思います。それ以外で真理値を代替している場合はだめですが。
==演算子など組み込みの演算子については、やはり1とみなして問題ありません。

実行速度についてはコンパイラの最適化で消えるたぐいのものです。消えなかった場合はCPUのパイプラインを崩すのでifを使わないほうが良いと思います。

問題の可読性ですが、そもそもoperator[]というシンタックスシュガーを使わずにポインタ演算とか可読性のかけらもないのであれですが、はっきりいって文脈次第なのでなんとも・・・。

ちなみに私なら問題のコードは

for(size_t i = 0; '\0' != s[i] && '\0' != t[i]; ++i) count += (s[i] == t[i]);

と書くと思います(stって多分文字列ですよね?)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/26 12:49

    逆コードゴルフですね。

    キャンセル

  • 2017/12/26 13:37

    回答ありがとうございます。文字列です。

    私も他の言語に習いカウント変数iを使う方に賛成ですが、

    このコードを書いた方は変数が増えるのを嫌ったのではないかと考え、その考えを尊重してあえてそこは触れませんでした。

    ただ私が問題だと思った方はそもそも「役割が違うのではないか」ということです。

    真偽値の1は、偶々真になっているだけですから、それを数値の1とみなして足すのは、いくら現象的には正しくても、何か違和感を感じました。

    仰る通り==なら返る値は0か1ですから問題ないですが、他の回答でのコメントでも出てますが、関数が返す真偽値が0と1とは限りませんから、仕方なく数値で扱っている真偽値を数値とみなすのはコードとしてどうなのかな?と思った次第です。

    キャンセル

  • 2017/12/26 21:23

    Cで真理値が混乱しているのは一つにC99以前にbool型がなかったことです。そのためにその他の型で代替していたために、0と1とは限らないという状況が出来上がった。

    話を戻して、boolからintへの暗黙変換がされるのはたしかに気持ち悪さはありますね。Javaだとできませんし。

    しかしC/C++ではifをあまり書きたくないという思いがあるので、こういうコードもよく見かけるんですね。綺麗さより実利というか。

    キャンセル

  • 2017/12/27 11:14

    なんとなく「ifを書きたくない」というのがわかるようなわからないような…

    具体的な理由とかってあるんでしょうか?

    例えば(昔のコンパイラでは)効率化されず少し遅くなるとか…

    キャンセル

0

今はコンパイラの最適化の性能が向上してますから、処理系依存やトリッキーな書き方は避け、多少冗長でも誰が見ても間違わないような書き方をしたほうが良いと思います。

メモリが極端に少ないとか、少しでも高速に動かす必要がどうしてもある場合は、処理系の指定やコメントでその旨を記述しておいたうえで、そういうコードを書くべきかと。
そうでない場合は、書いた人の単なる自己満足だけだと思います。(ま、趣味のソフトならご自由にというところでしょか)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.86%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    見栄え無視!とにかく軽くしたい

    rubyです。 0以上且つ9桁以下の整数値データを渡すと、加工して返す関数を作成しました。 行っている処理は ①送り値の数値を一桁ずつ調べていき、  1.その桁の数値が

  • 解決済

    非同期処理 wait,notifyAllの使い方

    import java.util.Collections; import java.util.LinkedList; import java.util.List; import j

  • 受付中

    プログラムソースを教えて欲しいです

    前提・実現したいこと しりとりプログラムを作ろうとしています。 CSVデータ中の情報を読み込みデータ内の単語のみでしりとりを行います。 ルールとして ・CPUとの対戦型

  • 受付中

    rescueを使用すると実行時間が10倍近く長くなる

    前提・実現したいこと Rubyにおいてエラーが起きた際、プログラミングを止めない為にrescueを追加しました。 本来60秒程度で終わっていたプログラムが、640秒近くまで動作

  • 解決済

    配列

    n個のデータを配列に読み込み、平均と分散を求めるプログラムを作成して、下記のデータで試せ、という問題です。 {3.9,10.4,9.5,7.5,2.8,4.8,2.9,8.1,3

  • 解決済

    Javascript if文でフォームの数字によって計算式を変える

    前提・実現したいこと Javascriptのif文でフォームに入力した数値によって4パターンの計算をしたいのですがなかなかうまくいきません。。 該当のソースコード <ht

  • 受付中

    Rubyで巨大なHashを高速に生成するにはどうすればいいですか?

    Ruby で、key, value のペアを300万個くらい持つ Hash を高速に生成したいです。 key は user_id で、value はすべて true です。Ruby

  • 解決済

    javaでHTMLファイルを分析

    javaでHTMLのファイルを読みだして財務分析をして流動比率というものを調べたいのですが普通に計算したほうが早いのはわかっているのですが、javaでやりたいです! これは読み

同じタグがついた質問を見る

  • C

    3321questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • プログラミング言語

    643questions

    プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。