C言語/基礎知識

出典: フリー教科書『ウィキブックス(Wikibooks)』

main関数[編集]

main 関数は、C言語プログラムのエントリーポイントとして機能します。エントリーポイントとは、プログラムの実行が開始される地点であり、プログラム内の最初の関数です。プログラムが実行されると、まずこの関数が呼び出されます。ここでは、main 関数の基本的な説明を行います。

main 関数の基本形[編集]

int main(void) {
    /* プログラムの処理 */
    return 0;
}

main 関数の役割[編集]

  • プログラムの実行が開始される地点であり、プログラム内の最初の関数です。
  • プログラムの動作や処理を定義します。main 関数内に書かれたコードが実行されます。
  • main 関数が終了すると、プログラムも終了します。通常、return 0;main 関数を終了させます。これはプログラムが正常終了したことを示します。

パラメータのない main 関数[編集]

  • パラメータを持たない場合、int main(void) と書きます。この形式は、プログラムがコマンドライン引数を受け取らない場合に使用します。

main 関数の戻り値[編集]

  • main 関数の戻り値は int 型でなければなりません。通常、正常終了を示すために 0 を返しますが、他の値を返すこともできます。

コマンドライン引数を受け取る main 関数[編集]

int main(int argc, char *argv[]) {
    /* プログラムの処理 */
    return 0;
}
  • main 関数のパラメータ argc は、プログラムに渡されたコマンドライン引数の数を表します。
  • main 関数のパラメータ argv は、コマンドライン引数が格納された文字列配列です。

サンプルコード[編集]

以下は、main 関数の基本形を示したサンプルコードです。

#include <stdio.h>

int main(void) {
    printf("Hello, world!\n"); // 標準出力に文字列を出力
    return 0; // 正常終了を示す
}

このコードでは、"Hello, world!" という文字列を標準出力に出力し、その後プログラムを正常終了させます。

データ型と変数の基本[編集]

変数は、実行環境にあるデータの保存領域で、その内容は値を表すことができます[1]

変数の宣言[編集]

C言語では、変数を使用するまえに宣言する必要があります。

#include <stdio.h>

int main(void) {
  int a = 3;

  printf("この数は %d", a);
}
実行結果
この数は 3

というプログラムで考えてみましょう。

int a = 3; は、a という名前で型として int をもつ変数を宣言し 3 で初期化しています。

また、変数を表示する際には、printf("%d", a);のように引用符内で%dなどによって表示方法を指定しなければなりません。%dとは「十進数として表示しろ」という意味です。

変数名は識別子の一種[編集]

識別子(identifiers)は、様々な物の中からある特定のモノ同士が同じこと(あるいは違うこと)の手がかりにするための名前です。 C言語では、変数名も識別子の一種です(ほかに、関数名、構造体タグ・共用体・列挙体のタグ名とメンバー名、ラベル名、typedef名なども識別子です)。 識別子は、「アンダースコア _・英数字・英数字以外の文字」の組合わせで作られますが、先頭の一文字には アンダースコア _ と数字は使えず、英数字以外の文字は処理系によって使えない可能性があります。 識別子の大文字小文字は区別されます。

識別子に使えない単語[編集]

識別子には、intif などのキーワードや、printfboolなどの予約済み識別子も識別子に使えません。

日本語の単語をローマ字表記した識別子は使わない

外部に公開しない識別子はあえて短めにし、一時的な識別子であることを伝えようとする傾向がありますが、コーディング規約で定めていない限りリラックスした名付けが行われます。

ただし、日本語の単語をローマ字表記した識別子は単数複数の区別がないので int item = items[0]の様な名付けが行えなかったり、のつもりで tsuki と綴ったら tuki が正しかったなど、日本語のラテン語文字翻字のゆらぎでミスタイプとなる可能性を増やすので推奨しません(月、の場合 month なのか moon なのかの間にも曖昧さが生じます)。


キーワード[編集]

C言語で文法定義で既に使用し識別子に使用することが禁止されている単語のをキーワード(Keywords)といいます。

今後のページの単元では、変数をさらに発展させた「構造体」「共用体」「列挙体」などの新しいデータ構造が出てきますが、それら新しく習うデータ構造でも同様に、そのデータ構造の全体や部分につける名称も識別子なのでキーワードは使えません。

異なる型の値での初期化[編集]

literal-conversion.c
#include <stdio.h>

int main(void) {
  int a = 3.45;

  printf("%d\n", a);
}
コンパイル&実行
% clang literal-conversion.c -o literal-conversion
literal-conversion.c:4:11: warning: implicit conversion from 'double' to 'int' changes value from 3.45 to 3 [-Wliteral-conversion]
  int a = 3.45;
      ~   ^~~~
1 warning generated.
% ./literal-conversion
3
%

のように、整数を宣言した変数を浮動小数点リテラルで初期化してもコンパイルエラーにはなりません[2]

代入[編集]

初期化を伴わず宣言された自動変数の値は不定( indeterminate )です[3]。このため、参照する前に値を決定する必要があります。

変数の宣言で初期化を行わず、代入で値を与えた例
  int a;
  a = 3;

というように、2つの文に分けて記述することもできます[4]

変数の値を非可逆的に変更することを代入と呼び、典型的には代入演算子 = の左辺値式に変数・右辺に式の値を与えるケースを想起しますが、左辺値式にはポインタによる参照や配列要素の参照、構造体や共用体のメンバーの参照などがあります[5]

#include <stdio.h>

int main(void) {
  int a = 3;
  int next = 8;
  printf("%d\n", next);
}
実行結果
8

複数の変数の表示[編集]

複数の変数の表示
#include <stdio.h>

int main(void) {
  int a = 3;
  int b = 7;
  printf("変数aは%dです。変数bは%dです。\n", a, b);
}
実行結果
変数aは3です。変数bは7です。
printfの出力順序欄にあるaとbの順序を入れ替え
#include <stdio.h>

int main(void) {
  int a = 3;
  int b = 7;
  printf("さいしょの変数は%dです。つぎの変数は%dです。\n", b, a);
}
実行結果
さいしょの変数は7です。つぎの変数は3です。

出力結果も入れ替わります。

代入式の右辺と左辺[編集]

下記の式の a = a + 1のように、右辺と左辺に同じ変数が含むことも可能です。 =はC言語では、(if文などの条件式の中であっても)等式では無く代入でことに注意してください。

#include <stdio.h>

int main(void) {
  int a;      // int型のaという名前の変数を「宣言」します。
  a = 0;      // aに0を「代入」します。
  printf("いまの変数は%dです。\n", a);
  a = a + 1;  // aの値を「参照」してそれに1を加えたものをaに「代入」する
  printf("この時点の変数は%dです。\n", a);
}
実行結果
いまの変数は0です。
この時点の変数は1です。

まとめ[編集]

変数(へんすう)とは名前をもったデータを格納しておく領域のことです。 変数は、一般にメモリー上に確保され、値を代入したり参照したりすることができます。 変数は、型を持ちます。

さらに詳しい説明[編集]

変数の宣言[編集]

変数を使用するには前もって宣言をする必要があります。

int a;	// int型のaという名前の変数を「宣言」します。

int は変数のデータ型、a は変数名です。

変数のデータ型とは、メモリー上に確保する領域の大きさや、確保した領域の扱い方などを決定するものである(型指定子)。データ型は扱いたいデータの種類や値の範囲によって決定します。 「変数名」とはその変数を他の変数と区別するために付ける名前のことで、 変数名に使えるのは、半角英数の小文字と大文字、および_(下線、アンダーライン) です。

また、変数名に int は使えません。intreturn などのキーワードは変数名や関数名など利用できません。

キーワード ≠ 予約語
プログラミング言語によっては、予約語(reserved word)を言語仕様で定義されている場合もあります。

例えばRustでは、 typeof は予約語の1つで、現在(2021年12月)の Rust の文法では typeof は使われていませんが、プログラムの中で変数名などの識別子には使用できません。

2021年12月時点で現行の標準C言語仕様である ISO / IEC 9899:2018では、

キーワード
「§6.4.1 Keywords」で定義
予約済み識別子
「§7.1.3 Reserved identifiers」で定義
予約語(Reserved word)
定義は存在せず、言及もなし

このため『識別子に使えない単語』は、C言語では「キーワードまたは予約済み識別子」と読み替えて良さそうです。 また、「予約語は、Keywords の日本語訳」は(C言語では)明確に間違えだと言えます。

  • if はキーワードです ⇒ True
  • if は予約済み識別子です ⇒ False
  • if は識別子に使えます ⇒ False
  • printf はキーワードです ⇒ False
  • printf は予約済み識別子です ⇒ True
  • printf は識別子に使えます ⇒ False
  • abc はキーワードです ⇒ False
  • abc は予約済み識別子です ⇒ False
  • abc は識別子に使えます ⇒ True


キーワードの一覧[6]

[7]

なお、printfは標準ライブラリーの関数でキーワードではありませんが予約済み識別子で、もしグローバルな変数名に用いてしまうと重複定義となり、リンクに失敗します。

代表的な型
データ型の種類 データ型 データ型の名称 大きさ[bit] 扱える値の範囲
整数型 int 整数型 32 -2147483648 ~ 2147483647
浮動小数点型 float 単精度浮動小数点型 32

最小の正の数1.175494351e-38、 最大値3.402823466e+38

double 倍精度浮動小数点型 64 最小の正の数 2.2250738585072014e-308、 最大値 1.7976931348623158e+308
文字型 char 文字型 8

※ 大きさと扱える値の範囲は実装依存なので、上記の数値は一例です。

C言語では、文字を保持するための文字型 char があります。文字列は文字型の配列の '\0' で終端された特殊なケースです。

コメント[編集]

C言語におけるコメントは、プログラム内でコードの説明や一時的なコードの無効化に使用されます。C言語では、2つの主要なコメントスタイルがあります。

  1. 単一行コメント: このスタイルのコメントは、// で始まり、行の終わりまで続きます。これはC99からの機能です。
    // これは単一行コメントです。この行の終わりまでがコメントです。
    int x = 10; // コードとコメントを同じ行に書くこともできます
    
  2. 複数行コメント: このスタイルのコメントは、/* で始まり、*/ で終わります。これは複数行にまたがるコメントを作成するために使用されます。
    /*
    これは
    複数行
    コメントです。
    */
    int y = 20;
    

C言語のコメントは、プログラムの可読性を向上させるために重要です。適切にコメントを追加することで、コードの動作や目的を理解しやすくなり、他の開発者とのコラボレーションもスムーズになります。ただし、コメントが過度に使われるとコードの可読性が低下する可能性があるため、コメントの適切な使用には注意が必要です。

C言語のコメントについてさらに詳しく説明します。

コメントの使い方
  • コメントは、プログラム内で説明やメモを残すために使用されます。
  • コメントは、コード内の特定の行やブロックに追加することができます。
  • コメントを追加することで、コードの意図や機能を他の開発者や自分自身に伝えることができます。
コメントの例
  • 変数や関数の目的や使用法を説明するコメントを追加します。
// x はユーザーの入力を格納する変数です
int x;
  • 特定の処理やアルゴリズムの説明を追加します。
// 2つの数値を加算する関数
int add(int a, int b) {
    return a + b;
}
  • コードの一時的な無効化やデバッグ目的で一時的にコードを無視するためにコメントを使用します。
// printf("Hello, world!\n");
  • コードのセクションや機能を明確にするために、区切りコメントを使用します。
// ************************************************************
// このセクションはファイルの読み込みと処理に関するものです
// ************************************************************

コメントは、コードの理解と保守性を向上させるために重要な役割を果たします。適切に使われると、コメントはプログラムのドキュメントとして機能し、他の開発者や将来の自分自身がコードを理解しやすくします。

Doxygen
Doxygenは、C言語のための広く使用されているドキュメント生成ツールです。特定のコメント形式に従ってコメントを書くことで、Doxygenはそれらを解析し、自動的にソースコードのドキュメントを生成します。

例えば、次のような形式のコメントを使います:

/**
 * @brief この関数は二つの数値を加算します。
 * @param a 加算する最初の数値
 * @param b 加算する二番目の数値
 * @return 加算結果
 */
int add(int a, int b) {
    return a + b;
}

このようなコメントスタイルを使うと、Doxygenは関数や変数の説明、引数や戻り値の情報などを抽出してドキュメントを生成します。


式と演算子[編集]

C言語における式と演算子は、プログラムでデータを操作するための基本的な構成要素です。以下では、C言語における式と演算子について説明します。

式 (Expressions):[編集]

  • 定義: 式は、変数、定数、演算子、関数呼び出しの組み合わせであり、計算結果を生成します。
  • 例: x + ya * b - cfunc(10) などが式です。

演算子 (Operators):[編集]

C言語にはさまざまなタイプの演算子があります。主な演算子の種類を以下に示します。

  1. 算術演算子 (Arithmetic Operators):
    • 加算 (+), 減算 (-), 乗算 (*), 除算 (/), 剰余 (%) など。
    • 例: x + y, a - b, c * d, e / f, g % h
  2. 関係演算子 (Relational Operators):
    • 等しい (==), 等しくない (!=), より大きい (>), より小さい (<), 以上 (>=), 以下 (<=) など。
    • 例: x == y, a != b, c > d, e < f, g >= h, i <= j
  3. 論理演算子 (Logical Operators):
    • 論理積 (&&), 論理和 (||), 否定 (!) など。
    • 例: x && y, a || b, !c
  4. ビット演算子 (Bitwise Operators):
    • ビット毎のAND (&), OR (|), XOR (^), 左シフト (<<), 右シフト (>>) など。
    • 例: x & y, a | b, c ^ d, e << 1, f >> 2
  5. 代入演算子 (Assignment Operators):
    • 代入 (=), 複合代入 (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=) など。
    • 例: x = y, a += b, c -= d, e *= f, g /= h, i %= j
  6. その他の演算子:
    • 条件演算子 (?:), アドレス演算子 (&), 間接参照演算子 (*), サイズ演算子 (sizeof) など。

これらの演算子を組み合わせることで、複雑な式を作成し、プログラムの振る舞いを制御することができます。演算子には優先順位と結合性があり、これらを理解することは正確な式の評価に重要です。

算術演算子と代入演算子[編集]

C言語における算術演算子と代入演算子について説明します。

算術演算子 (Arithmetic Operators):[編集]

算術演算子は、数値の計算に使用されます。主な算術演算子は以下の通りです:

  • 加算 (+): 2つの数値を加算します。
  • 減算 (-): 左辺の数値から右辺の数値を減算します。
  • 乗算 (*): 2つの数値を乗算します。
  • 除算 (/): 左辺の数値を右辺の数値で除算します。
  • 剰余 (%): 左辺の数値を右辺の数値で除算し、剰余を返します。
int x = 10;
int y = 3;
int sum = x + y;     // sumには13が代入される
int difference = x - y;  // differenceには7が代入される
int product = x * y;     // productには30が代入される
int quotient = x / y;    // quotientには3が代入される
int remainder = x % y;   // remainderには1が代入される

代入演算子 (Assignment Operators):[編集]

代入演算子は、変数に値を代入するために使用されます。代入演算子は、算術演算子と組み合わせて複合代入演算子として使用することもできます。

  • 代入 (=): 右辺の値を左辺の変数に代入します。
  • 複合代入 (+=, -=, *=, /=, %=): 左辺の変数と右辺の値との演算結果を左辺の変数に代入します。

 :

int x = 10;
int y = 5;
x += y;     // xには15が代入される (x = x + y と同じ)
y *= 2;     // yには10が代入される (y = y * 2 と同じ)

算術演算子と代入演算子を組み合わせることで、変数の値を操作して計算することができます。 これらの演算子は、C言語で数値計算を行う際に広く使用されます。


算術演算子と代入演算子
演算子の種類 演算子 演算子の名称 意味
単項演算子 - 単項-演算子 右オペランドの符号を反転した値
乗除演算子 * 2項*演算子 左右オペランドの積
/ 2項/演算子 左オペランドを右オペランドで除した商
右オペランドの値は0以外でなければならません。
左右オペランドが整数型の場合、商の小数部は切り捨てられます。
% 2項%演算子 左オペランドを右オペランドで除した剰余
右オペランドの値は0以外でなければならません。
左右オペランドは整数型でなければならません。
加減演算子 + 2項+演算子 左右オペランドの和
- 2項-演算子 左オペランドから右オペランドを引いた差
代入演算子 = 単純代入演算子 左オペランドが指す変数に右オペランドの値を格納する

※ この表の演算子の種類は、演算子の優先順位が高い順番に並んでいます。

整数どうしの割り算[編集]

整数どうしの割り算
#include <stdio.h>

int main(void) {
  int a = 8, b = 5;
  double c = a / b;

  printf("計算結果は%f \n", c);
}
実行結果
計算結果は1.000000

と表示され、「1.600000」とは表示されません。

これは、整数どうしの割り算の結果は整数型なるので、端数は切り捨てられたからです(なお、プログラム中の「計算結果は」の後ろは「%f」(浮動小数点)にすることに注意してください)。

除算の片方の項をキャストで double に昇格
#include <stdio.h>

int main(void) {
  int a = 8, b = 5;
  double c = (double)a / b;

  printf("計算結果は%f \n", c);
}
実行結果
計算結果は1.600000

除算にあたって、「(double)」のように明示的に型変換する(キャストする)ことにより、他方の項も暗黙的に double に昇格させ、整数演算ではなく浮動小数点数演算を行わせています[8]

 double c = a / (double)b;

としても、同じように浮動小数点数として除算が行われます(型昇格の規則)。

参照[編集]

変数に格納されたの値を使用することを変数の値の参照と呼びます。

a = a + 1;	// aの値を「参照」してそれに1を加えたものをaに「代入」する

参照する際、変数があらかじめ初期化や代入されているよう注意してください。未初期化の変数の値は不定です。

識別子の長さとスコープ
for (int i = 0; i < 10; i++{...}

の i のように極めて短いスコープを持つ変数に short_loop_counter の様な冗長な名前を付けるのは滑稽です。

この様に、識別子の長さはスコープと関連付けて考えるべきです。 また、C言語にはC++の様な namespace が無いので名前の衝突を避ける工夫が必要です。 この事から、一貫した命名規約が1つのプログラムの中で貫かれているべきで、長さよりも一貫性が大事になります。


定数[編集]

(ここで言う定数はキーワード const によってもたらされたReadOnly属性のことではなく、リテラルのことです。一般にリテラルは定数には含まれます。むしろ無引数マクロを定数というケースがC言語では主流でした。)

定数とは、プログラム実行時に一定の値しかもたない数です。 変数の値がプログラム実行中に変更される場合もあるのに対して、定数の値はプログラム実行中を通して一定です。 ソースコード中で直接に記述された定数を特にリテラルとも呼ぶ。 ここではそのリテラルについて説明します。

整数定数を使ったプログラムの例
int main(void) {
  int a;
  a = 0;      // 整数定数0をiに代入します。
  a = a + 1;  // aの値に整数定数1を加える。
}

なお、これは画面に何も表示することなく、ただちに終了するプログラムです。 このソースコード中の「0」や「1」が定数です。

定数には整数定数、浮動小数点定数、文字定数、文字列定数、などがあります。 整数定数とは整数を記述するための定数で、主に10進数表記が使われます。 浮動小数点定数とは浮動小数点数を記述するための定数で、主に10進数の小数点数表記で記述します。 文字定数とは1バイト文字を記述するための定数で、文字を「'(一重引用符)」で囲む。 文字列定数とは1バイト文字または多バイト文字の文字列を記述するための定数で、文字列を「"(二重引用符)」で囲む。

リテラル表現
定数の種類 進数 記法
整数定数 10進数 10進数 1234
浮動小数点定数 10進数 10進整数部と「.」と10進小数部 3.14
文字定数 - 「'(一重引用符)」で囲まれた文字 'a'
文字列定数 - 「"(二重引用符)」で囲まれた文字列 "Hello, World!"

標準ライブラリー[編集]

標準ライブラリーは、プログラミングで頻繁に使用される基本的な機能を提供します。代表的な機能には、入出力 (stdio.h)、文字列操作 (string.h)、数学関数 (math.h) などがあります。

"stdio" は "standard input-output" の略であり、日本語では標準入出力を意味します。入出力はプログラミングにおいて基本的であり、stdio.hの中でも特に printf 関数と scanf 関数の使い方について説明します。

前処理指令[編集]

前処理は、翻訳単位の翻訳の前に行われる処理であり、前処理指令は # で始まる行や Pragma 演算子を指します。標準ライブラリーを使用するためには、適切な標準ヘッダーをインクルードする必要があります。

ヘッダーは、型定義や関数宣言やマクロ定義などをひとまとめにし、 ソースファイルやほかのヘッダーでインクルードして使用します。

標準ライブラリーを使用するためには、使用する標準ライブラリーの機能に応じた標準ヘッダーをインクルードする必要があります。 ヘッダーを組み込むには前処理指令の内の1つ #include指令を用います。 printf関数及びscanf関数を使用するためには、stdio.hというヘッダーを組み込む必要があります。

stdio.hをインクルード
#include <stdio.h>

ヘッダーをインクルードする1つの形式

#include "my_header.h"

は、ユーザー定義のヘッダーの include に使うものです。

標準ヘッダーは必ず < ... > でヘッダー名を囲む形式にします。

標準ヘッダーを " ... " で取り込むとユーザヘッダーの検索パスに標準ヘッダーと同じ名前(例: stdio.h)の偽装されたヘッダーを置かれた場合に偽装されたヘッダーを使ってしまうというセキュリティホールになります。標準ヘッダーは通常はユーザが書き換えできないパーミッションが付与されていますが、ユーザヘッダーの検索パスへのに書き込みパーミッションは付与されている可能性があります。


printf関数[編集]

printf 関数は、書式付けされた文字列を標準出力に書き込む関数です。書式付け文字列には、変換指定子が含まれ、これは後続の引数に対応します。

printf関数の宣言部分
int printf(const char * restrict format, ...);

format には、それに続く任意個数の実引数と同じ数だけ(%d のような)変換指定子( conversion specifier )[9]が含まれなれなければなりません[10]。 書式化文字列に含まれる変換指定子の部分が、それに対応する後の実引数の値によって置換されます。

変換指定子は、実引数のデータ型に応じて、主に以下のようになる。

*printf関数の変換指定子
実引数のデータ型 変換指定子
int
(整数)
%d
double
(浮動小数点数)
%f
char
(文字)
%c
char*
(文字列)
%s

エスケープシーケンス[編集]

書式付きの文字列定数には、エスケープシーケンス(Escape sequence; 逆斜線表記)を含めることもできます。逆斜線表記という名前ですが、OSによっては、斜線ではなく、いわゆる円マークが表示される場合もありますが、それで正常です[11]

逆斜線表記とは、\に文字が続くことで特別な意味を表すものです。

エスケープシーケンスとその意味
エスケープシーケンス 意味
\n 改行(New line)
現在の印字位置を次の行の先頭位置に移動する
\t 水平タブ(horizontal Tab)
次の水平タブ位置に移動する
\' シングルクォーテーション(single quotation mark)
一重引用符
\" ダブルクォーテーション(double quotation mark)
二重引用符
\\ 円記号(\)

なお、数値表示フォーマットを意味する文字列 %d などを(代入ではなく)そのまま表示したい際のエスケープは、\ではなく%です。なので、たとえばprintf("%%dを表示したい");を実行すれば「%dを表示したい」と表示されます。

printf関数の使用例[編集]

printf関数を用いて変数を出力に書き込む
#include <stdio.h>

int main(void) {
  int i = 1234;
  printf("iの値は%d\n", i);  //「iの値は1234(改行)」と出力します。
  double d = 3.14;
  printf("dの値は%f\n", d);  //「dの値は3.14(改行)」と出力します。
  char c = 'a';
  printf("cの値は%c\n", c);  //「cの値はa(改行)」と出力します。
  char str[] = "Hello, World!";
  printf("strの値は%s\n", str);  //「strの値はHello, World!(改行)」と出力します。
}

scanf関数[編集]

scanf関数を使うことにより、標準入力(既定値はキーボード)から入力した値を読み取らせることができます。

#include <stdio.h>

int main(void) {
  int input = -1; // scanf() が失敗した場合の対策
  printf("整数を入力してください。\n");
  if (scanf("%d", &input) == EOF) {
      printf("End of File に達しました。\n");
      return 1;
  }
  printf("入力された整数は%dです。 \n", input);
}

これを実行すると、まず

整数を入力してください。

と表示されます。

そして、カーソルが点滅するので、そこに整数を入れて、エンターキー(リターンキー)を押す。 たとえば整数 73 を入れると、

整数を入力してください。
73
入力された整数は73です。

と表示されます。

scanfの使用では、「&input」のように、変数名の前に「&」をつける必要があります。なお、この「&」記号は、記憶領域のアドレスという意味を表す。

アドレスについては、高度な説明になるので、ほかのページで後述します。

「なんで変数へのキーボードからの入力のさい、アドレスというものを使うのか?」という疑問には、

左辺値式を関数の引数に渡す方法が他にない。

という答えになります。

左辺値式は代入の左辺になりうる式の事で、a = 1aが左辺値式です。 他方、関数の引数は値渡しされるので、変数の値を渡すことはできますが、仮引数を変更して元々の変数の値は変わりません。 このため、アドレスを引数として渡し関数の中で間接参照演算子を使い値を書き変えています。

#include <stdio.h>

void by_value(int i) { i = 0; }
void by_address(int *i) { *i = 100; }

int main(void) {
  int i = 10;
  printf("i = %d\n", i);
  by_value(i);
  printf("by_value(i); i = %d\n", i);
  by_address(&i);
  printf("by_address(&i); i = %d\n", i);
}

結果

i = 10
by_value(i); i = 10
by_address(&i); i = 100

scanf関数の使用例[編集]

//例 scanf関数を用いて入力を変数に読み込む。
#include <stdio.h>

int main(void)
{
  int i;
  if (scanf("%d", &i) == EOF) {  //整数入力をiに格納します。
      printf("End of File に達しました。\n");
      return 1;
  }
  double d;
  if (scanf("%f", &d) == EOF) {  //浮動小数点数入力をdに格納します。
      printf("End of File に達しました。\n");
      return 1;
  }
  char c;
  if (scanf(" %c", &c) == EOF) {  //文字入力をcに格納します。
      printf("End of File に達しました。\n");
      return 1; 
  }
  char str[32];
  if (scanf("%31s", str) == EOF) {  //文字列入力をstrに格納します。
      printf("End of File に達しました。\n");
      return 1; 
  }
}

scanf関数の実用には様々な問題を解決する必要があるが、ここではこれ以上説明しません。

scanfのくわしい説明[編集]

scanf 関数は、標準入力からの入力を受け取り、指定された変数に格納します。 変換指定子を使用して、入力された値のデータ型を指定します。

scanf関数の宣言部分
int scanf(const char * restrict format, ...);

format には、それに続く任意個数の実引数と同じ数だけの変換指定子が含まれなければなりません。 標準入力からの入力が、書式化文字列に含まれる変換指定子に従って、それに対応する後の実引数が指す変数に代入されます。

*scanf関数の変換指定子
実引数のデータ型 変換指定子
int 整数 %d
double 浮動小数点数 %f
char 文字 %c
char* 文字列 %s

変換指定子に対応する実引数は、単項&演算子(アドレス参照演算子)を前置します。

&オブジェクト

これはscanf関数にアドレスを渡すことでオブジェクトへの代入を可能にするためです。

脚註[編集]

  1. ^ C言語の仕様書では変数(Variables)についての定義はなく、かわって§3.15 「オブジェクトは、実行環境にあるデータの保存領域で、その内容は値を表すことができる。」とオブジェクトという名前で登場しますが、オブジェクト指向プログラミングでのオブジェクトと混同しやすいのと、変数という呼称が浸透しているので、本書では「実行環境にあるデータの保存領域で、その内容は値を表すことができる。」存在を変数と称します。
  2. ^ 過去の編集で「コンパイルでエラーとなる」とされていましたが、実際は規格的にも実装もエラーとはなりません。上記のように、clang-14.0.4 は警告しましたが、gcc-12.1.1 は(-Wall をつけても)警告しませんでした。
  3. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 30, §6.2.4 Storage durations of objects. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. "If an initialization is specified for the object, it is performed each time the declaration or compound literal is reached in the execution of the block; otherwise, the value becomes indeterminate each time the declaration is reached.(仮訳:オブジェクトに初期化が指定されている場合、ブロックの実行中に宣言または複合リテラルに到達するたびに初期化が実行され、そうでない場合は宣言に到達するたびに値が不定になります。)" 
  4. ^ 宣言と同時に初期化することで「未初期化変数の参照」という厄介な問題が避けられるので、宣言と同時に初期化することを強く推奨します。
  5. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 40, §6.3.2.1 Lvalues, arrays, and function designators. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  6. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 42, §6.4.1 Keywords. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  7. ^ 7.00 7.01 7.02 7.03 7.04 7.05 7.06 7.07 7.08 7.09 7.10 7.11 7.12 7.13 7.14 7.15 7.16 7.17 7.18 7.19 7.20 N3054 working draft — September 3, 2022 ISO/IEC 9899:2023 (E). ISO/IEC JTC1/SC22/WG14. p. 53, §6.4.1 Keywords. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3054.pdf. 
  8. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 39, §6.3.1.8 Usual arithmetic conversions. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  9. ^ N2176 C17 ballot ISO/IEC 9899:2017. ISO/IEC JTC1/SC22/WG14. p. 225, §7.21.6.1 The fprintf function. オリジナルの2018-12-30時点によるアーカイブ。. https://web.archive.org/web/20181230041359/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf. 
  10. ^ 以前の編集で、書式化文字列を書式付の文字列定数としていましたが、定数(リテラル)である必要はありません。
  11. ^ 逆斜線表記という名前ですが、皆様の画面では \ ではなく \ と表示されていると思います。それで正常です。

参考文献[編集]