はじめまして。最近趣味でプログラムを始めました。
参考書を見ながら、printfを使って「3-2は1です。」と表示しようとしました。
見ながら打ち込んで行ったのですが、
printf("%d-%dは%dです。", 3, 2, 3-2 ); を
printf("%d-%dは%dです。")のまま、コンパイルしてしまったのですが
計算結果は
「1638280-4198563は1です。」との結果がでました。
これは何が表示されたのでしょうか?
「%d-%dは%dです。」と同じ書式設定の物を打ち込んでいるのになぜ違う
数字の羅列が出たのでしょうか?
参考書のプログラムは下記の通りでした。
#include <stdio.h>
int main(void)
{
printf("%d-%dは%dです。", 3, 2, 3-2 );
return 0;
}
どうぞご回答よろしくお願いします。
No.2ベストアンサー
- 回答日時:
ちょっと専門的な話になります。
関数を呼び出すとき、内部では
引数1: XXXX
引数2: YYYY
引数3: ZZZZ
....
という引数を渡すための領域を用意して、関数を呼び出します。
関数側では、この領域から引数を得ます。
int func(int a,int b)
という関数だったら、引数は2個なので
z=func(101,102)
とあったら
引数1: 101
引数2: 102
という領域を用意して、関数側では
a←引数1(101)
b←引数2(102)
となります。
ところが、printf の様に引数の数が一定でない関数の場合、引数がいくつ渡されたかが「わかりません」
というのがC言語の仕様です。
引数の数は、何か別の方法で知らせる必要があります。
引数に「引数の数」を入れる
func( 3,101,102,103) /* 最初の3は引数の数 */
特定の引数を「これが最後」という意味に使う
func( 101,102,103, -1) /* -1 が来たら終わり */
printfの場合は、「書式文字列を解析する」ことで、必要な引数を知ります。
printf("%d-%dは%dです。", 3, 2, 3-2 );
だと
引数1: "%d-%dは%dです。" (へのポインタ)
引数2: 3
引数3: 2
引数4: 1 (3-2の計算は、関数に渡す前に計算される)
となります。
このとき、printf側では
・引数1の書式文字列を得る
・書式文字列を解析する
→ %d が3つあるから、 intが3つ必要
・1つ目のintとして 引数2 を得る
・2つ目のintとして 引数3 を得る
・3つ目のintとして 引数4 を得る
と動作します。
では
printf("%d-%dは%dです。" );
だとどうなるか?
引数1: "%d-%dは%dです。" (へのポインタ)
はいいでしょう。
このとき、printf側では
・引数1の書式文字列を得る
・書式文字列を解析する
→ %d が3つあるから、 intが3つ必要
・1つ目のintとして 引数2(!?) を得る
・2つ目のintとして 引数3(!?) を得る
・3つ目のintとして 引数4(!?) を得る
と動作します。
引数2、3、4は用意されていないのに、そこから値を得ています。
用意されていない領域でも、アクセスしようとしたらエラーにならずにアクセスできる。
これもC言語の特徴です。
/* その領域がOSの保護領域等の場合は、エラーになることがあります。
ただ、これは、OSが発生させるエラーであって、C言語としてのエラーではありません */
このとき、引数2、3、4には何が入っているかわかりません(不定)
前に別な関数を使ったときの残りかもしれないし、何かの変数が使っている領域かもしれません。
今回はたまたま
引数2: 1638280
引数3: 4198563
引数4: 1 (これはたまたま1だった、というだけで、3-2が入っていたわけではない)
だった、ということです。
これを完全に防ぐ方法は「ありません」。
プログラマが気をつける以外に方法はありません。
一部Cコンパイラには、書式文字列と引数の数を比較して、数や型に不一致があると「警告」を出すものもあります。
/* gcc に -Wall オプションを付けるとか */
ですが、これも printf(s,1,2,3) 等と変数を使われたら対応できません。
余談ですが。
printf("%d-%dは%dです。", 3.0, 2.0, 3.0-2.0 );
も期待する出力にはならないでしょう。
実は、数だけではなく、引数の型もわかりません。
そのため、%d→int と決め付けて値を得ます。
このとき 3.0→3と変換されるのではなく、「 3.0の内部のビット表現→それと同じビット表現のint 」と変換されるため、「3」とはまったく別の値になります。
丁寧な解説ありがとうございました。
知識の浅い私にも何とか理解できそうです。
ご回答をいただくまでいろんなC言語の解説サイトを見ましたが、上記に触れているところはありませんでした。
どこの入門サイトにも最初に触れるprintfなので、どこかしら解説されていそうですがどこもスルーされているようです。
皆さんエラー内容に関しては特にモヤモヤしたりしないんでしょうか?わたしはモヤモヤしっぱなしだったんですが・・・
No.3
- 回答日時:
規格上, printf などで第1引数に与える書式に対して, そのあとの引数列が足りないあるいは型が矛盾する場合には
未定義動作
となっています.
まあたいていは今の例のように何かしらのゴミが表示されるでしょうが, たとえ
・突然鼻歌を歌いだす
・なぜか自爆する
などとということが起きたとしても「未定義動作」である以上文句は言えません.
わかりやすい例えでの解説ありがとうございます。
私が「旦那にラーメン食いたいから箸を取って欲しいと命令したら、ボールペン2本(箸ではないが取り敢えず箸と認識できそうなもの)を持ってきた」みたいな解釈でよろしいのでしょうか?
No.4
- 回答日時:
プログラムから呼び出されるサブルーチン printf に必要な、レジスタの初期化処理が不足しているからです。
特殊呼び出しとなる printf は、一般にC言語コンパイルラではうまく検証できないので、この様な泥臭い問題が発生します。
---- printf("%dです。", 123) をコンパイルした機械語イメージ
mov レジスタ2, 前の処理に使った値
...
mov レジスタ2, 123 ← レジスタの値を初期化
mov レジスタ1, 書式文字列のアドレス
call printf ← レジスタ1 の書式に%dがあるので、レジスタ2 から「123」を読み取って、整数として表示
---- printf("%dです。") をコンパイルした機械語イメージ
mov レジスタ2, 前の処理に使った値
...
mov レジスタ1, 書式文字列のアドレス
call printf ← レジスタ1 の書式に%dがあるので、レジスタ2 にたまたま設定してあった「前の処理に使った値」を読み取って、整数として表示
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# C言語 3 2022/10/04 15:07
- C言語・C++・C# Cのdoubleの浮動小数点表示について 3 2023/04/17 13:14
- C言語・C++・C# 至急お願いします。プログラミングの問題です。 最初に正の整数nの入力を受け付け、次に分数の分子と分母 3 2022/07/19 17:09
- C言語・C++・C# 至急教えてください! プログラミングの問題です! お願いします! 出力2と全く同じ出力をするように、 2 2022/06/22 23:10
- C言語・C++・C# プログラミングのペーパーテスト 実行結果を表示せよ #include <stdio.h> int h 1 2022/07/09 15:27
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 最初に正の整数nの入力を受け付け、次に分数の分子と分 1 2022/07/19 17:03
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 malloc関数を使ってください!お願いします! 最 1 2022/07/21 09:28
- C言語・C++・C# C言語: ポインタ 5 2022/06/01 08:33
- C言語・C++・C# プログラミングの授業のペーパーテスト 実行結果を答えろ #include int x[ ] = {1 3 2022/06/16 20:08
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
10個出力で改行したいのですが...
-
c言語でAからZまでを表示する...
-
1秒で動くインターバルタイマ
-
C言語初心者です。次の問題で質...
-
printf で二進表示を行いたい。
-
ヘロンの公式を作ったプルグラ...
-
C言語での数字の花形表示
-
二つの整数値の大小比較
-
インクリメント演算子をprintf...
-
strcmp
-
球の体積と表面積を表示するプ...
-
printf( " %2d", p * q );
-
C言語 プログラミング
-
コンパイルエラーについて
-
エラーの意味が…
-
Cのdoubleの浮動小数点表示につ...
-
Cプログラムについて
-
C言語 関数
-
CTRL+Dでループを抜けるには
-
hit&bolwのプログラミングがで...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
10個出力で改行したいのですが...
-
既約分数の表示プログラム
-
printf で二進表示を行いたい。
-
8人分のテストの点数を入力し、...
-
printf( " %2d", p * q );
-
strcmp
-
CTRL+Dでループを抜けるには
-
4の倍数を論理演算で表す。。
-
%P と %X の違い
-
【C言語教えてください】sin波...
-
c言語でAからZまでを表示する...
-
cshの文字列操作(0埋め)
-
万年カレンダーのC言語プログラ...
-
コマンドラインに出力した文字...
-
scanfに文字が入力されたときに...
-
コンパイルエラーについて
-
ホームページをC言語で作りたい...
-
改行について 1行に何個かづ...
-
台形の面積を求めるプログラム
-
なぜgccはstdio.hをインクルー...
おすすめ情報