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

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

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

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

Q&A

解決済

6回答

3475閲覧

C言語での三項演算子の出力方法について

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2020/03/11 14:33

前書き

FizzBuzz問題をif文を使わないで解くコードをC言語で考えています。
コードは三項演算子を使う事にして、出力の形を

数値 (Fizz or Buzz or FizzBuzz or 空白)

にしたいと考えて、最初に以下のコードの考えました。

ver1

1  void FB(){ 2 for( int a = 1; a<50; a++){ 3 printf(a % 15 == 0 ? "%d Fizzbuzz\n",a : a % 3 == 0 ? "%d Fizz\n",a : a % 5 == 0 ? "%d Buzz\n",a : "%d\n",a); 4 } 5}

しかし実行すると以下のエラーが出ました。

error

1  warning: pointer/integer type mismatch in conditional expression

そこで三項演算子について調べていると以下のコードと似た記述をしているコードを見つけて書き直した所、実行できました。

ver2

1  printf(a % 15 == 0 ? "%d Fizzbuzz\n" : a % 3 == 0 ? "%d Fizz\n" : a % 5 == 0 ? "%d Buzz\n" : "%d\n",a);

本題

実行は出来たのですが最初のerrorについて理解しきれていません。
私は三項演算子の処理(条件)ごとに ,a を付ける必要があると考えていました。

  条件 ? "%d Fizzbuzz\n",a : 処理(条件)

ですが実際は、一番最後の処理の後に ,a を付けるだけで他の処理にも反映が出来ました。

エラーが条件文側の問題である事までは理解したのですが、文法記述として何が問題だったのか理解できていないので、その部分を教えてもらえると嬉しいです。

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

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

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

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

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

guest

回答6

0

エラーerrorと警告warningは厳密に区別して下さい。エラーは文法に則っていないもの、警告は文法的に解釈は可能なんだけど正しくない可能性があるもの、です。

まず。
三項演算子ですが、
式1 ? 式2 : 式3
で、式1の値が0以外なら式2の「値」を、式1の値が0なら式3の「値」を返すものです。決して、ソースの文字上の処理などではなく、式を評価(計算)した結果を返すことに注意して下さい。これが勘違いその1。

次、カンマ演算子に絡んで、
"%d Buzz\n", a
であれば、,はカンマ演算子なので"%d Buzz"を評価して捨て、aを評価してそれを返します。では、
式1 ? 式2 : 式3 , 式4
はどのように評価されるでしょうか。式1 ? 式2 : 式3は三項演算子によって結びつきますので、これを式Aとすると
式A, 式4
という解釈になり、まず式Aを評価してその結果は捨てられ、式4の値が返ることになります。
プログラム中のa % 5 == 0 ? "%d Buzz\n", a : "%d\n", aの部分に適用すると式Aの部分は
a % 5 == 0 ? "%d Buzz\n", a : "%d\n"
a%5が0であれば、"%d Buzz\n",aでint型のaを返し、
a%5が0以外であれば、"%d\n"つまりcharへのポインタを返します。式の結果が、あるときは整数型、あるときはポインタを返すのは(Cではポインタの扱いに鷹揚で、暗黙に整数型に置き換えたりするのでエラーにはならないとしても)なんだか異常と考えられるでしょう。
これが「警告」の内容です。

投稿2020/03/11 15:06

編集2020/03/11 15:36
thkana

総合スコア7703

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

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

rubato6809

2020/03/11 15:42 編集

エラー、警告を区別することを含めて概ね同意です。 > カンマ演算子なので"%d Fizz"を評価して捨て、aを評価して a == 3 の時点で具体的に説明すると printf("%d Fizz", a); となるのではなく、"%d Fizz"を捨てて、 printf(a, a); 即ち printf(3, 3); を実行します。 もちろん、セグメントフォールトで異常終了します(確かめましたw)。 それと、質問者は > しかし実行すると以下のエラーが出ました。 と書いてますが、プログラムを実行した時の表示ではなく、コンパイル時の表示です。これも適切に区別していただきたいものです。
thkana

2020/03/11 15:48

> プログラムを実行した時の表示ではなく、コンパイル時の表示です。これも適切に区別していただきたいものです。 ですね。 「プログラムを実行」というのはソースコードに書いてある内容に沿ってコンピュータが動くこと。で、コンパイル時のエラーと実行時のエラーは対応方法が全然違いますから。 なのに、IDEでポタン一発しか知らないとコンパイルと実行の区別を知りもしない人もいるようです。 別に常にコマンドラインでタイピングの苦行をしろとはいいませんが、一度はコマンドラインでコンパイル/実行して、IDEが中でやっていることを知っておいて欲しいと思います。
guest

0

演算子による演算の結果は式です。
「関数の引数2つ」というのは、式じゃないです。個々の引数が式です。
コードで言うと、

C

1x = "%d\n", a; 2printf(x);

で、

C

1printf("%d\n", a);

と同じ意味にはならないのは分かりますよね?(そもそもxの型は??)
ということで、演算結果として2つの値を持つのは無理です。

お書きのコードは、"~~~",aのカンマがカンマ演算子と見做されて、左側の値("~~~")が無視され、

C

1printf(a % 15 == 0 ? a : a % 3 == 0 ? a : a % 5 == 0 ? a : "%d\n", a);

と同じになります。で、a"%d\n"の型が不一致なので警告が出ています。いずれにせよ、期待通りの結果にはなりません。

投稿2020/03/11 14:59

otn

総合スコア85901

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

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

0

三項「演算子」は、あくまで「値」を計算するものです。printf関数は複数の値を引数として呼び出すことができますが、「値」とは、printf(A, B, ...)とあるうちの、AやBのことです。例えば、このAを、X ? Y : Zで置き換えると、printf(X ? Y : Z, B, ...)となります。もっと具体的にすると、printf(a % 2 == 0 ? "Even! %d" : "%d is Odd", B)とすれば、aが偶数ならprintf("Even! %d", B)と、奇数ならprintf("%d is Odd", B)と同じになります。三項演算子で、「表示する書式を」切り換えているわけです。「表示する書式と表示する値の組み合わせ」を切り換えるわけではないので、質問にあった、「,a」の記述が「三項演算子内では」不要なのです(三項演算子の終了後は必要)。

投稿2020/03/11 14:51

編集2020/03/11 14:56
majiponi

総合スコア1722

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

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

0

3項演算子は、

  条件 ? 真のときの値 : 偽のときの値

です。

エラーが出る、っていうコードは、上記の3項演算子の形になってませんね

投稿2020/03/11 14:38

y_waiwai

総合スコア88042

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

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

thkana

2020/03/11 15:06

そもそもエラーでないし、3項演算子の形にはかろうじてなっています。
guest

0

ベストアンサー

まだ解決していないので私も説明してみます。

一見なんでもないコンマ「,」が曲者です。関数の引数並びを区切るコンマ(printf("%d\n", num); のような)は見慣れていると思いますが、文法的にコンマ演算子と呼ぶコンマは馴染みが少ないと思います。

コンマ演算子と、その働き、どれがコンマ演算子なのか。この機会にコンマ演算子に慣れるために、まずは文字列反転コードで典型的な使い方を示します。

C

1#include <stdio.h> 2#include <string.h> 3int main(void) 4{ 5 char str[] = "1234567"; // この文字列を反転する 6 int a, b; char t; 7 printf("1: %s\n", str); // 反転する前 8 for (a = 0, b = strlen(str) - 1; a < b; ++a, --b) 9 t = str[a], str[a] = str[b], str[b] = t; // 交換 10 printf("2: %s\n", str); // 反転した後

for ( 初期化 ; 条件 ; 増分処理 ) 文 という構文で、

  • a = 0, b = strlen(str) - 1 の2つの処理をまとめて「初期化」にする
  • ++a, --b の2つの処理をまとめてひとつの「増分処理」にする
  • t = str[a], str[a] = str[b], str[b] = t; をひとつの文にまとめる

これら4つのコンマはどれもコンマ演算子です。
上のコードに次の三行を加えます。

C

1 printf( "hello %s", "world(3)\n" ); // 引数を区切るカンマ 2 printf(("hello %s", "world(4)\n")); // カンマ演算子 3 printf( "ans = %d\n", (b = 2, b + 3)); // ans = 5

警告(warning)には目を瞑ってください。実行モジュールが作られ、実行できますから。実行すると表示結果は次の通りです。

plain

1hello world(3) ← 引数を区切るコンマ 2world(4) ← コンマ演算子 3ans = 5 ← ans = 5

「引数を区切るコンマ」行の表示は問題ないでしょう。「コンマ演算子」行の前に「ans = 5」行の方を考察します。

(b = 2, b + 3) というカッコの中に式が二つあり、順に実行した結果、カッコ全体の値は 5(= b + 3 = 2 + 3)となったのです。
即ち、printf("ans = %d\n", (b = 2, b + 3)); は printf("ans = %d\n", 5);
となったのですが、このことは**「式1, 式2」の値は「式2」**だということを意味します。

そのことから、カッコで囲んだ ("hello %s", "world(4)\n") の値は、コンマの右側の "world(4)\n" となるので、
printf(("hello %s", "world(4)\n")) は printf("world(4)\n") と評価されたことが表示結果から読み取れます。

次に条件演算子(三項演算子、?:)。分かりやすくひとつだけ使ったコードを追加して試します。

C

1 for (a = 4; a <= 7; ++a) 2 printf(a == 6 ? "hello %s", "world(6)\n" : "%d\n", a);

実行すれば表示結果は次のようになるはずです。

plain

14 25 3world(6) 47

この表示結果から、上の printf() は

  • a != 6 の場合、printf("%d\n", a); を実行した
  • a == 6 の場合、printf("world(6)\n"); を実行した

ことがわかります。もっと踏み込んで言えば、a == 6 の時、

  • printf( "hello %s", "world(6)\n" ); と評価したのではなく
  • printf(("hello %s", "world(6)\n")); と評価したようだ

と推測できる表示結果です。つまり、そのコンマは、引数を区切るコンマではなく、コンマ演算子だったということ。

また、このことは条件演算子の「条件 ? 式A : 式B」という構造からも言えます。
「条件 ? 式A1 , 式A2 : 式B」と、コンマ演算子がある場合、
「式A1, 式A2」の部分はまとめてひとつの式とみなされるので、
「条件 ? ( 式A1 , 式A2 ) : 式B」のように扱われる、と考えられます。

念の為:同じ「,」でも「引数を区切るコンマ」と「コンマ演算子」は文法的に違う。区別できるようになりましょう。
まとめると、

  1. printf(a == 6 ? "hello %s", "world(6)\n" : "%d\n", a); は
  2. a == 6 の時、printf(("hello %s", "world(6)\n")); と評価され、
  3. 結果として printf("world(6)\n"); を実行した。

もうひとつ確認しておくと、

C

1 printf(a == 6 ? "hello %s", "world(6)\n": "%d\n", a);

で条件演算子とみなされたのは、

C

1 a == 6 ? "hello %s", "world(6)\n" : "%d\n"

の範囲です。
さらに、( 式A1 , 式A2 ) は結果として「式A2」の値が評価されるのだから、上のループは次と同じです。

C

1 for (a = 4; a <= 7; ++a) 2 printf(a == 6 ? "world(6)\n" : "%d\n", a);

ここまでくれば

三項演算子の処理(条件)ごとに ,a を付ける必要があると考えていました

は正しくなく、とてもマズイことを説明できます。次のコードを追加します。

C

1 printf(a > 0 ? "true %d\n", a : "false %d\n", a);

は、上の考察を応用すると、次のコードと同じです。

C

1 printf(a > 0 ? a : "false %d\n", a);
  • a > 0 が偽なら printf("false %d\n", a); を実行する(これは問題ない)
  • a > 0 が真なら printf(a, a); を実行する(大問題)

printf() の一番目の引数は書式文字列(のアドレス)なのに整数 a が渡されるので、コンパイラは

warning: pointer/integer type mismatch in conditional expression

という警告を出したのです。実行すればセグメンテーション・フォールトで異常終了します。

エラーが条件文側の問題である事までは理解した

まず、エラー(error)ではなく警告(warning)である、とツッコんでおきます。
警告は「条件式にポインタ/整数という型の不一致がある」と言ってますが、条件式それ自体の問題というよりも条件式の周辺に問題があると受け取れば良いと思います。その証拠に、同じ条件式でも次のコードに警告はでません。

C

1 printf(a > 0 ? "true %d\n" : "false %d\n", a);

たとえば、うっかりセミコロン「;」を書き忘れた時、コンパイラはその行ではなく、次の行にエラーがある、と表示することがあります。それと似たようなことではないかと。一般に、コンパイラが的確なエラー表示を出すことは難しいと聞きました。

投稿2020/03/13 10:05

rubato6809

総合スコア1382

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

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

退会済みユーザー

退会済みユーザー

2020/03/13 14:44

丁寧なご回答ありがとうございます。 理解する事ができました。 またエラーと警告を一緒くたにして申し訳ありません。まだ自分がプログラミングでの細かい表現やIDEやコンパイラなどがどのように動いているのか理解できておらず、雑な表現をしてしまいました。
guest

0

3項演算子の ? と : の間の「"%d Fizzbuzz\n",a」は式であり
「,」は、2項演算子のコンマ演算子で、演算結果は右オペランドの a だけです。

ver1 の printf は次のコードと同じです。

printf(a % 15 == 0 ? a : a % 3 == 0 ? a : a % 5 == 0 ? a : "%d\n", a);

最後の「,」関数の引数の区切りです。

3項演算子の結果は、a または "%d\n" です。

"%d\n" は文字列で「charの配列」ですが、「charへのポインタ」に変換されます。
a の型は int。
だから、そのような型の不一致という警告(warning) が出るのです。

投稿2020/03/11 15:00

kazuma-s

総合スコア8224

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問