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

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

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

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

Q&A

解決済

4回答

1878閲覧

c言語 ファイル出力

mightyMask

総合スコア143

C

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

2グッド

0クリップ

投稿2017/06/10 00:15

putchar() や fputc() といった関数は 非負整数1バイトを出力するわけですが、なぜ引数が int なのでしょうか。

tkanda, yohhoy👍を押しています

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

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

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

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

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

guest

回答4

0

C言語の歴史を遡っていくと、かつてはプロトタイプ宣言がありませんでした。そして、そういうプロトタイプのない関数へ渡す引数は、int未満であれば自動でintに変換される、という既定の実引数拡張が行われていました。つまり。charを渡してもintを渡したのと同じことになっていました。

その後、プロトタイプ宣言ができたのですが、従来通りの挙動(intを渡しても問題なく動く)とするために、仮引数の型はcharではなくintとなっている、ということのようです。

その辺の事情について

投稿2017/06/10 00:37

maisumakun

総合スコア145184

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

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

0

ベストアンサー

maisumakunさんがおっしゃっている通り、これは遺産/歴史によるものです。
C90標準の前には関数のプロトタイプ宣言はなく、全ての関数の全ての引数は Default promotion rules(既定の実引数拡張)の対象だったため、char は自動的に int として渡されました(shortはintに、floatはdoubleにというように)。標準は、既存のコードとの互換性を保つために、これらの関数の引数型をそのまま残しています。
この場合、引数型は int ですから、Integer promotionsというルールに則っています。

Integer promotions についての記述は §6.3.1 にあります。

¶2 The following may be used in an expression wherever an int or unsigned int may be used:

An object or expression with an integer type (other than int or unsigned int) whose integer conversion rank is less than or equal to the rank of int and unsigned int.

A bit-field of type _Bool, int, signed int, or unsigned int.
If an int can represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions.58) All other types are unchanged by the integer promotions.

¶3 The integer promotions preserve value including sign. As discussed earlier, whether a 'plain' char is treated as signed is implementation-defined.

  1. The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.

投稿2017/06/10 05:32

kjfkhfhgx

総合スコア48

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

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

0

これは、メモリー上の変数の場所(アドレス)がint型のサイズ(2バイト/4バイト)に整列されるという、C言語の仕様によるものです。これはバイトアライメントと呼ばれる仕様で、PCのメモリーアーキテクチャに深く依存した話になります。

(参考)データ型のアラインメントとは何か,なぜ必要なのか?

関数呼び出しの際、引数となる変数はスタックメモリー上に並べて配置され、関数側に制御が渡されることになります。この時、スタックメモリー上にアロケートされる引数の領域もまたバイトアライメントの原則に従う必要があります。(あなたが従うのではなく、Cコンパイラーがそのようにコードを生成する必要がある、という意味です。)C言語仕様にはこのことが関数引数のint型への暗黙的変換として定義されています。

変数領域のアライメントについては、以下のようなプログラムを実行してみると、確認することができます。

C

1#include <stdio.h> 2void f(int i, char c); 3int I1 = 0; 4char C1 = 0; 5int I2 = 0; 6char C2 = 0; 7 8int main() { 9 f(1, 2); 10 return 0; 11} 12 13void f(int argi, char argc) { 14 int i1 = 0; 15 char c1 = 0; 16 int i2 = 0; 17 char c2 = 0; 18 19 printf("&I1 = 0x%lx, &C1 = 0x%lx, &I2 = 0x%lx, &C2 = 0x%lx\n", &I1, &C1, &I2, &C2); 20 printf("&i1 = 0x%lx, &c1 = 0x%lx, &i2 = 0x%lx, &c2 = 0x%lx\n", &i1, &c1, &i2, &c2); 21 printf("&argc = 0x%lx, &argi = 0x%lx\n", &argc, &argi); 22} 23 24/* 25 * 外部変数、ローカル変数、引数変数のすべてのアドレスが4の倍数になっていることが確認できます。 26 * これはつまり、C1やc1、argcなどの変数は1バイトの領域しか使用しないにもかかわらず、 27 * 隣の変数の領域との間を3バイト分のパディング領域で埋めることでバイトアライメント 28 * の原則が維持されていることを示しています。 29 */

さて、上記のように char 型の引数は暗黙的に int 型として処理されているわけですが、ではなぜ putchar() や fputc() の引数が int 宣言されているのでしょうか。暗黙的な型変換が自動的に行われるのであれば、これらの標準関数のプロトタイプも putchar(char c) で良いのでは?ということになりますね。

これに関しては、C言語の古い仕様(C90よりも前の仕様)では、プロトタイプ宣言という機能が無かったことと、大昔のC言語はint型が2バイト長だったことが関係していると言われています。

プロトタイプ宣言機能が無かった頃、putchar()やfputc()の引数に char ではなく、int を渡すようなコードが多数存在していました。これは、当時のプログラマーがPCのメモリーアーキテクチャやC言語のバイトアライメントに関する仕様に対して、熟知しているがゆえに、暗黙的な型変換の仕組みを「気持ち悪い」と感じていたことと、1バイトの文字を保持するのに int 型を使うことに違和感を感じなかったからではないかと想像できます。

標準関数のプロトタイプが決められる過程で、既存のコードに char 型変数とに暗黙の型変換を強制するよりも、バイトアライメントの原則に従い、int型の引数とするほうが、より自然であると判断されたのには、このような背景があったのではないでしょうか。

ご参考になれば。

投稿2017/06/10 01:54

tkanda

総合スコア2425

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

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

tkanda

2017/06/10 04:35

提示していただいたスレッドにもありますが、初期のC言語(K&R)からchar型の引数や戻り値はなかったんですね。何故なかったかというとそれはバイトアライメントのルールに由来しているということでしょうから、関係ない話ではない気がしますが。
yohhoy

2017/06/10 04:38

int型が該当アーキテクチャ上の"自然な"データサイズであることは同意しますが、バイトアライメント(正確にはアライメント要件)と絡める説明が妥当か否かはわかりませんね。まあ、いずれも推測にすぎないので強い反対意見ではありません。(個人的に気になったという程度です)
tkanda

2017/06/10 05:08

あ、そうですね。すべてのアドレスが4の倍数になるとは限りません。4バイトの境界をまたがないように整列されるというのが正しいですね。1バイトの変数のアロケーションは [data]+[3bytes padding]となる実装と、[3bytes padding]+[data]となる実装があります。後者の場合1バイト変数のアドレスは4バイト境界+3のアドレスになりますね。
raccy

2017/06/10 05:22

境界をまたがないという意味がよくわかりません。4バイトの枠の中に一つだけ変数が確保されるという意味ですか?そうであれば、変数4つが4バイトの中に収まっているように見える下記のコードについて、3バイトのパディングはどこに行ったのでしょうか? https://wandbox.org/permlink/pXdVdArrvPjsRdJp
guest

0

EOF を表す為です。unsigned char の 0~255 では -1 は表現できないですからね。

投稿2017/06/10 03:56

編集2017/06/10 03:57
mattn

総合スコア5030

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

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

maisumakun

2017/06/10 04:06

取得はともかく、EOFを「出力」することはないのではないでしょうか。
mattn

2017/06/10 04:14

成功すると引数そのままが返ります。失敗するとEOFが返ります。その実装を簡略化する為にそうなったと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問