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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C

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

プログラミング言語

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

Q&A

解決済

10回答

4617閲覧

if文を使うべきか否か

namnium1125

総合スコア2043

C

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

プログラミング言語

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

4グッド

1クリップ

投稿2017/12/25 12:28

編集2017/12/30 02:06

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

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

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

c

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

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

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

c

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

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

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

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

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

追記

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

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

plain

1 user system total real 2count_eq 0.000000 0.000000 5.060000 ( 5.073679) 3count_if 0.000000 0.000000 5.040000 ( 5.045016) 4count_if_after 0.000000 0.000000 5.160000 ( 5.170517) 5count_if_null 0.000000 0.000000 5.130000 ( 5.135678) 6count_r_eq 0.000000 0.000000 5.200000 ( 5.206062) 7count_r_if 0.000000 0.010000 5.160000 ( 5.151430) 8count_r_if_after 0.000000 0.000000 5.090000 ( 5.103209) 9count_r_if_null 0.000000 0.000000 5.080000 ( 5.088278) 10result -> line: 10000, total count: 204632007 11 12 user system total real 13count_eq 0.000000 0.000000 5.140000 ( 5.150710) 14count_if 0.000000 0.000000 5.040000 ( 5.050327) 15count_if_after 0.000000 0.000000 5.100000 ( 5.104275) 16count_if_null 0.000000 0.000000 5.190000 ( 5.203628) 17count_r_eq 0.000000 0.000000 5.110000 ( 5.111663) 18count_r_if 0.000000 0.010000 5.150000 ( 5.137537) 19count_r_if_after 0.000000 0.000000 5.100000 ( 5.118537) 20count_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

nullbot, LouiS0616, yoh1028👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答10

0

ベストアンサー

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

ということで、パフォーマンスを図るためのコード類を作りました。長くなったので下記の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/26 15:43

raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namnium1125

2017/12/27 02:11

こんなに凄い検証をわざわざありがとうございます!!m(_ _)m (ruby触ったことないんですけど、今回のコードからいろいろと学ばせていただきました。。) 論より証拠(ただし環境依存)なんだなと強く感じました。 raccy様の環境では(*ristrictだと)ifの方が速いんですね、私の方は現在手元にPCがないのでまだ検証できてないのですが、 ifの方が遅いと誤解していました。。 ただ、だからと言って、例え私が使う全ての環境でifを使った方が速かったとしても、やっぱり「ifが絶対」ではないんじゃないかなと、今回の回答全体を見て思います。 というのは生成される機械語の内容や、(人によっては)分岐を挟まないことによる簡潔さが生まれることなど、速さ以外の面でも、「こっちが良い」と断定できるほどのものがないように感じたからです。(なんか偉そうにすみません。。あくまでも率直な感想です。) それでも具体的にどう考えてきたかがわかったので、とても勉強になりました。 改めて回答ありがとうございます。m(_ _)m
guest

0

C

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

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

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

追記

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

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

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

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

投稿2017/12/25 12:30

編集2017/12/26 02:23
Zuishin

総合スコア28656

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2017/12/25 12:54

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

2017/12/25 12:59

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

2017/12/25 13:12

やっぱり可読性や意味の方が大切ですよね、、 回答ありがとうございます。m(_ _)m
namnium1125

2017/12/26 02:42

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

2017/12/26 02:50 編集

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

2017/12/26 04:49

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

0

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

C

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

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

C

1 a = (条件) ? b : c; 2 3 if (条件) { 4 a = b; 5 } else { 6 a = c; 7 }

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

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

C

1 while (*s != '\0' && *t != '\0') { 2 if (*s == *t) { 3 count++; 4 } 5 s++; 6 t++; 7 }

わかりやすくなった、誤解の余地が無くなった、けれども、冗長にも感じられます。難しすぎるトリッキーな書き方には、いわばイディオムもあります。慣れが要るのです。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 01:44

編集2017/12/26 04:05
rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2017/12/26 01:55

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

2017/12/26 01:58

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

2017/12/26 02:01 編集

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

2017/12/26 02:01

まあ、そうです(笑)
namnium1125

2017/12/26 02:36

回答ありがとうございます。アセンブラではこうなるんですね、、イディオムとしてのこだわりを感じました。 rubato6809様のコーディング規約も、このアセンブラコード(jne命令?)に則ってインデント必須にしておられるなら、なるほどと感じました。 しかしこう考えていくと、真の意味ではC言語は他の最近の言語とは似ても似つかない感じがします。 「アセンブラを気にして書くべき」と思う人と、「現在の主流な言語に寄せて書くべき」と思う人の2種類がいるって感じなのでしょうか? 私は後者です。
rubato6809

2017/12/26 03:47 編集

「少なくありません」とは書いたけど、多くの人にとって、アセンブリレベルを意識するのは、全体から見ればあくまで例外的な事であって、「主流な言語に寄せて書く」が原則だと思います。 ただし、メモリの実際の姿を意識することは、他の言語以上に重要だと思います。特に入門レベルの様々なつまづきを乗り越えるには必須だと私は考えます。バイト毎に連続したアドレスが振られていること、種々の型の変数、配列、文字列、構造体、ポインタ等がメモリ上に、どう格納されるのか、2進数、エンディアン、できればスタックも。こうした事をできるだけ具体的にイメージする事が大事だと思います。抽象化したイメージだけだと、どこかで誤解やミスを犯します。不可解な現象もメモリ上でどうなるか、それを考えれば解決することが結構あります。 メモリを意識するのはアセンブリ言語に触れなくても可能です。 逆にお尋ねしたいのですが、namnium1125さんは、Cプログラムを読み書きする時、メモリ上にある変数・配列など、その「姿」を意識しないでしょうか。 その上で、モヤモヤを解消したい、真髄(?)を具体的に理解したい、ならアセンブリ言語に触れることです。でもCを扱う人が全員そうしなければならないとまでは考えません。
namnium1125

2017/12/26 04:58

返信ありがとうございます。 int型は4バイトで10進数では-2^31〜2^31までの範囲を表せられるとか、配列は要素の型のサイズ*個数分だから要素数はsizeof(arr)/sizeof(要素の型)になる、みたいなことは確かに意識しますが、 具体的にどのような基準でどこに値を格納するのかとか、if文を書くと機械語ではこうなる、みたいなことはあまり意識したことがなかったので、今回の質問で得られた答えは自分にとって新鮮なものが多かったです。 やっぱりアセンブリやってみたいと思います。
guest

0

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

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

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

たとえば

C

1 x++ + x++

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

そして、最後に一言

C

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

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

投稿2017/12/31 12:51

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namnium1125

2017/12/31 13:07

回答ありがとうございます!m(_ _)m 一文一式、一文二式、、そういう考え方があるんですね!確かに複数の意味を含めすぎると複雑になりますよね。わかります。 最後の一言についてですが、「0以外の整数値になる処理系もある」という意味でしょうか? それとも「(1ではなく)『0以外の整数値』とみなさなければならない」という意味でしょうか?
raccy

2017/12/31 13:38

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

2018/01/01 01:58

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

0

ご質問における、if 文を使わないコードを(a)とし、if 文を使うコードを(b)とすると、
問題とされるのは以下の3点ではないでしょうか。
0. (a)が正しいかどうか
0. (a)が好ましいかどうか
0. 自分ならどうするか

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

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

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

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

投稿2017/12/28 01:57

yoh1028

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

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

*s++ == *t++

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

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

投稿2017/12/25 15:43

KojiDoi

総合スコア13669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namnium1125

2017/12/26 00:52 編集

回答ありがとうございます。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 +=の方はわざわざこんな書き方をする理由がいまいちわかりませんでした。
guest

0

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

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

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

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

投稿2017/12/25 12:57

otn

総合スコア84421

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

namnium1125

2017/12/25 13:14

回答ありがとうございます。 ぶっちゃけ1文字しか変わらない気がするのですが、、(^ ^; 人によってはif文は煩わしいのでしょうか?
otn

2017/12/25 13:23

いや、別に文字数のことは言ってません。「コンパクト」という語が良くなかったですね。括弧内の「簡潔」が意図です。 > 人によってはif文は煩わしいのでしょうか? 分岐なので、この場合はここを実行、そうでないとここを実行ということで、簡潔では無いです。
namnium1125

2017/12/25 13:33

返信ありがとうございます。関係ないかも知れませんけど、なんとなく三項(条件)演算子が頭によぎりました… 確かに三項演算子のように、単一の処理として書く目的で書いているのでしたら、納得できるような気がします。 (トンチンカンなことを言ってるかもしれません…その時はすみません)
otn

2017/12/25 13:52

> 2行目でif文を使わないのは、実行速度を速くするためなのでしょうか? が質問のポイントだと思って、「おそらく、分岐を避けて簡潔に書きたかったんだろう」という回答をしたのですが、「どちらの書き方が良いか?」というのが質問のポイントなのだったら、ifでしょうね。 > 確かに三項演算子のように、 count += (*s++ == *t++) ? 1 : 0; はあまりにも無駄無駄感が強すぎなので、これと比べるなら、 count += (*s++ == *t++) ; の方が良いですね。
namnium1125

2017/12/26 00:00

返信ありがとうございます。 三項演算子は今回のケースでも使えるのではないか?という意味ではなくて、 「if文を使わない理由」として、同じく見た目上の「分岐(というよりはif)を避ける」目的なのかなと思い、書いた次第です。(結局分岐ですけどね(^ ^; ) 質問の論点は、(実行速度に限らず)件の記述をする理由が知りたかったので、「簡潔に書きたかったから」が欲しかった答えの1つです。ありがとうございます。
otn

2017/12/26 00:11

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

2017/12/27 02:57

他の人へのコメントに対するコメントですが、本筋から外れるので、こちらに書きますね。 > なんとなく「ifを書きたくない」というのがわかるようなわからないような… かなり違うジャンルですが、デザインパターンに、「nullObjectパターン」という、ifを避けるパターンがあります。これは実行速度を犠牲にしてでもifを避けるといいうことです。
namnium1125

2017/12/27 05:55

返信ありがとうございます。m(_ _)m まだあまり深く調べてはいないのですが、`if(変数 == null){}`という例外処理を避ける、ということですよね? それ以外のifまで避けるのでしょうか?
otn

2017/12/27 07:52

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

2017/12/27 08:21

nullObjectパターンを教えてくださった返信を読んだ直後はどんなifも避けるパターンなのかと思ってしまいましたよ(^ ^; でも確かに例外処理とかそれに関わるifはなるべく消したいというのはわかります。
guest

0

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

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

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

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

c

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

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

投稿2017/12/26 03:30

yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2017/12/26 03:49

逆コードゴルフですね。
namnium1125

2017/12/26 04:37

回答ありがとうございます。文字列です。 私も他の言語に習いカウント変数iを使う方に賛成ですが、 このコードを書いた方は変数が増えるのを嫌ったのではないかと考え、その考えを尊重してあえてそこは触れませんでした。 ただ私が問題だと思った方はそもそも「役割が違うのではないか」ということです。 真偽値の1は、偶々真になっているだけですから、それを数値の1とみなして足すのは、いくら現象的には正しくても、何か違和感を感じました。 仰る通り==なら返る値は0か1ですから問題ないですが、他の回答でのコメントでも出てますが、関数が返す真偽値が0と1とは限りませんから、仕方なく数値で扱っている真偽値を数値とみなすのはコードとしてどうなのかな?と思った次第です。
yumetodo

2017/12/26 12:23

Cで真理値が混乱しているのは一つにC99以前にbool型がなかったことです。そのためにその他の型で代替していたために、0と1とは限らないという状況が出来上がった。 話を戻して、boolからintへの暗黙変換がされるのはたしかに気持ち悪さはありますね。Javaだとできませんし。 しかしC/C++ではifをあまり書きたくないという思いがあるので、こういうコードもよく見かけるんですね。綺麗さより実利というか。
namnium1125

2017/12/27 02:14

なんとなく「ifを書きたくない」というのがわかるようなわからないような… 具体的な理由とかってあるんでしょうか? 例えば(昔のコンパイラでは)効率化されず少し遅くなるとか…
guest

0

こんにちは。

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

投稿2017/12/25 13:08

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2017/12/25 13:18

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

2017/12/25 13:19

回答ありがとうございます。m(_ _)m ということは、C言語では、性能的な面から今回みたいなハック?を使うこと自体は、可能性としてありえるのでしょうか?
Chironian

2017/12/25 13:37

think49さん。パイプラインの効果でjmp命令が入るより速い場合もあります。が、実際には最適化が働くので同等だろうと予想します。 namnium1125さん。性能差が有意な場面では有りと思います。 例えば1クロックでも速くしたい時に前者なら1ループで1クロック速くなるなら十分有りかと。
think49

2017/12/25 13:58 編集

To: Chironian さん すみません、私の知識の及ばない問題に口を出してしまったかもしれません。 「パイプラインの効果でjmp命令が入るより速い場合」が全く理解できておりません…。 私は次のように解釈していて、 (前者) 「*s++ == *t++」を評価 -> 「count +=」を評価 (後者) 「*s++ == *t++」を評価 -> 前式が 1 の場合のみ「count++」を評価 後者が「count++」を評価しなくて良いケースがあるので、後者の方が速いと判断していました。 あるいは、前者は一つの Statment で済み、後者は2つの Statement を使うので、前者の方が速い、という理屈なのでしょうか。
Chironian

2017/12/25 14:09

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

2017/12/25 14:43

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

2017/12/25 14: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++);のほうが速いのか、速いとしてどれぐらいの違いになるのかは一概には言えないでしょう。
think49

2017/12/25 15:38

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

2017/12/26 00:19

皆様ありがとうございます。m(_ _)m 正直に申しますと、アセンブラ言語がわからないので、細かいところは全くついていけていないです(苦笑) 環境依存というのは了解しました。私は完全に素人の趣味レベルですから、そこまで速度を追求するような立場ではありませんので、ここでは、 「他人に見せるコードなら」どうかというのを基準にしたいです。 …と書くとif文一択の気がしますけどね…(^ ^; ただこれを基準にした場合でも、「どの処理系でも速くなるから」こう書くべき、という意図が書いた人にあるならば、具体的な環境がなくとも十分参考になると思いました。 が、今回はそうではないみたいですね…
guest

0

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

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

投稿2017/12/26 08:12

PineMatsu

総合スコア3579

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問