C言語の基本知識をゼロから学ぶハンズオン学習

  1. C言語でHelloWorldを表示してみよう
    1. 1. HelloWorldを表示してみよう
      1. 1-1. プロジェクトの作成
      2. 1-2. コーディング
    2. 2. コードの理解
    3. 3. コンソールに出力
      1. 3-1. printf()関数
      2. 3-2. puts()関数
    4. 4. コメントアウト
    5. 5. エスケープシーケンス
    6. 6. コンパイルエラー処理
  2. 演算
    1. 1. 四算演算子
    2. 2. 複合代入演算子
    3. 3. 比較演算子
    4. 4. 論理演算子
    5. 5. 単項演算子(インクリメント、デクリメント)
  3. 変数
    1. 1. 変数
      1. 1-1. 変数とは
      2. 1-2. 変数の定義
      3. 1-3. 代入
    2. 2. データ型
    3. 3. 変数の命名規則
    4. 4. 型変換
  4. 条件分岐
    1. 1. 条件分岐とは
    2. 2. if文
      1. 2-1. if文
      2. 2-2. else文
      3. 2-3. else if文
      4. 2-4. if文のネスト
    3. 3. switch文
  5. 繰り返し処理
    1. 1. 繰り返し処理とは
    2. 2. for文
      1. 2-1. for文
      2. 2-2. for文のネスト
    3. 3. while文 と do-while文
      1. 3-1. while文
      2. 3-2. do-while文
  6. 配列変数
    1. 1. 配列変数とは
    2. 2. 配列変数の定義
      1. 2-1. 配列変数の宣言
      2. 2-2. 配列変数の初期化
    3. 3. 配列変数の要素を取得
  7. 関数
    1. 1. 関数とは
    2. 2. 関数の定義
      1. 2-1. 関数の定義
      2. 2-2. 関数の呼び出し
      3. 2-3. 関数の定義と呼び出しの注意点
    3. 3. 変数のスコープ
  8. ファイル分割
    1. 1. ファイル分割とは
    2. 2. ヘッダファイルのインクルード
      1. 2-1. ヘッダファイルの定義
      2. 2-2. ヘッダファイルの読み込み
    3. 3. グローバル変数の扱い
  9. アドレスとポインタ
    1. 1. 変数のアドレスとは
      1. 1-1. アドレスの取得
    2. 2. ポインタ
      1. 2-1. ポインタとは
      2. 2-2. ポインタの定義
      3. 2-3. ポインタの初期化
      4. 2-4. ポインタとswap関数
      5. 2-5. 動的なメモリの生成
        1. 2-5-1. malloc()関数
        2. 2-5-2. free()関数
        3. 2-5-3. calloc()関数
        4. 2-5-4. realloc()関数
  10. 文字列の操作
    1. 1. 文字列の結合
    2. 2. 文字列のコピー
    3. 3. 文字列の長さを取得
    4. 4. 文字列の比較
    5. 5. 文字列から数値への型変換
      1. 5-1. int型へ変換
      2. 5-2. double型へ変換
    6. 6. 数値から文字列への変換
  11. 構造体
    1. 1. 構造体とは
    2. 2. 構造体の定義
    3. 3. 構造体のメンバ変数にアクセス
    4. 4. 構造体配列
    5. 5. 構造体名の変更
    6. 6. 構造体のポインタにアクセス
  12. ファイルの操作
    1. 1. テキストファイルとバイナリファイル
    2. 2. テキストファイルの操作
      1. 2-1. ファイルのポインタ宣言
      2. 2-2. ファイルのオープン
      3. 2-3. ファイルに書き込み
      4. 2-4. ファイルの読み込み
        1. 2-4-1. 1行ごとに読み込む
        2. 2-4-2. 1文字ごとに読み込む
      5. 2-4. ファイルのクローズ
      6. 2-5. プログラムの終了
    3. 3. バイナリファイルの操作
      1. 3-1. バイナリファイルのポインタ宣言
      2. 3-2. バイナリファイルのオープン
      3. 3-3. バイナリファイルに書き込み
      4. 3-4. バイナリファイルの読み込み
    4. 4. ファイルサイズの取得

C言語でHelloWorldを表示してみよう

1. HelloWorldを表示してみよう
2. コードの理解
3. コンソールに出力
4. コメントアウト
5. エスケープシーケンス
6. コンパイルエラー処理

1. HelloWorldを表示してみよう

1-1. プロジェクトの作成

① メニューバーの「ファイル」から「新規」→「プロジェクト」を選択します。

② 「C/C++」から「Cプロジェクト」を選択し、「次へ」をクリックします。

③ 以下を入力、選択し、「完了」をクリックします。

  • プロジェクト名(ここではHelloWorldとしています)
  • プロジェクト・タイプを「実行可能」→「空のプロジェクト」に選択
  • ツールチェーンを「MacOSX GCC」に選択

プロジェクトが出来上がりました。

④ 出来上がったプロジェクトを右クリックして「新規」→「ソース・フォルダー」を選択します。

⑤ フォルダー名を入力し(ここではsrcとしています)「完了」をクリックします。

⑥ プロジェクト内に出来たフォルダを右クリックし、「新規」→「ソース・ファイル」を選択します。

⑦ 「ソース・ファイル」を入力し、「完了」をクリックします。
※ここではHelloWorld.cとしています。

1-2. コーディング

① ファイルが作成されました。デフォルトでコメントが表示されるので、不要であれば削除してください。

② 以下のコードを記述します。

#include<stdio.h>  // ヘッダファイルの読み込み

int main(void) {
	printf("HelloWorld");
	return 0;
}

③ メニューバーの「プロジェクト」から「すべてビルド」を選択しビルドします。

ビルドが成功したら下部コンソールに完了メッセージが出てきます。
コードに間違いがある場合は、エラーメッセージが出てきます。

④ メニューバーの「実行」から「実行」→「ローカルC/C++アプリケーション」を選択します。

⑤ 実行結果が下部コンソールに出力されます。

2. コードの理解

以下のコードを解説していきます。

#include <stdio.h>

int main(int argc, char** argv){
    printf("Hello World.\n");
    return 0;
}
#include <stdio.h>

stdio.h は「標準入力・出力」ライブラリを含むヘッダーファイルで、このファイルには、コンソールに文字列を表示する printf や、ユーザーから入力を受け取る scanf などの関数が定義されています。
#includeは、ヘッダファイルを読み込むときに用いる宣言です。ここで読み込むファイルは、stdio.hというファイルです。“.h”は、C言語のヘッダファイルの拡張子です。

int main(void)

main 関数は、Cプログラムが実行される際に最初に呼び出される関数です。intmain 関数が整数を返すことを意味し、void はこの関数が引数を取らないことを示しています。
プログラムのエントリーポイントです。main 関数からプログラムの実行が始まり、main 関数内のコードが順番に実行されます。

printf("HelloWorld");

printf は文字列を画面に表示するための関数です。このコードでは、"HelloWorld" という文字列を画面に表示します。

return 0;

returnmain 関数の終了を示すもので、後に続く値(ここでは 0)をプログラムがオペレーティングシステムに返します。
0 は「正常終了」を意味し、プログラムがエラーなく終了したことを示します。通常、main 関数の最後に return 0; が書かれます。

3. コンソールに出力

コンソールに出力する関数があります。

3-1. printf()関数

文字列をコンソールに出力します。
改行はされません。改行するには、最後に’¥n’を付与します。
数値の場合は、文字列に変換する必要があります。

printf("%d", 100);

3-2. puts()関数

文字列をコンソールに出力します。
改行は行われます。
数値の場合は、文字列に変換する必要があります。

puts("Hello");
puts("World");

4. コメントアウト

コメントアウトしたい行に「/* */」や「//」を付与すると、その行は処理されません。

#include<stdio.h>

int main(void) {
//	printf("HelloWorld");  // コメントアウト
	return 0;
}

5. エスケープシーケンス

エスケープシーケンスを使用することにより、改行や「”」などを表現することができます。

エスケープシーケンス意味
∖a警告音
∖bバックスペース
∖n改行
∖tタブ
∖¥文字としての¥
∖?文字としての?マーク
∖”“(ダブルクオーテーション)
∖’‘(シングルクオーテーション)
∖0null(ヌル)文字

6. コンパイルエラー処理

以下のコードには、コンパイルエラーが発生しているため、ビルドに失敗します。

コンソールを確認すると、青色の部分で5行目は「printf?」と表示され、コードの5行目にも赤色点線が付与されています。
コード5行目を見ると「print」のため、「printf」とするとコンパイルエラーは解消されます。

演算

1. 四算演算子
2. 複合代入演算子
3. 比較演算子
4. 論理演算子
5. 単項演算子(インクリメント、デクリメント)

1. 四算演算子

C言語の演算子には、以下のものがあります。

演算の種類演算子の記号記述例
加算+2 + 1
減算2 – 1
乗算*2 * 1
除算/2 / 1
剰余%2 % 1

例文

#include <stdio.h>

int main(void){
    printf("%d", 2+1);  //加算
    printf("%d", 2-1);  //減算
    printf("%d", 2*1);  //乗算
    printf("%d", 2/1);  //除算
    printf("%d", 2%1);  //剰余
    return 0;
}

printf関数には表示する形式を指定するフォーマット文字列が必要のため、「%d」に演算結果を代入し表示します。

2. 複合代入演算子

C言語における主な代入演算は、以下のとおりです。

複合代入演算子記述例該当する演算
+=a += 1;a = a + 1;
-=a -= 1;a = a – 1;
*=a *= 2;a = a * 2;
/=a /= 2;a = a / 2;
%=a %= 2;a = a % 2;

3. 比較演算子

比較演算子は、ある値と別の値を比較し、その結果をtrueまたはfalseで返します。
主な比較演算子は以下のとおりです。

比較演算子概要
>より大きい場合は、true
>=以上の場合は、true
<より小さい場合は、true
<=以下の場合は、true
==等しい場合は、true
!=等しくない場合は、true

4. 論理演算子

論理演算子とは、ある値と別の値を「および」や「または」で評価し、その結果をtrueまたはfalseで返します。
主な論理演算子は以下のとおりです。

論理演算子概要
a && baとbがどちらもtrueの場合、true
a || baまたはbのどちらかがtrueの場合、true
!aaがtrueでない場合、true

5. 単項演算子(インクリメント、デクリメント)

変数に1を足す、もしくは引くだけの処理を行う際、変数の前後どちらかに「++」を付けた処理をインクリメント、変数の前後どちらかに「–」を付けた処理をデクリメントといいます。

インクリメント

$変数名++;

デクリメント

$変数名--;

変数

1. 変数
2. データ型
3. 変数の命名規則
4. 型変換

1. 変数

1-1. 変数とは

変数とは、数値や文字列などの値を入れるための箱のようなもので、中の値を変更することが可能です。
この変数には、値を1つだけ格納することが可能です。

1-2. 変数の定義

変数の宣言する方法は以下のとおりです。

データ型 変数名;

例文

int num;  // int型で変数numを宣言

変数の宣言と初期化を同時に行います。

データ型 変数名 = 代入する値;

例文

int num = 1;  // int型で変数numを宣言し、整数1で初期化

複数の変数を同時に宣言できます。

データ型 変数名, 変数名;

例文

int num1, num2;  // int型で変数num1とnum2を宣言

複数の変数を同時に宣言した場合、それぞれの変数を初期化できます。
例文

int c = 1, d = 2;  // 整数型変数a,bを宣言と同時に両方に値を代入
int e, f = 3;  // 整数型変数a,bの宣言と同時にbにのみ値を代入

1-3. 代入

変数に対して値を代入することができ、何度も値を変えることができます。

例文

int num = 1;  // int型で変数numを宣言し、整数1で初期化
printf("%d", num);  // 1が表示される
num = 2;  // 変数numに対して2を代入
printf("%d", num);  // 2が表示される

2. データ型

C言語には以下のデータ型があります。

データ型概要
char1バイトの符号付整数。ASCIIコードといった文字コードに使用。
unsigned char1バイトの符号なし整数。
short2バイトの符号付整数。
unsigned short2バイトの符号なし整数。
long4バイトの符号付整数。
unsigned long4バイトの符号なし整数。
int2または4バイトの符号付整数。(コンパイラに依存)
unsigned2バイトまた4バイトの符号なし整数。(コンパイラに依存)
float4バイトの単精度浮動小数点実数。
double8バイトの倍精度浮動小数点実数。

3. 変数の命名規則

変数名を命名する時のルールとして、半角アルファベット(a~z、A~Z)、数字、_(アンダーバー)が使用でき、大文字と小文字を区別します。
変数名は自由に付けることができますが、数字や、C言語にあらかじめ定義されている予約語は名前に付けられません。
開発現場では、そのプロジェクトの開発規約に沿って定義しますが、変数名は基本的にはキャメルケースを使用します。

4. 型変換

変数に型の異なる値を代入することができます。
型変換を行う場合は、「( )」を使用して変換する値への型変換を行います。
例文

int i1 = 3;
double d1 = 1.23;
int i2 = (int) d1;  // int型にキャスト
double d2 = (double) i1;  // double型にキャスト

条件分岐

1. 条件分岐とは
2. if文
3. switch文

1. 条件分岐とは

if文は「もし明日晴れだったら公園に行く、雨だったら家で過ごす」というような、条件によって処理を分けたいときに使います。
C言語での条件分岐には、if文とswitch文があります。

2. if文

2-1. if文

if文による条件分岐は以下のように定義します。

if (条件式) {  //①
  条件式でtrueの時の処理 //②
}

① 条件式には結果がbool型で返されるような式にします。(trueかfalseを返す)
 比較演算子や論理演算子が使用できます。
② 処理は①の条件式により結果がtrueとなった場合に、実行されます。そのため、①がfalseの場合②の処理は実行されません。

例文

#include <stdio.h>

int main(void) {
	int num = 3;

	if (num == 3) { //①
		printf("変数numは3です"); //②
	}

	return 0;
}

① 変数numの値は3であり、if文により3なのかをチェックしています。
② ①のチェックで3なので、②の処理が実行されます。

2-2. else文

上記の2-1. if文の例文で、条件式の結果がfalseのときなので何も処理されませんでした。
ここで、else文を使用するとfalseの場合の処理を行うことができます。

if (条件式) {
  条件式でtrueの時の処理
} else {
  条件式でfalseの時の処理
}

例文

#include <stdio.h>

int main(void) {
	int num = 1;

	if (num == 3) {
		printf("変数numは3です");
	} else {
		printf("変数numは3以外です");
	}

	return 0;
}

2-3. else if文

if~else の処理の場合2分岐のみですが、if elseを使うことで3つ以上の条件分岐を記述することができます。
else if文はいくつでも定義できます。
ここで注意すべきなのが、コードは上から下へ順に処理され、またif文は条件式でtrueと評価されると以降のelse ifの条件式は評価せずにそのif文を抜けます。そのため、上から条件に厳しい条件式を定義すると良いです。

if (条件式A) {
  条件式Aでtrueの時の処理
} else if (条件式B) {
  条件式Bでtureの時の処理
} else {
  上記の条件式全てでfalseの時の処理
}

例文

#include <stdio.h>

int main(void) {
	int num = 2;

	if (num == 1) {
		printf("変数numは1です");
	} else if (num == 2) {
		printf("変数numは2です");
	} else {
		printf("変数numは1と2以外です");
	}

	return 0;
}

2-4. if文のネスト

ネストとは、入れ子構造を意味し、if文の中にif文を記述できます。
いくつでも定義することができますが、複雑さや可読性などを考慮しあまり多くし過ぎるのも良くありません。

if (条件式){
    if (条件式){
    }
}

3. switch文

値が整数値であれば、switch文を用いてswitch文により条件分岐を行うことができます。

switch (値) { //①
  case 値1: //② 値はcaseの値1と同じかをチェック
    処理1; //③
    break; //④
  case 値2: //② 値はcaseの値2と同じかをチェック
    処理2; //③
  case 値3: //② 値はcaseの値3と同じかをチェック
    処理3; //③
    break; //④
  default: //⑤
    上記のcaseに該当しない場合の処理
}

① caseと比較する値を指定します。
② caseはswitchの値と比較され、同じであれば③の処理を行います。
③ ②のcaseで一致(true)した場合、その処理を行います。
④ breakは、そのcaseの処理を抜けます。すなわち、breakに到達しなければ処理を抜けないことを意味するため、case2にはbreakがないため、case3の処理も実行されます。(フォールスルー)
⑤ defaultは、caseに該当しない場合に実行されます。

繰り返し処理

1. 繰り返し処理とは
2. for文
3. while文
4. do-while文

1. 繰り返し処理とは

1〜100の数字を表示したい場合、以下のようにすると表示できます。

printf("%d", 1);
printf("%d", 2);
printf("%d", 3);
 // 3~99は省略
printf("%d", 100);

しかし、面倒です。
その時にC言語では繰り返し処理によるfor文またはwhile文を使用します。

2. for文

2-1. for文

for文による繰り返し処理は以下のように定義します。

for (初期化式; 条件式; 更新式) {  //①
  for文の条件式でtrueの際、繰り返される処理 //②
}

① 初期化式には、for文の処理のスタート地点を指定します(最初の一度だけ実行)
  条件式には、for文による繰り返し処理がどのくらいまで行うかの条件を指定します。
  更新式には、{ }内の処理を実行したあとに行われます。
② 処理は①の条件式により結果がtrueとなった場合に、実行されます。そのため、①がfalseの場合②の処理は実行されません。

例文

「1. 繰り返し処理とは」で出題した参考コードを、for文に置き換えてみます。
1からスタート(let i = 1;)し、100まで処理を繰り返します(i <= 100)。なので、100まではtrueの評価となります。
for文内の処理が終わると、初期化式の数値から1ずつ増えていきます(i++)。
条件式が101となるとfalseの評価となりこのfor文を抜けます。

#include <stdio.h>

int main(void) {

	for (int i = 1; i <= 100; i++) {
		printf("%d", i);
	}

	return 0;
}

2-2. for文のネスト

for文もif文と同様にネストすることができます。

for (初期化式; 条件式; 更新式){
    for (初期化式; 条件式; 更新式){
    }
}

3. while文 と do-while文

3-1. while文

while文による繰り返し処理は以下のように定義します。

初期化式 //①
while (条件式) { //②
  while文の条件式でtrueの際、繰り返される処理 //③
  更新式 //④
}

① 初期化式には、while文の処理の最初に、一度だけ実行されます。
② 条件式には、while文による繰り返し処理の条件を指定します。
③ 処理は②の条件式により結果がtrueとなった場合に、実行されます。そのため、②がfalseの場合②の処理は実行されません。
④ 更新式には、{ }内の処理を実行したあとに行われます。

例文

「1. 繰り返し処理とは」で出題した参考コードを、while文に置き換えてみます。

#include <stdio.h>

int main(void) {

	int i = 1;
	while(i <= 100) {
		printf("%d", i);
		i++;
	}

	return 0;
}

3-2. do-while文

do-while文による繰り返し処理は以下のように定義します。

do {
  処理 //①
} while (条件式); //②

① whileの条件式判定より前にdo{ } 内の処理が実行されます。
② ①の処理後に条件式判定を行います。以降の処理はwhile文と同じです。

配列変数

1. 配列変数とは
2. 配列変数の定義
3. 配列変数の要素を取得

1. 配列変数とは

変数を用いて大量のデータを扱う場合、配列変数を用いることができます。

2. 配列変数の定義

2-1. 配列変数の宣言

配列変数は、以下のように宣言します。

データ型 配列名[配列の大きさ];

例文

int numArr[3];

2-2. 配列変数の初期化

配列変数の宣言と初期化は同時に行うことができます。

データ型 配列名[配列の大きさ] = {値1, 値2, 値3・・・};

例文

int numArr[3] = { 11, 22, 33 };

3. 配列変数の要素を取得

配列変数に格納されている要素は、添え字を指定することで取得できます。

配列変数名[添え字];

例文

#include <stdio.h>

int main(void) {

	int numArr[3] = { 11, 22, 33 };
	printf("%d", numArr[0]);  // ①
	printf("%d", numArr[1]);  // ②
	printf("%d", numArr[2]);  // ③

	return 0;
}

① 0番目の要素を取得し、11を表示
② 1番目の要素を取得し、22を表示
③ 2番目の要素を取得し、33を表示

関数

1. 関数とは
2. 関数の定義

3. 変数のスコープ

1. 関数とは

関数とは、計算を行う処理など何度でも再利用可能な処理をまとめたものを定義することができます。

2. 関数の定義

2-1. 関数の定義

関数の定義には以下のように定義します。

戻り値のデータ型 関数名(引数1の型 引数1, 引数2の型 引数2,・・・) { //①
    処理
    return 返す値; //②
}

① 引数は省略でき、またカンマで区切って複数指定できます。
 この受け取った引数を使用して関数内の処理に使用していきます。
② returnは戻り値であり、関数の処理結果をこの関数を呼び出した元へ返します。省略可能です。
またreturnは処理制御を戻す意味もあり、このreturn直後にコードを記述すると到達できないコードとなりエラーになります。

2-2. 関数の呼び出し

関数は、関数名と引数の数を合わせてることで呼び出せます。

例文

#include <stdio.h>

int add(int val1, int val2) {  //②
	int result = 0;
	result = val1 + val2;
	return result;
}

int main(void) {
	int addResult;
	addResult = add(1, 2);  //①③
	printf("%d", addResult);
	return 0;
}

① 関数addを呼び出します。
② 計算を行う関数を定義しました。
 この関数は引数1と引数2の値を足し算し、その結果を戻り値として返します。
③ 関数addの戻り値を変数addResultに代入します。

2-3. 関数の定義と呼び出しの注意点

C言語では呼び出す関数を先に定義しておかなければエラーとなります。
例文(エラー)

#include <stdio.h>

int main(void) {
	int addResult;
	addResult = add(1, 2);   //関数addを呼び出す
	printf("%d", addResult);
	return 0;
}

int add(int val1, int val2) {  //先に定義されていないため、エラー
	int result = 0;
	result = val1 + val2;
	return result;
}

そのため、関数のプロンプト宣言をしておくと、C言語のコンパイラに「これからこのような関数を定義しますよ」ということを伝えることができます。

データ型 関数名(引数データ型, 引数データ型・・・・);

例文

#include <stdio.h>

int add(int, int);   //関数addのプロンプト宣言

int main(void) {
	int addResult;
	addResult = add(1, 2);
	printf("%d", addResult);
	return 0;
}

int add(int val1, int val2) {
	int result = 0;
	result = val1 + val2;
	return result;
}

3. 変数のスコープ

変数のスコープとは、変数の有効範囲のことです。
グローバル変数とローカル変数があります。

概要有効範囲
グローバル変数ファイル(.cファイルなど)全体で有効なスコープ。
ファイルの冒頭で宣言される。
ファイル内全体
ローカル変数ブロック({}で囲まれた部分)内で定義された変数。
if、while、for文の中や関数の中で宣言された変数
ブロック内

ファイル分割

1. ファイル分割とは
2. ヘッダファイルのインクルード

3. グローバル変数の扱い

1. ファイル分割とは

C言語は「.c」のファイルに記述していましたが、大規模になってくると1つのファイルに収まりません。
ファイルは、規模に応じて複数に分割されることになりますが、ファイルが分割されれば他のファイルにある関数を呼び出すことが出来なくなります。
そこで、C言語には「.hのヘッダファイル」と「.cのソースファイル」があり、ヘッダファイルに、.cファイルに記述されているファイルの内容を記述しておき、それを読み込むことにより、他の.cファイルに記述されている関数を利用することが出来るようになります。
以降、以下のコードを基にファイル分割していきます。

#include <stdio.h>

int add(int, int);

int main(void) {
	int addResult;
	addResult = add(1, 2);
	printf("%d", addResult);
	return 0;
}

int add(int val1, int val2) {
	int result = 0;
	result = val1 + val2;
	return result;
}

以下の用にファイル分割ができます。

ヘッダファイル(Fun.h)

#ifndef _FUN_H_
#define _FUN_H_


int add(int, int);

#endif

定義された関数用ファイル(Fun.c)

#include "Fun.h"  //ヘッダファイルのインクルード

int add(int val1, int val2) {
	int result = 0;
	result = val1 + val2;
	return result;
}

コントロールを行うファイル(Main.c)

#include <stdio.h>
#include "Fun.h"  //ヘッダファイルのインクルード

int main(void) {
	int addResult;
	addResult = add(1, 2);
	printf("%d", addResult);
	return 0;
}

2. ヘッダファイルのインクルード

2-1. ヘッダファイルの定義

ヘッダファイルは以下のように定義します。

#ifnfdef _ヘッダファイル名を大文字で記述_H_
#define _ヘッダファイル名を大文字で記述_H_

プロトタイプ宣言;
プロトタイプ宣言;
            :


#endif //  _ヘッダファイル名を大文字で記述_H_

2-2. ヘッダファイルの読み込み

ヘッダファイルを#includeを使用して、ソースファイルに読み込みます。

#include "ヘッダファイル名"

3. グローバル変数の扱い

グローバル変数は、プログラムが複数に分割された場合、宣言されているファイル以外の場所では使えなくなってしまいます。
その場合、extern修飾子を付与することにより、.cソースファイル間で使用できるようになります。

extern データ型 変数名;

アドレスとポインタ

1. 変数のアドレス
2. ポインタ

1. 変数のアドレスとは

変数はPC端末のメモリ中に存在するため、アドレスがあります。
C言語では、そのアドレスを扱うことができます。

1-1. アドレスの取得

アドレスを取得するには、変数名の先頭に「&」を付与します。また、メモリサイズを取得するにはsizeof演算子を使用します。
sizeof演算子は、変数や型のメモリのサイズを取得する演算子で、引数に変数や型を指定すれば、そのサイズをバイト単位で取得できます。

sizeof(変数や型)

例文

#include <stdio.h>

int main(int argc,char** argv){
    int i = 100;		//	int型の変数
    printf("変数iの値は%d、大きさは%dbyte、アドレスは0x%x\n",i,sizeof(int),&i);
    return 0;
}

実行結果

変数iの値は100、大きさは4byte、アドレスは0x6d7df20c

2. ポインタ

2-1. ポインタとは

ポインタ(ポインタ変数)とは、アドレスを入れることを前提とした変数のことです。
ポインタと変数の違いは以下のとおりです。

ポインタ変数概要
宣言int *i;int i;ポインタ変数は、変数の先頭に*をつける
*iiポインタ変数で値を示すには、先頭に*をつける必要がある
アドレスi&a通常の変数は、値を入れるが前提だが、ポインタ変数はアドレスを入れる

2-2. ポインタの定義

ポインタを定義するには、変数名の先頭に「*」を付与します。

データ型 *変数名;

例文

int *num;

2-3. ポインタの初期化

ポインタはNULLで初期化するのか基本となっています。
例文

int *p = NULL;

しかし、初期化したまま何らかの変数のアドレスを設定しなかった 場合、コンパイルエラーは出ませんが、実行時エラーが出てプログラムは止まってしまうことに注意が必要です。

2-4. ポインタとswap関数

変数は1つの戻り値しか返すことができませんが、swap関数の引数にポインタは、アドレスを与えられた二つの変数の値を入れ替えることができます。

例文

#include <stdio.h>

void swap(int*, int*);

int main(int argc, char** argv){
	int a = 1,b = 2;
    printf("a = %d b = %d\n",a,b);
    swap(&a,&b);
    printf("a = %d b = %d\n",a,b);
    return 0;
}

// 値の入れ替えを行う関数を定義
void swap(int* num1,int* num2){
	int temp = *num1;
	*num1 = *num2;
	*num2 = temp;
}

2-5. 動的なメモリの生成

ポインタの既にある変数のアドレスを取得するという方法を使えば必要な個所で動的にメモリを確保し、必要がなくなったときに破棄すると言う事が出来るようになります。
必要な時に確保して、必要が無くなったら破棄できるという点がメリットです。例えば、画像のファイルなどは容量が大きく、メモリを圧迫することから、必要な時だけ保持し、不必要になったら破棄することが出来ればメモリの節約をすることが可能です。

2-5-1. malloc()関数

メモリを動的に確保する関数は、malloc()です。()内に確保したいメモリの大きさをバイト数で指定します。

malloc(確保したいメモリの大きさ);

例文

malloc(sizeof(int)*SIZE);  //int型のbyte数を指定
2-5-2. free()関数

メモリを解放するには、free()の関数を使用します。()内には、確保したメモリのアドレスを持つポインタを引数として与えます。

free(ポインタ);

例文

int* p1 = NULL;
// メモリの開放
free(p1);
2-5-3. calloc()関数

mallocと基本は一緒ですが、生成された領域はNULLではなく、全て0で初期化されています。

2-5-4. realloc()関数

一度確保したメモリを違うサイズで確保し直します。

文字列の操作

1. 文字列の結合
2. 文字列のコピー
3. 文字列の長さを取得
4. 文字列の比較
5. 文字列から数値への型変換
6. 数値から文字列への変換

1. 文字列の結合

文字列変数に対して別の文字列変数(文字列)を結合するには、strcat()関数を使用します。
右辺の文字列を左辺の文字列変数(文字列)に追加します。

strcat(文字列変数, 結合したい文字列変数や文字列);

2. 文字列のコピー

文字列変数に対して第2引数に指定した文字列をコピーするには、strcpy()関数を使用します。
右辺の文字列を左辺の文字列変数にコピーします。

strcpy(文字列変数, コピーしたい文字列変数や文字列);

3. 文字列の長さを取得

文字列の長さは、strlen()関数を用いて取得します。
()に文字列を与えると、戻り値として長さを得られる。

strlen(長さを知りたい文字列);

4. 文字列の比較

文字列を比較するには、strcmp()関数を使用します。
比較を行い、等しければ0を返します。

strcmp(文字列, 比較を行いたい文字列);

5. 文字列から数値への型変換

5-1. int型へ変換

文字列から整数へ変換するには、atoi()関数を使用します。

atoi(文字列の整数);

例文

atoi("100");

5-2. double型へ変換

文字列から浮動小数点へ変換するには、atoi()関数を使用します。

atof(文字列の浮動小数点);

例文

atoi("1.23");

6. 数値から文字列への変換

printf()関数と同様に、数値から文字列へ変換できるsprintf()関数があります。

sprintf(変数, "%d", 数値);

例文

int a = 100;
sprintf(s1,"%d",a);

構造体

1. 構造体とは
2. 構造体の定義
3. 構造体のメンバにアクセス
4. 構造体配列
5. 構造体名の変更

6. 構造体のポインタにアクセス

1. 構造体とは

構造体とは、複数の変数をひとまとめにするもので、Javaでいうオブジェクトのようなものです。

2. 構造体の定義

構造体の定義を構造体テンプレートと言い、structが、構造体を表すキーワードです。
{ }の中に、ひとまとめにするメンバ変数を定義します。最後は;(セミコロン)で終了します。

struct 構造体名 {
    メンバ変数名1;
    メンバ変数名2;
};

3. 構造体のメンバ変数にアクセス

構造体を呼び出すには、呼び出したいメソッド内で構造体変数の定義を行います。

struct 構造体名 構造体変数名;

この構造体変数名を使用して、構造体内で定義されているメンバ変数を呼び出します。
呼び出すには「 . 」を使用します。

構造体変数名.構造体内の呼び出したいメンバ変数名;

例文

#include <stdio.h>
#include <string.h>

// 社員のデータを入れる構造体
struct employee{
	int id;	//社員番号
	char name[256];	//名前
	int age; //年齢
};

int main(int argc,char** argv){
    struct employee data;
	data.id = 1;	// 社員番号を設定
	strcpy(data.name,"テスト太郎");	// 名前を設定
	data.age = 22;	// 年齢を設定

	// データの内訳を表示
	printf("社員番号:%d 名前:%s 年齢:%d\n",data.id,data.name,data.age);
    return 0;
}

4. 構造体配列

上記では、1人の社員情報しか扱っていませんでした。ここで複数の社員情報を扱いたい場合は、構造体配列を使用します。

struct student data[] = {
    { structの定義内容 },
    { structの定義内容 },
    { structの定義内容 }
};

例文

#include <stdio.h>
#include <string.h>

// 社員のデータを入れる構造体
struct employee {
    int id;  //社員番号
    char name[256]; //名前
    int age; //年齢
};

// 構造体の定義
struct employee data;

int main(int argc, char **argv) {
    int i;

    // 構造体配列
    struct employee data[] = {
        { 1, "テスト太郎", 18 },
        { 2, "テスト花子", 19 },
        { 3, "テスト次郎", 18 },
        { 4, "テスト三郎", 18 }
    };

    // for文で構造体配列を表示
    for (i = 0; i < 4; i++) {
	printf("社員番号:%d 名前:%s 年齢:%d\n", data[i].id, data[i].name, data[i].age);
    }
    return 0;
}

5. 構造体名の変更

構造体名を変更するには、typedefを付与します。

typedef struct 旧構造体名 新構造体名;

typedefが付与された構造体は、先頭に”struct”キーワードをつけることなく構造体変数を定義することが可能となります。

新構造体名 構造体変数名;

例文

#include <stdio.h>
#include <string.h>

// 社員のデータを入れる構造体
struct employee {
    int id;  //社員番号
    char name[256]; //名前
    int age; //年齢
};

// 構造体の定義
typedef struct employee data;

int main(int argc, char **argv) {
    int i;
    data data[] = {
        { 1, "テスト太郎", 18 },
        { 2, "テスト花子", 19 },
        { 3, "テスト次郎", 18 },
        { 4, "テスト三郎", 18 }
    };

    // for文で構造体配列を表示
    for (i = 0; i < 4; i++) {
	printf("社員番号:%d 名前:%s 年齢:%d\n", data[i].id, data[i].name, data[i].age);
    }
    return 0;
}

6. 構造体のポインタにアクセス

構造体のポインタにアクセスするには、「->(アロー演算子)」を使用します。

void setData(student_data* data,int id,char* name,int age){
    data -> id = id;   // idにアクセス
    data -> name = name;   // 名前にアクセス
    data -> age = age;   // 年齢にアクセス
}

ファイルの操作

1. テキストファイルとバイナリファイル
2. テキストファイルの操作
3. バイナリファイルの操作
4. ファイルサイズの取得

1. テキストファイルとバイナリファイル

ファイルは大きく分けて「テキストファイル」と「バイナリファイル」に分けられます。

ファイルの種類概要
テキストファイル文字として読めるデータ.txtファイル、.cファイル、.htmlファイルほか
バイナリファイル文字として読めないデータ
(画像や音声など)
.pngファイル、.wavファイルほか

2. テキストファイルの操作

テキストファイルを操作するには、あらかじめ用意されている機能を使用します。

2-1. ファイルのポインタ宣言

ファイルを操作するするには、ポインタを宣言する必要があります。

FILE *file;  // *fileの部分がファイルポインタです

2-2. ファイルのオープン

ファイル操作を行うファイルをfopen()関数を用いてオープンします。
また、第2引数にそのファイルに対して行う処理モードを指定します。
ファイルのオープンが失敗すると、NULLが返されます。

file = fopen(ファイルが格納されていパス, "処理モード");

処理モード

処理モード概要対象ファイルが存在しない場合
rテキストデータの読み込みエラーになる
wテキストデータの書き込みファイルを新規作成する
aテキストデータへの追加書き込みファイルを新規作成する
r+テキストデータを更新モードで開く(書き込み・読み込み共に可)エラーになる
w+テキストデータを更新モードで開く(書き込み・読み込み共に可)ファイルを新規作成する
a+テキストデータを更新モードで開く(書き込み・読み込み共に可)ファイルを新規作成する

2-3. ファイルに書き込み

オープンしたファイルに書き込むには、fprintf()関数を使用します。

fprintf(書き込みを行う内容);

2-4. ファイルの読み込み

2-4-1. 1行ごとに読み込む

テキストファイルから1行ごと読み込むには、fget()関数を使用します。

fget(文字列, 文字列サイズ, ファイルポインタ);

なお、読み込みが成功すれば、これと同じ値が返ってきます。読み込みがうまくいかなかったり、最後まで読みきった場合は、NULLが返ってきます。

2-4-2. 1文字ごとに読み込む

テキストファイルから1文字ごと読み込むには、fgetc()関数を使用します。

fgetc(ファイルポインタ);

戻り値が文字コードとなっており、ファイルの最後まで到達すると、EOFが返ってきます。そのため、EOFが出現するまでファイルを読み続け、表示することにより、読み込んだファイルが全て表示される仕組みになっています。

2-4. ファイルのクローズ

ファイルの操作が終了したら、fclose()関数を使用してクローズを行います。

fclose(ファイルポインタ);

2-5. プログラムの終了

プログラムの終了にはexit() 関数を用います。引数 status は 0 以上 255 以下の値を設定し、正常終了の場合には0を設定します。

exit(statusの値);

3. バイナリファイルの操作

バイナリファイルを操作するテキストファイルと同様に、あらかじめ用意されている機能を使用します。

3-1. バイナリファイルのポインタ宣言

バイナリファイルを操作するするには、ポインタを宣言する必要があります。

FILE *file;  // *fileの部分がファイルポインタです

3-2. バイナリファイルのオープン

バイナリファイル操作を行うファイルをfopen()関数を用いてオープンします。
また、第2引数にそのファイルに対して行う処理モードを指定します。
ファイルのオープンが失敗すると、NULLが返されます。

file = fopen(ファイルが格納されていパス, "処理モード");

処理モード

処理モード概要対象ファイルが存在しない場合
rbバイナリデータの読み込みエラーになる
wbバイナリデータの書き込みファイルを新規作成する
abバイナリデータへの追加書き込みファイルを新規作成する
rb+ / r+bバイナリデータを更新モードで開く(書き込み・読み込み共に可)エラーになる
wb+ / w+bバイナリデータを更新モードで開く(書き込み・読み込み共に可)ファイルを新規作成する
ab+ / a+bバイナリデータを更新モードで開く(書き込み・読み込み共に可)ファイルを新規作成する

3-3. バイナリファイルに書き込み

オープンしたバイナリファイルに書き込むには、fwrite()関数を使用します。

fwrite(データ, データのバイト長, データの数, ファイルポインタ);

3-4. バイナリファイルの読み込み

バイナリファイルを読み込むには、fread()関数を使用します。

fread(データ, データのバイト長, データの数, ファイルポインタ);

4. ファイルサイズの取得

ファイルを読み込む際、ファイルの大きさを取得し、その分のメモリを確保してデータを読み込む必要があります。
そこで、ファイルサイズを取得する以下の関数を使用します。

関数構文概要
fseek()fseek(ファイルポインタ, 移動バイト数, 開始位置);ファイルを開始位置から、移動バイト数移動する
ftell()ftell(ファイルポインタ);現在のファイル位置の値をバイト数で返す

開始位置

開始位置の定数概要
SEEK_SETファイルの先頭
SEEK_CURファイルの現在位置
SEEK_ENDファイルの終端