プログラム設計の基本概念#
アルゴリズムの特性#
- 有限性:アルゴリズムは有限のステップで終了しなければならず、無限ループや死ループに入ってはいけない。
- 確定性:アルゴリズムの各ステップは明確に定義されており、曖昧さがあってはならない。同じ入力は同じ出力を生成する。
- 実行可能性:アルゴリズムの各ステップは実行可能であり、実現不可能な操作を含んではならない。
- ゼロ個以上の入力:アルゴリズムはゼロ個以上の入力パラメータを受け入れることができ、これらのパラメータは問題の入力データである。
- 一つ以上の出力:アルゴリズムは一つ以上の出力結果を生成し、これは問題の解決策である。
三つの基本構造#
三つの基本構造から成るアルゴリズムは、任意の複雑な問題を解決することができる。
- 順次構造:プログラム内の文は順番に実行され、上から下へ、各文は一度だけ実行される。
- 選択構造:選択構造は条件の真偽に基づいて異なるコードブロックを実行することを許可する。これには
if
文やswitch
文などが含まれる。例えば、if
文を使用して条件に基づいて異なるコードブロックを実行し、分岐ロジックを実現することができる。if (condition) { // コードブロック1 } else { // コードブロック2 }
- ループ構造:ループ構造は特定の条件が満たされるまで同じコードブロックを何度も実行することを許可する。これは
for
、while
、およびdo-while
などのループ文を使用して実現できる。while (condition) { // ループ実行のコード } for (int i = 0; i < n; i++) { // ループ実行のコード }
第 2 章 C プログラム設計の初歩的理解#
2.1 簡単な C 言語プログラムの構成と形式#
/*矩形の面積を求める*/
#include "stdio.h"
int main() {
double a, b, area;
a = 1.2; /*矩形の二つの辺の長さをそれぞれaとbに代入*/
b = 3.6;
area = a * b; /*矩形の面積を計算し、変数areaに保存*/
printf("a=%f,b=%f,area=%f\n", a, b, area);
}
- C 言語では、主関数名として main を使用する必要があり、実行可能な C プログラムには主関数が一つだけ存在しなければならない。
- C プログラム内の各実行文はセミコロン「;」で終了しなければならない。
- コメント内容は「/」と「/」の間に置かなければならず、コメントの間に「/」や「/」をネストすることはできない。
2.2 識別子、定数、変数#
2.2.1 識別子#
- アルファベット、数字、アンダースコアで構成され、最初の文字はアルファベットまたはアンダースコアでなければならない。
- 種類
- キーワード
- 事前定義された識別子
- ユーザー識別子
2.2.2 定数#
- プログラムの実行中にその値が変更できない量。
- 分類
- 整数定数:数字のみで表され、小数点を含まない。
- 短整数定数
- 長整数定数
- 実数定数:小数点を含む数。
- 文字定数
- 文字列定数
- 整数定数:数字のみで表され、小数点を含まない。
2.2.3 シンボル定数#
/*円の面積を計算*/
#include "stdio.h"
#define PI 3.14159 /*シンボル名PIを3.14159として定義*/
int main() {
double r, s;
r = 5.0;
s = PI * r * r;
printf("s=%f\n", s);
}
2.2.4 変数#
- 変数はプログラムの実行中にその値が変更できる量。
2.3 整数型データ#
2.3.1 整数定数#
- 整数定数は 10 進数、8 進数、16 進数などの形式で表すことができる。
- 8 進数:先頭は数字「0」でなければならない。
- 16 進数:先頭に「0x」または「0X」を使用する;16 進数の中の文字 a、b、c、d、e、f は小文字でも大文字でもよい。
- C プログラムでは、10 進数のみが負数であり、8 進数と 16 進数は整数のみである。
2.3.2 整数変数#
- 基本型の整数変数は型名キーワード int で定義される。
int k;
int i, j, k;
int i = 1, j = 0, k = 2;
2.3.3 整数データの分類#
- 短整数型(short int)
- 基本整数型(int)
- 長整数型(long int)
- 符号なし型(unsigned):符号なし整数は数の末尾に文字の接尾辞 u または U を付ける;長整数型の符号なし整数定数の場合は接尾辞 lu または LU を付ける。
変数が符号なし型として指定されていない場合、変数は暗黙的に符号あり型(signed)と見なされる。
2.3.4 整数のメモリ内の保存形式#
- 通常、1 バイトの最右ビットを最低位と呼び、最左ビットを最高位と呼ぶ。有符号整数の場合、最高位は整数の符号を格納するために使用され、符号ビットと呼ばれる。正の整数の場合、最高位には 0 が置かれ、負の整数の場合、最高位には 1 が置かれる。
- 負の整数はメモリ内に「補数」形式で保存される。
ある二進数の補数を取る手順は、例えば 10000101(10 進数 - 5)の補数を取る場合、以下のようになる:
- 原数の反数を求める。原数の符号ビットを除く二進数をビットごとに反転させ、11111010 を得る。
- 得られた反数に 1 を加え、原数の補数を得る。11111011 を得る。
メモリ内に補数形式で保存されている二進数を 10 進数の負の整数に変換する手順:
- 符号ビットを除く各ビットを反転させる。
- 得られた二進数を 10 進数に変換する。
- 求めた数から 1 を引く。
2.4 実数型データ#
2.4.1 実数定数#
- 小数形式:必ず小数点を含む。
2.4.2 実数変数#
- 単精度型(float)
- 定義:
float a,b,c;
- 4 バイトのストレージユニットを占める。
- 定義:
- 倍精度型(double)
- 定義:
double x,y,z;
- 8 バイトのストレージユニットを占める。
- 定義:
2.5 算術式#
2.5.1 基本的な算術演算子#
- 加算(+)
- 減算(-)
- 乗算(*)
- 除算(/)
- 剰余(%):演算対象は整数型のみ。
- これらの演算子は二つの演算対象を必要とし、二項演算子と呼ばれる;
- 「+」と「-」は単項演算子としても使用でき、演算子は演算数の左側に出現しなければならない。
- 二項演算子の両側の演算数の型が一致する場合、得られた結果の型は演算数の型と一致する。
- 二項演算子の両側の演算数の型が一致しない場合、システムは自動的に型変換を行い、演算子の両側の型が一致した後に演算を行う。
- C 言語では、すべての実数の演算は倍精度方式で行われる。
2.5.2 演算子の優先順位、結合性と算術式#
-
算術演算子の優先順位
-
算術演算子と丸括弧の結合性
- 上記の演算子の中で、単項演算子「+」と「-」の結合性は右から左であり、他の演算子の結合性はすべて左から右である。
-
算術式
- 定義:算術演算子と一対の丸括弧を用いて演算子(またはオペランド)を接続した、C 言語の文法に従った式。
- 演算対象は定数、変数、関数などである。
2.5.3 強制型変換式#
- 形式:
(型名)(式)
(型名)
は強制型変換演算子と呼ばれる。
2.6 代入式#
2.6.1 代入演算子と代入式#
- 形式:
変数名=式
- 代入演算子の左側には変数のみが許可され、定数や式は許可されない。
- C 言語では、最左側の変数に得られた新しい値が代入式の値となる。
2.6.2 複合代入式#
- 定義:代入演算子の前に他の演算子を加えること。
2.6.3 代入演算中の型変換#
- 代入演算子の両側のデータ型が一致しない場合、代入の前にシステムは自動的に右側の式で求めた数値を代入演算子の左側の変数の型に変換する。
- C 言語の式(代入式を除く)における変換規則
- 短整数型と長整数型 $ 短整数型 \to 長整数型 $
- 符号付き整数型と符号なし整数型 $ 符号付き整数型 \to 符号なし整数型 $
2.7 自加、自減演算子とカンマ#
2.7.1 自加演算子「++」と自減演算子「--」#
- 「++」と「--」は単項演算子であり、定数や式に代入することはできない。
- 前置形式と後置形式の両方で出現することができる。
- 変数に対しては 1 を自加または自減する;式に対しては、前置形式では先に自加してから変数の値を使用し、後置形式では先に変数の値を使用してから自加する。
- 結合方向:右から左。
2.7.2 カンマ演算子とカンマ式#
- カンマ演算子の結合方向:左から右。
- カンマ演算子の優先順位は最低である。
第 3 章 順次構造#
3.1 代入文#
- 代入文(式文):代入式の末尾に「;」を加える。
3.2 データ出力#
3.2.1 printf 関数の一般的な呼び出し形式#
- 呼び出し形式:
printf(フォーマット制御,出力項1,出力項2,···)
printf("a=%d,b=%d", a, b);
3.2.2 printf 関数でよく使われるフォーマット説明#
各フォーマット説明は「%」で始まり、フォーマット文字で終了し、その間に必要に応じて「幅説明」、「左寄せ記号「-」」、「先頭ゼロ記号「0」」を挿入することができる。
- フォーマット文字
フォーマット文字 | 説明 |
---|---|
c | 1 文字を出力 |
d または i | 符号付き 10 進整数を出力。% ld は長整数、% hd は短整数、% I64d は 64 ビット長整数 |
o | 8 進数形式で整数を出力。%#o は先頭に 0 を加える |
x または X | 16 進数形式で整数を出力。%#x または %#X は先頭に 0x または 0X を付けた 16 進数を出力 |
u | 符号なし 10 進形式で整数を出力 |
f | 小数点を含む数学形式で浮動小数点数を出力(単精度および倍精度数) |
e または E | 指数形式で浮動小数点数を出力(単精度および倍精度数) |
g または G | システムが % f または % e(または % E)形式を選択し、出力幅を最小にする |
s | 文字列を出力し、「\0」に出会うまで |
p | 変数のメモリアドレスを出力 |
% | '%' を出力 |
- 出力データが占める幅の説明
- % とフォーマット文字の間に整数定数を挿入して出力の幅を指定する。指定された幅が出力データの実際の幅を超える場合、出力時は右寄せされ、左側に空白が補充される。
- float および double 型の実数については、「n1.n2」の形式で出力幅を指定できる(n1 と n2 はそれぞれ整数定数を表す)。ここで n1 は出力データの幅(小数点を含む)を指定し、n2 は小数点以下の桁数を指定し、n2 は精度とも呼ばれる。
- f、e または E の場合、出力データの小数点以下の桁数が n2 桁を超えると、右側の余分な小数を切り捨て、切り捨てた部分の最初の小数を四捨五入する;出力データの小数点以下の桁数が n2 桁未満の場合、最右側の小数に 0 を補充する。
- また「.n2」形式を使用して、総幅を指定しないこともできる;「n1.0」または「.0」形式を指定すると、小数点および小数部分は出力されない。
- g または G の場合、幅は出力の有効数字の桁数を指定する。デフォルトは 6 桁の有効数字である。
- 出力データを左寄せするには:「%」と幅の間に「-」を加える。
- 出力データに常に +/- を付けるには:「%」とフォーマット文字の間に「+」を加える。
3.2.3 printf 関数使用時の注意事項#
- printf 関数を呼び出すとき、その引数は右から左に処理される。
- 出力データの幅は変更可能である。
printf("%*d",m,i); /*mで指定された幅に従ってiの値を出力し、mの値は出力しない*/
3.3 データ入力#
3.3.1 scanf 関数の一般的な呼び出し形式#
- 呼び出し形式:
scanf(フォーマット制御,入力項1,入力項2,···)
scanf("%d%f%lf",&k,&a,&y);
3.3.2 scanf 関数でよく使われるフォーマット説明#
フォーマット文字 | 説明 |
---|---|
c | 1 文字を入力 |
d | 符号付き 10 進整数を入力 |
i | 整数を入力し、整数は先頭に 0 を付けた 8 進数または先頭に 0x(または 0X)を付けた 16 進数でも可 |
o | 8 進数形式で整数を入力し、先頭に 0 を付けることもできる |
x | 16 進数形式で整数を入力し、先頭に 0x または 0X を付けることもできる |
u | 符号なし 10 進形式で整数を入力 |
f(lf) | 小数点を含む数学形式または指数形式で浮動小数点数を入力(単精度数は f、倍精度数は lf) |
e(le) | 同上 |
s | 文字列を入力し、「\0」に出会うまで |
- scanf 関数は正しく入力されたデータ項の個数を返す。
3.3.3 scanf 関数を使用してキーボードからデータを入力する#
- 数値データを入力する:整数や実数などの数値型データを入力する場合、入力されたデータの間には空白、改行、タブなどの区切り文字が必要である。
- 特定の入力データをスキップするには、% と文字の間に「*」を追加する。
3.4 複合文と空文#
3.4.1 複合文#
- 文の形式:
{文1 文2 文3 ··· 文n}
3.4.2 空文#
int main(){
; /*空文*/
}
3.5 プログラムの例#
/*
* Created by binxin on 2023/8/6.
*
* ターミナルから2つの整数を変数xとyに入力し;その後xとyを出力;xとyの値を交換した後、再度xとyを出力
*/
#include "stdio.h"
int main() {
int x, y, t;
printf("整数xとyの値を入力:");
scanf("%d%d", &x, &y);
printf("x=%d,y=%d\n", x, y);
t = x;
x = y;
y = t;
printf("x=%d,y=%d", x, y);
}
/*
* Created by binxin on 2023/8/6.
*
* double型の数を入力し、その数を小数点以下2桁に保ち、3桁目の小数を四捨五入してから出力する
*/
#include "stdio.h"
int main() {
double x;
printf("データを入力:");
scanf("%lf", &x);
printf("x=%f\n", x);
x = x * 100;
x = x + 0.5;
x = (int) x;
x = x / 100;
printf("x=%f", x);
}
第 4 章 選択構造#
4.1 関係演算と論理演算#
4.1.1 C 言語の論理値#
- C 言語では、非 0 は「真」を、0 は「偽」を表す。
4.1.2 関係演算子と関係式#
- 6 種類の関係演算子
- 小なり <
- 大なり >
- 等しい ==
- 小なりまたは等しい <=
- 大なりまたは等しい >=
- 等しくない!=
- 二文字から成る演算子の間に空白を入れてはいけない。
- 関係演算子は二項演算子であり、左から右への結合性を持つ。
- 関係演算の値は「論理値」であり、整数 0 または 1 のいずれかである。
4.1.3 論理演算子と論理式#
4.1.3.1 C 言語の論理演算子#
- 三種類の論理演算子
- 論理積 &&
- 論理和 ||
- 論理否定!
- && と || は二項演算子であり、! は単項演算子で、演算対象の左側に出現する。結合方向は左から右。
4.1.3.2 論理式と論理式の値#
- 論理式の演算結果は 1 または 0 である。
- && または || から成る論理式は、特定の条件下で「ショートサーキット」現象を引き起こすことがある。
4.2 if 文と if 文から構成される選択構造#
4.2.1 if 文#
- 基本形式
if(式) 文 /* else節を含まないif文 */
if(式) 文1 /* else節を含むif文 */
else 文2
4.2.2 ネストされた if 文#
- 文の形式
if(式1){
if(式2) 文1
} else {
文2
}
- else 節は常に前の最近の else を含まない if に結びつく。
4.3 条件式から構成される選択構造#
- 条件演算子:
? :
,C 言語が提供する唯一の三項演算子。 - 右条件演算子から構成される条件式:
式1 ? 式2 : 式3
- 演算機能:式 1 の値が非零の場合、式 2 の値が全体の式の値となり;式 1 の値が零の場合、式 3 の値が全体の式の値となる。
4.4 switch 文および switch 文と break 文から構成される選択構造#
switch(式){
case 定数式1:文1
case 定数式2:文2
·
·
·
case 定数式n:文n
default :文n+1
}
- キーワード case と定数式の間には必ず空白が必要である。
- 通常、case の後の文の最後に break 文を加え、break 文に到達したときに即座に switch 文の本体から抜け出す。
第 5 章 ループ構造#
5.1 while 文と while 文から構成されるループ構造#
- 一般形式:
while(式) ループ本体
- 式は空であってはならない。
5.2 do-while 文と do-while 文から構成されるループ構造#
- 一般形式
do
ループ本体
while(式);
- do は while と一緒に使用しなければならない。
5.3 for 文と for 文から構成されるループ構造#
- 一般形式:
for(式1;式2;式3) ループ本体
- 実行プロセス
- 式 1 を計算する。
- 式 2 を計算する。値が非零の場合はステップ 3 に進み、値が零の場合はステップ 5 に進む。
- for ループ本体を一度実行する。
- 式 3 を計算し、ステップ 2 に戻る。
- ループを終了する。
- for 文の式は部分的または全体的に省略可能だが、二つの「;」は省略できない。
5.4 ループ構造のネスト#
- ネストされたループ:一つのループ本体内に別のループが完全に含まれている。
5.5 break 文と continue 文のループ本体内での作用#
- break 文はループ本体内および switch 文の本体内でのみ使用できる。
- continue 文の作用は、今回のループ本体内で残りの未実行の文をスキップし、直ちに次のループ条件の判定に進むこと。
5.6 プログラムの例#
/*
入力された複数の正整数の中から最大値を選び、-1で入力を終了する
*/
#include <stdio.h>
int main(){
int max, n;
do{
printf("データを入力し、-1で終了:");
scanf("%d", &n);
if(max<n){
max = n;
}
} while (n != -1);
printf("max=%d", max);
}
/*
反復法を用いてある数aの平方根を求める。平方根の反復公式はx1=(x0+a/x0)/2である。
*/
#include <stdio.h>
#include <math.h>
int main(){
double x0, x1, a;
printf("データを入力:");
scanf("%lf", &a);
x0 = a / 2;
x1 = (x0 + a / x0) / 2;
do{
x0 = x1;
x1 = (x0 + a / x0) / 2;
} while (a > 0 && fabs(x1 - x0) > 1e-6);
printf("x=%f", x1);
}
第 6 章 文字型データ#
6.1 文字型定数#
6.1.1 文字定数#
- 単一引用符内の大文字と小文字は異なる文字定数を表す。
- 単一引用符内の空白文字も一つの文字定数である。
- 文字定数は一つの文字のみを含むことができる。
- 文字定数は単一引用符で囲む必要があり、二重引用符で囲むことはできない。
- 文字定数はメモリ内で 1 バイトを占め、文字の ASCII コード値が保存される。
6.1.2 エスケープ文字定数#
文字形式 | 機能 |
---|---|
\n | 改行 |
\t | 水平方向にいくつかのタブを移動 |
\v | 垂直方向にいくつかのタブを移動 |
\r | 改行符 |
\f | ページ送り符 |
\b | バックスペース(Backspace キー) |
\ | 逆スラッシュ文字「\」 |
' | 単一引用符文字 |
" | 二重引用符文字 |
\ddd | 三桁の 8 進数が表す ASCII 文字 |
\xhh | 二桁の 16 進数が表す ASCII 文字 |
\0 | 空値、その ASCII コード値は 0 |
- エスケープ文字定数は一つの文字のみを表す。
- 逆スラッシュの後の 8 進数は 0 で始める必要はない。
- 一対の単一引用符内では、逆スラッシュの後に 16 進数を続けて ASCII 文字を表すことができる。
6.1.3 文字列定数#
- 定義:二重引用符で囲まれた一連の文字。
- C 言語では、システムが各文字列の最後に自動的に文字
'\0'
を追加して文字列の終了マークとする。
6.1.4 文字量に対する演算#
- 文字量は任意の整数演算に参加できる。
6.2 文字変数#
- C 言語では、文字変数はキーワード char で定義され、定義と同時に初期値を与えることができる。
- 1 バイトを占める。
- 文字変数は整数型変数として処理でき、整数型変数に許可される任意の演算に参加できる。
6.3 文字の入力と出力#
6.3.1 printf および scanf 関数を使用して文字を出力および入力する#
- 入力および出力を行う関数を呼び出す際、プログラムの冒頭に
#include <stdio.h>
を含む命令行が必要である。
6.3.2 putchar および getchar 関数を使用して文字を出力および入力する#
- putchar 関数は文字を出力するために使用され、呼び出し形式は:
putchar(ch)
- ch は文字定数でも文字変数でもよい。
- getchar 関数は文字を入力するために使用され、呼び出し形式は:
ch=getchar()
6.4 プログラムの例#
/*
26個の大文字を出力し、それらのASCIIコードを表示し、各行に2組のデータを出力する。
*/
#include <stdio.h>
int main(){
char c = 'A';
for (int i = 0; i < 26; i=i+2){
printf("%c %d %c %d\n", c + i, c + i, c + i + 1, c + i + 1);
}
}
/*
ターミナルから1文字を入力し、Enterキーを押すまでプログラムは続行しない。
*/
#include <stdio.h>
int main(){
printf("データを入力:");
while (getchar() != '\n'){
}
printf("end");
}
/*
ターミナルから入力された1行の文字の中のすべての小文字を大文字に変換し、他の文字はそのままにする。
*/
#include <stdio.h>
int main(){
char ch;
printf("データを入力:");
while ((ch = getchar()) != '\n'){
if(ch>='a' && ch<='z'){
ch = ch - 'a' + 'A';
}
putchar(ch);
}
}
/*
入力された文字の中の空白、改行、タブの個数をカウントし、!で入力を終了する。
*/
#include <stdio.h>
int main(){
char ch;
int sum = 0;
printf("データを入力:");
while ((ch = getchar()) != '!'){
if(ch==' '||ch=='\n'||ch=='\t'){
sum++;
}
}
printf("sum=%d", sum);
}
/*
暗号文を明文に変換し、暗号文は文字@で終了し、変換ルールは次の通り:
(1)文字の場合、文字列の次の文字に変換する。例えばAはBに変換される。
(2)文字ZはAに変換される。
(3)大文字と小文字の文字はすべて小文字に変換される。
(4)他の文字はそのまま出力される。
*/
#include <stdio.h>
#include <ctype.h>
int main(){
char ch;
printf("データを入力:");
while ((ch = getchar()) != '@'){
if(isalpha(ch)){ /*isalpha(ch)はchの文字が文字であるかを判断し、そうであれば関数値は1*/
ch = tolower(ch); /*大文字を小文字に変換*/
ch = (ch - 'a' + 1) % 26 + 'a';
}
putchar(ch);
}
}
第 7 章 関数#
7.1 ライブラリ関数#
- C 言語のライブラリ関数を呼び出す際に必要な include 命令行。
- 標準ライブラリ関数の呼び出しの一般形式:
関数名(引数表)
7.2 関数の定義と戻り値#
7.2.1 関数定義の文法#
- 関数定義の一般形式
関数戻り値の型名 関数名(型名 形式引数1,型名 形式引数2,······){
説明部分
文部分
}
- 関数名と形式引数はユーザーが命名した識別子である。同一プログラム内で関数名は一意でなければならず、形式引数名は同一関数内で一意であればよい。
- 関数の内部で関数を定義することはできない。
- デフォルトの関数戻り値の型は int 型である。
- 戻り値の型が int 型の関数を除き、関数は先に定義(または説明)されてから呼び出されなければならない。
- もし関数が特定の操作を完了するためだけに使用され、戻り値がない場合、その関数は void 型として定義する必要がある。
7.2.2 関数の戻り値#
- 関数の値は return 文を通じて返され、一般形式は:
return 式;
またはreturn (式)
- 同一関数内で return 文は一度だけ実行される可能性がある。
7.3 関数の呼び出し#
7.3.1 関数の二つの呼び出し方式#
- 関数の一般的な呼び出し形式:
関数名(実引数表)
- 二つの呼び出し方式
- 呼び出された関数が特定の値を求めるために使用される場合、関数の呼び出しは許可される式の任意の場所に出現することができる。
- 関数が戻り値を必要としない場合、関数の呼び出しは独立した文として出現することができる。
7.3.2 関数呼び出し時の文法要件#
- 関数を呼び出すとき、関数名は呼び出される関数名と完全に一致しなければならない。
- 実引数の個数は形式引数の個数と一致しなければならない。
- 関数は先に定義されてから呼び出されなければならない(戻り値が int または char の場合を除く)。
- 関数は直接または間接的に自分自身を呼び出すことができ、これを再帰呼び出しと呼ぶ。
7.4 関数の説明#
7.4.1 関数説明の形式#
- 主関数を除き、ユーザー定義の関数は「先に定義し、後に使用する」という規則に従う。
- 一般形式
型名 関数名(引数型1,引数型2,······)
型名 関数名(引数型1 引数名1,引数型2 引数名2,······)
7.4.2 関数説明の位置#
- すべての関数の外部で、呼び出される前に関数を説明する場合、関数の説明文の後のすべての位置でその関数を呼び出すことができる。
- 関数説明は呼び出し関数内の説明部分にも置くことができ、main 関数内で説明する場合、その関数は main 関数内でのみ認識される。
7.5 呼び出し関数と呼ばれる関数間のデータ伝達#
- 実引数と形式引数間でデータを伝達する。
- return 文を通じて関数の値を呼び出し関数に返す。
- グローバル変数を通じて(推奨されない)。
7.6 プログラムの例#
/*
関数isprime(int a)を作成し、引数aが素数であるかどうかを判断する。素数であれば1を返し、そうでなければ0を返す。
*/
#include <stdio.h>
int isprime(int a);
int main(){
int x;
printf("データを入力:");
scanf("%d", &x);
if(isprime(x)){
printf("素数");
} else{
printf("素数ではない");
}
}
int isprime(int a){
for (int i = 2; i <= a / 2;i++){
if (a % i == 0){
return 0;
}
}
return 1;
}
第 8 章 アドレスとポインタ#
8.1 ポインタ変数の定義とポインタ変数の基型#
- ポインタ変数を定義する一般形式:
型名 *ポインタ変数名1,*ポインタ変数名2,······
- ポインタ変数は基型を区別する必要があり、基型が異なるポインタ変数は混合して使用できない。
8.2 ポインタ変数に値を割り当てる#
8.2.1 ポインタ変数にアドレス値を割り当てる#
- アドレス演算子(&)を使用してアドレス値を取得する。
- アドレス演算子は変数や配列要素にのみ適用でき、式、定数、または register として指定された変数には適用できない。
- & は演算対象の左側に置かなければならず、演算対象の型はポインタ変数の基型と同じでなければならない。
- ポインタ変数を通じてアドレス値を取得する。
- 代入演算を行うとき、代入記号の両側のポインタ変数の基型は同じでなければならない。
- 標準関数を通じてアドレス値を取得する。
- malloc や calloc などのライブラリ関数を呼び出してメモリ内に動的ストレージユニットを確保し、確保した動的ストレージユニットのアドレスをポインタ変数に割り当てることができる。
8.2.2 ポインタ変数に「空」値を割り当てる#
p=NULL
- ポインタ p はアドレス 0 のストレージユニットを指すのではなく、確定的な値「空」を持つ。
8.3 ポインタ変数の操作#
8.3.1 ポインタを通じてストレージユニットを参照する#
- 間接アクセス演算子(間接アドレス演算子):*
- ポインタ変数に確定的なアドレス値が格納されている場合、「*」を使用してポインタを通じてそのアドレスのストレージユニットを参照することができる。
- 単項演算子であり、演算対象の左側に出現しなければならない。
8.3.2 ポインタを移動する#
- 定義:ポインタ変数に整数を加えたり引いたりすること、または代入演算を通じてポインタ変数が隣接するストレージユニットを指すようにすること。
- ポインタが連続したストレージユニットを指している場合にのみ、ポインタの移動は意味を持つ。
8.4 関数間のアドレス値の伝達#
8.4.1 形引数がポインタ変数の場合の実引数と形引数間のデータ伝達#
/*
関数myadd(int *a,int *b)を作成し、ポインタaとbが指すストレージユニット内の二つの値を加算し、その和を関数値として返す。
*/
#include <stdio.h>
int myadd(int *a, int *b);
int main(){
int a, b,sum;
printf("データを入力:");
scanf("%d%d", &a, &b);
sum = myadd(&a, &b);
printf("sum=%d", sum);
}
int myadd(int *a, int *b){
int sum;
sum = *a + *b;
return sum;
}
8.4.2 アドレスを通じて呼び出し関数内の変数の値を直接変更する#
/*
swap関数を呼び出し、主関数内の変数xとyのデータを交換する。
*/
#include <stdio.h>
void swap(int *, int *);
int main(){
int a = 30, b = 20;
printf("a=%d b=%d\n", a, b);
swap(&a, &b);
printf("a=%d b=%d", a, b);
}
void swap(int *a,int *b){
int t;
t = *a;
*a = *b;
*b = t;
}
8.4.3 関数がアドレス値を返す#
/*
主関数内の変数iとjの中でより大きい数のアドレスを関数値として返す。
*/
#include <stdio.h>
int *fun(int *, int *);
int main(){
int i, j;
i = 10;
j = 20;
printf("max=%d",*fun(&i, &j));
}
int *fun(int *i, int *j){
if(*i>*j){
return i;
} else {
return j;
}
}
第 9 章 配列#
9.1 一次元配列の定義と一次元配列要素の参照#
9.1.1 一次元配列の定義#
- 定義:配列の各要素は一つのインデックスを持つ。
- 一般形式:
型名 配列名[整数定数式],······
- 配列説明子と通常の変数名は一つの型定義文の中に同時に出現することができる。
9.1.2 一次元配列の参照#
- 参照形式:
配列名[インデックス式]
9.1.3 一次元配列の初期化#
- 割り当てられた初期値が定義された配列の要素数より少ない場合、システムは自動的に後の要素に初期値 0 を補充する。
- 文字型配列も同様に初期値 0 を補充する、すなわち
\0
。
9.1.4 初期値を通じて配列のサイズを定義する#
- C 言語では、初期値を通じて配列のサイズを定義することができ、この場合、配列説明子の一対の角括弧内で配列のサイズを指定する必要はない。
9.1.5 一次元配列の定義と配列要素参照の例#
/*
30個の要素を持つint型配列を定義し、配列要素に奇数1、3、5、···を順に割り当て、最後に各行に10個の数を順に出力し、最後に逆順に各行に10個の数を出力する。
*/
#include <stdio.h>
int main(){
int a[30];
int i, k = 1;
for (i = 0; i < 30; i ++){
a[i] = k;
k = k + 2;
}
for (i = 0; i < 30; i ++){
printf("%4d", a[i]);
if ((i+1) % 10 == 0){
printf("\n");
}
}
printf("\n");
for (i = 29; i >= 0; i --){
printf("%4d", a[i]);
if (i % 10 == 0){
printf("\n");
}
}
}
9.2 一次元配列とポインタ#
- 関数体内または関数外で定義された配列名は、配列の最初の要素のアドレスを格納するポインタ変数名と見なすことができ、配列が占める連続したストレージユニットの開始アドレスである。配列を定義する際の型はこのポインタ変数の基型である。
- このポインタ変数のアドレス値は変更できず、配列名に新たに値を割り当てることはできない。
- 配列名に整数を加えることで、配列内の異なる要素のアドレスを順に表現することができる。
9.3 関数間での一次元配列と配列要素の参照#
9.3.1 配列要素を実引数として使用#
- 各配列要素は実際にはメモリ内の一つのストレージユニットを表し、配列要素の値はその変数に渡すことができ、関数内ではその変数に対してのみ操作でき、対応する配列要素を直接参照することはできず、関数内で対応する配列要素の値を変更することはできない。
9.3.2 配列名を実引数として使用#
- 配列名自体はアドレス値であり、対応する形引数はポインタ変数でなければならず、このポインタ変数の基型は配列の型と一致しなければならない。関数内ではこのポインタ変数を通じて呼び出し関数内の対応する配列要素を参照し、呼び出し関数内の対応する配列要素の値を変更することができる。
/*
関数を通じて主関数内で定義された配列に0以上の整数を入力し、負の数を入力の終了マークとする;別の関数を呼び出してその配列内のデータを出力する。
*/
#include <stdio.h>
#define M 100
int input(int *a);
void output(int *a,int n);
int main(){
int a[M],n;
n = input(a);
output(a,n);
}
int input(int *a){
int n,i;
i = 0;
printf("データを入力:");
scanf("%d", &n);
while (n >= 0){
a[i] = n;
i++;
scanf("%d", &n);
}
return i - 1;
}
void output(int *a,int n){
for (int i = 0; i <= n;i++){
printf("%d ", a[i]);
}
}
9.3.3 配列要素のアドレスを実引数として使用#
- 配列要素を実引数として使用する場合、アドレス値であるため、対応する形引数も基型が同じポインタ変数でなければならない。
/*
10個の要素を持つchar型配列の下から4番目の要素からすべてをアスタリスクに設定し、最初の4つの要素はそのままにする。
*/
#include <stdio.h>
#define M 10
#define B 4
void setstar(char *c);
int main(){
char c[M] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
setstar(c);
for (int i = 0; i < M; i++){
printf("%c ", c[i]);
}
}
void setstar(char *c){
for (int i = B; i < M; i++){
c[i] = '*';
}
}
9.4 一次元配列の応用例#
/*
15個の要素を持つ配列を定義し、Cライブラリのランダム関数を呼び出してすべての要素に0〜49のランダム数を割り当てる。
2. 配列要素の値を出力する。
3. 各3つの数の合計を順に求め、主関数に返す。
4. 最後にすべての合計を出力する。
*/
#include <stdio.h>
#include <stdlib.h>
#define M 15
#define N 3
void getrand(int *a);
void arrout(int *a,int n);
void getsum(int *a, int n,int *sum);
int main(){
int a[M],sum[M/N];
getrand(a);
arrout(a,M);
getsum(a, N,sum);
arrout(sum,M/N);
}
void getrand(int *a){
for (int i = 0; i < M; i++){
a[i] = rand() % 50;
}
}
void arrout(int *a,int n){
for (int i = 0; i < n; i++){
printf("%d ", a[i]);
}
printf("\n");
}
void getsum(int *a, int n,int *sum){
for (int i = 0; i < M/N; i++){
sum[i] = a[i * N] + a[i * N + 1] + a[i * N + 2];
}
}
9.5 二次元配列の定義と二次元配列要素の参照#
9.5.1 二次元配列の定義#
- 定義文形式:
型名 配列名[定数式1][定数式2],······
9.5.2 二次元配列の参照#
- 参照形式:
配列名[インデックス式1][インデックス式2]
9.5.3 二次元配列の初期化#
- 割り当てられた初期値の個数は配列要素の個数と同じである。
- 二次元配列を定義する際に同時に各要素に初期値を割り当てることができる
int a[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
- 二次元配列を定義する際に同時に各要素に初期値を割り当てることができる
- 各行に割り当てられた初期値の個数が配列要素の個数と異なる。
- システムは自動的にその行の後の要素に初期値を割り当てる。
- 割り当てられた初期値の行数が配列の行数より少ない。
- システムは自動的に後の各行の要素に初期値 0 を補充する。
- 初期値を割り当てる際に波括弧を省略する。
int a[4,3]={1,2,4,5};
- システムは a 配列の要素がメモリ内に配置される順序に従って、波括弧内のデータを一つ一つ対応させて各要素に割り当て、データが不足している場合、システムは後の要素に自動的に初期値 0 を補充する。
9.5.4 初期値を通じて二次元配列のサイズを定義する#
- 最初の角括弧内の定数式を省略することができるが、二番目の角括弧内の定数式を省略することはできない。
9.5.5 二次元配列の定義と配列要素参照の例#
/*
キーボードから2*3の二次元配列のデータを入力し、第一行に1、2、3を割り当て、第二行に10、20、30を割り当て、その後この二次元配列を行ごとに出力する。
*/
#include <stdio.h>
int main(){
int a[2][3];
printf("データを入力:");
for (int i = 0; i < 2; i++){
for (int j = 0; j < 3; j++){
scanf("%d", &a[i][j]);
}
}
for (int i = 0; i < 2; i++){
for (int j = 0; j < 3; j++){
printf("%4d", a[i][j]);
}
printf("\n");
}
}
9.6 二次元配列とポインタ#
次の定義を示す:
in *p,a[3][4];
- 二次元配列 a は複数の一次元配列から構成される。
- 上記の二次元配列において、a [0],a [1],a [2] はすべて一次元配列名であり、同様に変更不可能なアドレス定数を表し、その値は二次元配列の各行の最初の要素のアドレスであり、その基型は配列要素の型である。
- 二次元配列名もアドレス値定数である。
- 二次元配列名もアドレス定数を格納するポインタであり、その値は二次元配列の最初の要素のアドレスである。
- 二次元配列名は行ポインタとして理解されるべきである。
- 二次元配列要素のアドレス
- 二次元配列要素のアドレスは式 & a [i][j] を使用して求めることができ、各行の先頭アドレスを通じて表現することもできる。
9.7 二次元配列名とポインタ配列を実引数として使用する#
9.7.1 二次元配列名を実引数として使用する場合の実引数と形引数間のデータ伝達#
- 二次元配列名を実引数として使用する場合、対応する形引数は行ポインタ変数でなければならない。
9.7.2 ポインタ配列を実引数として使用する場合の実引数と形引数間のデータ伝達#
- ポインタ配列を実引数として使用する場合、対応する形引数はポインタを指すポインタでなければならない。
9.8 二次元配列プログラムの例#
/*
ランダム関数を呼び出して5*6の二次元配列要素に10〜40の範囲内の整数を割り当て、二次元配列の各行要素の平均値を求める。
*/
#include <stdio.h>
#include <stdlib.h>
void getrand(int a[][6]);
void getave(int a[][6], double *ave);
int main(){
int a[5][6];
double ave[5]={0};
getrand(a);
for (int i = 0; i < 5; i++){
for (int j = 0; j < 6; j++){
printf("%d ", a[i][j]);
}
printf("\n");
}
getave(a, ave);
for (int i = 0; i < 5; i++){
printf("%f ", ave[i]);
}
}
void getrand(int a[][6]){
for (int i = 0; i < 5; i++){
for (int j = 0; j < 6; j++){
a[i][j] = rand() % 31 + 10;
}
}
}
void getave(int a[][6], double *ave){
for (int i = 0; i < 5; i++){
for (int j = 0; j < 6; j++){
ave[i] = ave[i] + a[i][j];
}
ave[i] = ave[i] / 6;
}
}
第 10 章 文字列#
10.1 一次元文字配列を使用して文字列を保存する#
- C 言語における文字列の約束
- 文字列は文字型の一次元配列を使用して保存され、文字
\0
を「文字列終了マーク」として規定する。 \0
はマークとしてストレージスペースを占有するが、実際の長さには含まれない。
- 文字列は文字型の一次元配列を使用して保存され、文字
- C 言語における文字列定数の表現
- 文字列定数は二重引用符で囲まれた一連の文字であり、システムは自動的に末尾に文字
\0
を追加する。
- 文字列定数は二重引用符で囲まれた一連の文字であり、システムは自動的に末尾に文字
- C 言語における文字列定数はアドレス値を提供する。
- 文字配列と文字列の違い
- 文字配列内にのみ文字列を保存でき、代入文を使用して文字列定数や他の文字配列からの文字列を直接文字列変数に代入することはできない。
- 文字列は文字配列の具体的な応用である。
10.1.1 初期値を使用して一次元文字配列に文字列を割り当てる#
- 一般配列に初期値を割り当てるのと同じ方法で一次元文字配列に初期値を割り当てることができる。
- 割り当てられた初期値の文字数が配列の要素数より少ない場合、システムは自動的に後の要素に
\0
を追加する。
- 割り当てられた初期値の文字数が配列の要素数より少ない場合、システムは自動的に後の要素に
- 初期値を直接文字列定数として割り当てる。
10.1.2 C プログラムの実行中に一次元文字配列に文字列を割り当てる#
- 文字配列全体に文字列を割り当てるために代入文を使用することはできない。
- 配列要素に逐次的に文字値を割り当て、最後に手動で文字列終了マークを追加する。
10.2 ポインタを使用して文字列を指す#
- 初期値を使用してポインタを文字列に指す。
- 文字ポインタ変数を定義する際に、文字列を格納するストレージユニットの開始アドレスをポインタ変数に割り当てることができる。
- 代入演算を通じてポインタを文字列に指す。
10.3 文字列の入力と出力#
10.3.1 文字列を入力および出力する際の必要条件#
- 文字列を出力する際、出力項は文字列定数または文字配列名であるか、文字列を指す文字ポインタ変数でもよい。
- 文字列を入力する際、入力項は文字配列名または文字ポインタ変数である。
10.3.2 フォーマット指定子 %s
を使用して全体を入力および出力する#
- scanf 関数内で使用することで文字列全体を入力することができる。
- 空白と改行は入力データの区切り文字として扱われ、読み込まれない。
- 入力文字列の長さが文字配列が収容できる文字数を超える場合、システムはエラーを報告しない。これはインデックスの越境に相当する。
- 入力項が配列要素のアドレスである場合、入力された文字はこの要素から順に配列内に格納される。
- 入力項が文字ポインタ変数である場合、このポインタ変数は十分なスペースを持つ連続したストレージユニットを指している必要がある。
- printf 関数内で使用することで文字列全体を出力することができる。
- ストレージユニット内の文字を順に出力し、最初の
\0
に出会うまで続ける。 \0
は終了マークであり、出力文字には含まれない。- 出力終了後に自動的に改行は行われない。
- ストレージユニット内の文字を順に出力し、最初の
10.3.3 gets、puts 関数を呼び出して一行の文字列を入力または出力する#
- gets 関数:
gets(str_adr)
- gets 関数はキーボードから文字列を読み込み(空白文字を含む)、改行文字に出会うまで読み込む。
- 改行文字は読み込まれた後、文字列の内容としては扱われず、システムは自動的に
\0
で置き換える。
- puts 関数:
puts(str_adr)
- ストレージユニット内の文字を順に出力し、最初の
\0
に出会うと出力を終了し、自動的に改行文字を出力する。
- ストレージユニット内の文字を順に出力し、最初の
10.4 文字列処理に使用される関数#
以下の関数を使用する際には、ヘッダーファイル
<string.h>
を追加する必要がある。
- 文字列代入(コピー)関数 strcpy:
strcpy(s1,s2)
- s2 が指す文字列(ソース)の内容を s1 が指すストレージスペース(目的)にコピーし、関数は s1 の値、すなわち目的の文字列の先頭アドレスを返す。
- s1 は s2 の文字列を格納するのに十分なスペースを指している必要がある。
- 文字列結合関数 strcat:
strcat(s1,s2)
- s2 が指す文字列の内容を s1 が指す文字列の後に結合し、自動的に s1 の末尾の
\0
を上書きし、関数は s1 が指すアドレス値を返す。 - s1 が指す文字列は、二つの文字列を結合した内容を格納するのに十分なスペースを持っている必要がある。
- s2 が指す文字列の内容を s1 が指す文字列の後に結合し、自動的に s1 の末尾の
- 文字列の長さを求める関数 strlen:
strlen(s)
- s を開始アドレスとする文字列の長さを計算し、関数値として返す。末尾の終了マーク
\0
は含まれない。
- s を開始アドレスとする文字列の長さを計算し、関数値として返す。末尾の終了マーク
- 文字列比較関数 strcmp:
strcmp(s1,s2)
- s1 と s2 の対応する位置の文字を順に比較し、文字の大小はその ASCII コード値に基づく。
10.5 プログラムの例#
/*
関数slenth(char *s)を作成し、ポインタsが指す文字列の長さを返す。これはライブラリ関数strlenの機能に相当する。
*/
#include <stdio.h>
int slenth(char *s);
int main(){
int length;
char str[] = "ABCDEFG";
length = slenth(str);
printf("%d", length);
}
int slenth(char *s){
int i = 0;
while (*(s + i) != '\0'){
i++;
}
return i;
}
第 11 章 関数に関するさらなる議論#
11.1 main 関数に渡される引数#
- main 関数は通常二つの引数を取ることができる。
- 最初の引数 argc は整数型でなければならない。
- 二番目の引数 argv は文字型のポインタ配列のポインタであり、各ポインタは文字列を指す。
main(int argc, char **argv){
···
}
11.2 実引数を通じて関数名または関数へのポインタを関数に渡す#
- 関数ポインタ変数の定義:C 言語では関数名はその関数の入口アドレスを表すため、関数のアドレスを格納するためのポインタを定義することができる。
#include <stdio>
double fun(int a, int *p){
···
}
main(){
double (*fp)(int, int *),y;
int n;
fp = fun;
·
·
·
y = (*fp)(56, &n); /* ここで関数ポインタを通じてfun関数を呼び出す */
·
·
·
}
- 関数名または関数へのポインタ変数を実引数として渡すことができる。
11.3 関数の再帰呼び出し#
/*
再帰的な方法でn!を求める。
*/
#include <stdio.h>
int fac(int n);
int main(){
int n;
printf("データを入力:");
scanf("%d", &n);
printf("%d!=%d", n, fac(n));
}
int fac(int n){
int f;
if (n == 1 || n == 0){
return 1;
} else {
f = n * fac(n - 1);
return f;
}
}
第 12 章 C 言語におけるユーザー識別子の作用域とストレージクラス#
12.1 ローカル変数#
- ローカル変数(内部変数):関数内部または複合文内部で定義された変数。
- 関数の形引数もローカル変数に含まれる。
- グローバル変数(外部変数):関数外部で定義された変数。
- C 言語では、二種類のストレージクラスがある