共用体

Posted コメントするカテゴリー: C

共用体とは、1つのアドレスに異なるデータを割りあてることです。

共用体は、1つのメモリ領域で異なる型の変数のどれかひとつを選んで使うことができます。
共用体の宣言は以下のようになります。

//「uniondata」は共用体名、「unilist1」は共用体変数
union uniondata{
    int no;
    char name[10];
    float weight;
};
union uniondata unilist1;

共用体を使ったサンプルを試してみます。

#include <stdio.h>

int main(void)
{

	//「uniondata」は共用体名、「unilist1」は共用体変数
	union uniondata{
	    int no;
	    char name[10];
	    float weight;
	};
	union uniondata unilist1;


	unilist1.no = 1;
	printf("%d\n", unilist1.no);
	
	strcpy(unilist1.name, "test!!");
	printf("%s\n", unilist1.name);
	
	unilist1.weight = 123.4;
	printf("%f\n", unilist1.weight);

	//結果以下のように表示されました
//1
//test!!
//123.400002

	return 0;
}

unilist1がメモリに占める領域は一番大きな型にあわせた大きさになる。
共用体はまだまだ深いので、改めて追記して理解を深めることにします。

引数つきマクロ

Posted コメントするカテゴリー: C

#defineを使うと、引数をもち、関数のように動作するマクロを定義することができます。

#define HIKU(x, y) ((x) - (y))

上記の書き方は有効範囲は1行です。
複数行の場合は次のように定義します。

#define hiku2(a, b, c) ((a) * (b) * (c) )\
+ a+b+c)

実際に動かしてみます

#include <stdio.h>

#define HIKU(x, y) ((x) - (y))

int main(void)
{
    int i;
    
    printf("マクロ!%d\n", HIKU(10, 8));
    
    return 0;
}

//結果は「マクロ!2」と表示されます

引数つきマクロは、実行する処理全体とその中の引数はカッコでくくります。

//正しい例
#define HIKU(x, y) ((x) - (y))

//間違った例
#define HIKU(x, y) (x - y)

マクロ名と()の間にスペースを入れてしまうと、区切りが正しく認識されません。

//正しい例
#define HIKU(x, y) ((x) - (y))

//間違った例
#define HIKU (x, y) ((x) - (y))

条件に応じたコンパイル指示

Posted コメントするカテゴリー: C

条件に応じて、必要な部分だけ抜き出してコンパイルしたい場合、次のように書きます。

#if 条件
    指定範囲
#endif
#ifdef 識別子
    指定範囲
#endif
#ifndef 識別子
    指定範囲
#endif

また、複数の条件を判断することもできます。

#ifdef 識別子
    範囲指定A   //指定範囲Aをコンパイルする
#elsif 条件B
    範囲指定B   //指定範囲Bをコンパイルする
#else
    範囲指定C   //指定範囲Cをコンパイルする
#endif

複数のプログラムファイル内でヘッダファイルを使うと、同じヘッダファイルを重複インクルードしてしまう場合があります。
これを防ぐためには次のように書きます。

#ifndef _MYHEADER_
#define _MYHEADER_
    //2回目以降は既に呼ばれているので、この内容は読み込まれない
    void MyFunc();
    extern int x;
#endif

マクロ

Posted コメントするカテゴリー: C

#からはじまる1行分のことをマクロと呼びます。
マクロはプログラムのソースコードをコンパイルする前にプリプロセッサが処理します。

プログラムの最初に記述する「#include」もマクロの一種です。

マクロは次のような書式で書きます。

//マクロの中にはスペースやタブは入れてはダメ
#define マクロ名 パターン

■置換
#defineは、文字列を置換するマクロです。

#define VALNUM 3;

次のように書くと、DEBUG_MODEが定義されているということを表します。

#define DEBUG_MODE;

簡単なサンプルを書いてみましたが、うまく動作せず。

#include <stdio.h>

#define VALNUM 3;

int main(void)
{
    printf("%d \n", VALNUM);
    
    
    return 0;
}


//実行結果
「7行目」で記述エラーを発見しました。
「,」を付け忘れています。

もうひとつ、サンプルを書きましたが、これもうまく動作しませんでした。原因について調査中、、、

#include <stdio.h>

#define VALNUM 3;

int main(void)
{
	int i;
	
	for (i=0; i<VALNUM; i++) {
	    printf("%d \n", i);
	}
    
    return 0;
}

//実行結果
「9行目」で記述エラーを発見しました。
「identifier」を付け忘れています。

エラーの原因から地道に紐解いていこうかと思います。
解決したら、ブログに追記します。

2014.09.25追記
コメントからご指摘いただいた点を元に、下記のようなプログラムを作って動かしてみました。

#include <stdio.h>
 
#define VALNUM 3
 
int main(void)
{
    int i;
     
    for (i=0; i<VALNUM; i++) {
        printf("%d \n", i);
    }
     
    return 0;
}

エラーが出ずに実行され、コンソール上には「0 1 2」という表示が出ました。
ケアレスミスでした。

static宣言

Posted コメントするカテゴリー: C

グローバル変数をstaticをつけて宣言すると、変数の有効範囲はその宣言をしたファイル内に限られます。

外部変数を宣言して、他のファイルから参照することはできません。

例として次のような場合

//ソースファイル1で記述
static int gvar;

//ソースファイル2で記述
extern int gvar;

//ソースファイル3で記述
extern int gvar;

上記のようにソースファイル2やソースファイル3からgvarを呼び出そうとしてもエラーになります。

■static宣言したローカル変数
static変数はプログラムの開始から終了まで、値が削除されません。
ローカル変数は関数の中で宣言され、関数の処理が終わると同時に破棄されます。

■const宣言
const宣言は変数の値を書き換えられないようにする宣言です。
定数として変数を使用する場合は、この宣言を使います。

const宣言を関数の引数で指定している場合、その引数は関数の中で値が保持されることを保障する動きになります。

ファイルの分割と組み立て

Posted コメントするカテゴリー: C

ソースファイルの分割

ひとつのソースファイルに大量のプログラムコードを書くと良くないので、分割をしてソースファイルを管理します。

通常、プログラムを構成する「機能」ごとにソースファイルを分割します。

ソースファイルを分割すると、どのプログラムファイルからも参照できる変数が必要になってきます。
その変数を「外部変数」とよび、変数を宣言することを「外部変数宣言」といいます。

ソースファイルの分割例

本体ファイル①

int global_val;

分割ファイル①

extern int global_val;

分割ファイル②

extern int global_val;

上記のように記述します。

■ヘッダファイルを利用する
ヘッダファイルを利用することにより、さらに効率的にプロジェクトを管理できるようになります。

コンパイルとリンク

Posted コメントするカテゴリー: C

実行ファイルを作るには、C言語のソースを書いて、「コンパイル」、「リンク」することで完成します。

コンパイルとリンクを合わせて「ビルド」または「メイク」と呼びます。

ヘッダファイル

Posted コメントするカテゴリー: C

拡張子が「h」のファイルのことをヘッダファイルと呼びます。

ヘッダファイルは、プロトタイプ宣言、構造体や定数の定義などのテキストファイルを指し示す。

ソースファイルの中に組み込むと、それらの宣言や定義を利用できるようになります。

#include <stdio.h>

インクルードの書き方は、C言語に標準搭載されているヘッダファイルは次のように書きます。

#include <stdio.h>

また、自分で作ったヘッダファイルは次のように書きます。

#include "xxxxxxx.h"

C言語が用意しているヘッダファイルは次のようなものがあります。

ヘッダファイル  処理の種類
stdio.h         入出力
string.h        文字列処理
time.h          時間処理
math.h          数学処理

自分でヘッダファイルを作るには、次のように書きます。

//呼び出し元の拡張子cのファイル
#include "test.h"
main ()
{

}

void testfunc()
{

}

自作ヘッダファイル側

void testfunc();

型の再定義

Posted コメントするカテゴリー: C

長い型名を簡潔に名前をつけなおすことができます。(型の再定義)

//「unsigned char」を「u_char」という名前に定義している
typedef unsigned char u_char;
u_char c;

ポインタ型の再定義もあります。

//ポインタ型の場合
typedef unsigned int * pt_int;
pt_int a;

構造体名を再定義する方法

typedef struct data {
    int no;
    char name;
    int age;
} DATA;
DATA list1;

//↑の例は下記のように記述したものと同じになる
typedef struct data {
    int no;
    char name;
    int age;
};
struct data list1;

構造体と配列

Posted コメントするカテゴリー: C

構造体変数を配列として考えることを構造体配列と呼びます。

#include <stdio.h>
  
int main(void)
{
      
    //構造体配列の宣言(構造体テンプレートと、構造体配列を別々に書く場合)
    struct data{
        int no;
        char name[10];
        int age;
    };
    
    struct data list1[10];
    
    //構造体配列の宣言(構造体テンプレートと、構造体配列を同時に書く場合)
    struct data2{
    	int no;
    	char name[10];
    	int age;
    } list1[10];
    
    //構造体配列を初期化する場合(各々の配列内に値をセットしている)
    struct data2 list1[10] = {
    	{1, "testA", 10},
    	{2, "testB", 20},
    	{3, "testC", 32},
    	{4, "testD", 41}
    };
    
    //↑の例では、list1[10]と定義しているので、10個分の構造体配列を持たせることができる
    
    
    return 0;
}

上記プログラムを実行しただけでは、画面上にはなにもおこりません。
そこで、構造体配列の中身を参照する記述を書いてみます。

#include <stdio.h>
  
int main(void)
{
      
    //構造体配列の宣言(構造体テンプレートと、構造体配列を別々に書く場合)
    struct data{
        int no;
        char name[10];
        int age;
    };
    
    struct data list1[10]; //構造体配列の宣言
    
    //構造体配列の宣言(構造体テンプレートと、構造体配列を同時に書く場合)
    struct data2{
    	int no;
    	char name[10];
    	int age;
    } list1[10];  //構造体配列の宣言(同時に)
    
    //構造体配列を初期化する場合(各々の配列内に値をセットしている)
    struct data2 list1[10] = {
    	{1, "testA", 10},
    	{2, "testB", 20},
    	{3, "testC", 32},
    	{4, "testD", 41}
    };
    
    //↑の例では、list1[10]と定義しているので、10個分の構造体配列を持たせることができる
    
    
    //構造体配列の中身を参照する記述
    //方法1
    int i;
    for (i = 0; i <10; i++ ) {
    	printf("%d %s %d \n", list1[i].no, list1[i].name, list1[i].age);
    }
    
    printf("-----------\n");
    
    //方法2
    int j;
    struct data2 *sp = list1;
    for (j = 0; j < 10; j++ ) {
    	printf("%d %s %d \n", list1[*(sp+j)].no, list1[*(sp+j)].name, list1[*(sp+j)].age);
    }

    printf("-----------\n");

    //方法3
    struct data2 *sp;
    for (sp = list1; sp != list1 + 10; sp++ ) {
    	printf("%d %s %d \n", sp->no, sp->name, sp->age);
    }

    return 0;
}

上記を実行した結果、方法2の結果がなぜか下記のようになる。

1 testA 10
2 testB 20
3 testC 32
4 testD 41
0  0
0  0
0  0
0  0
0  0
0  0
-----------
2 testB 20      //出力の開始がおかしい(2つめからになっている)
3 testC 32
4 testD 41
0  0            //←ここの値がおかしい
1 testA 10
1 testA 10
1 testA 10
1 testA 10
1 testA 10
1 testA 10
-----------
1 testA 10
2 testB 20
3 testC 32
4 testD 41
0  0
0  0
0  0
0  0
0  0
0  0

なぜなのか、、調査中

構造体の使用例

Posted コメントするカテゴリー: C

構造体の使用例について、実際に書いてみます。

#include <stdio.h>

int main(void)
{
	
	//構造体のテンプレート宣言
	struct data{
		int no;
		char name[10];
		int age;
	};
	
	//初期化(「1」や「test」というひとつひとつの要素はメンバ)
	struct data list1 = {1, "test !!!", 39};
	
	//メンバが多い場合は折り返して書く
	struct data list1 = {
		1,
		"test !!!",
		39
	};
	
	//構造体メンバへのアクセス
	printf("%d %s %d\n", list1.no, list1.name, list1.age);
	
	//構造体変数へ値を代入する
	list1.no = 3;
	strcpy(list1.name, "cotytext!");
	list1.age = 40;
	
	
	
	return 0;
}

構造体に対するポインタもあります。この場合は、構造体をポインタで指し示すことになります。

#include <stdio.h>
 
int main(void)
{
     
    //構造体のテンプレート宣言
    struct data{
        int no;
        char name[10];
        int age;
    } list1;
     
    //構造体のポインタを宣言
    struct data *sp;
     
    //ポインタへのアドレスの代入方法
    //struct data list1;
    sp = &list1;
     
    //ポインタを使った構造体の参照方法(phpのクラスのメンバ変数へのアクセスに似ている)
    printf("%d %c %d\n", sp->no, sp->name, sp->age);
     
     
    return 0;
}

構造体配列について

構造体

Posted コメントするカテゴリー: C

構造体について

構造体とは、異なる型の変数をひとまとめにして、取り扱える形にしたもの。
どのような型の変数をまとめるのかを指定することを、構造体のテンプレートと呼びます。
テンプレートを定義しただけではデータを入れることができないので、構造体の型を持つ変数を用意します。この変数のことを構造体変数と呼びます。

構造体を配列に格納すると、それは構造体配列と呼ばれるものになります。これはまだ良くわかっていないので後ほど勉強します。

構造体は異なる型の変数をひとつにまとめ、まとめられた要素ひとつひとつをメンバと呼びます。

実際に書いてみます。

#include <stdio.h>

int main(void)
{
	
	//構造体のテンプレート宣言
	struct data{
		int no;
		char name[10];
		int age;
	};
	
	//構造体変数の宣言(dataはテンプレート名、list1は構造体変数名)
	struct data list1;
	
	//構造体テンプレートと構造体変数を同時に宣言
	struct data2{
		int no;
		char name[10];
		int age;
	} list2;
	
	struct data2 list3; //宣言をした後から構造体変数を追加した場合
	
	return 0;
}




キーボード入力

Posted コメントするカテゴリー: C

キーボードからの入力を取り扱う方法を勉強します。

scanf()関数
キーボードから入力したデータを指定の書式に変換したり配列に格納したりします。

int a;
scanf("%d", &a); //&をつけてアドレスとしている

//文字列の場合
char s[30];
//配列の場合は、先頭要素のポインタとなるので、&はいらない
scanf("%s", s);

//複数のデータを一度に入力する場合
int a;
char s[30];
scanf("%d %s", &a, s);

gets()関数
キーボードから入力した1行分の文字列を文字列配列に格納する

char s[30];
gets(s); //1行分

getchar()関数
キーボードから入力された1文字だけ変数に格納する

//実行するとプログラムはキーボードからの入力待ちになる
int c;
c = getchar();

一般的な入出力

Posted コメントするカテゴリー: C

C言語はファイルポインタを通してディスク上のファイルを読み書きを行います。
また、ファイルポインタは必ずしもファイルを取り扱うのではなく、キーボードやディスプレイなどの入出力装置とのやりとりについても使用します。

■標準入出力ファイルの種類
基本的な入出力のため、stdin,stdout,stderrの3つのファイルポインタがあります。
これらはプログラム実行開始時に自動的に開いています。

stdinは標準入力と呼ばれ、キーボードなどからの入力を受け取る
stdoutはディスプレイなどへの出力を行います
stderrは基本的なエラー出力を(ディスプレイ等に)行います

ファイル用関数で標準入出力を指定するとキーボードやディスプレイからの入出力になります。
printf関数でディスプレイに出力する場合は、裏側でfprintf(stdout, “%d”, a);という命令が動き、ディスプレイに結果が出ています。

サンプル用プログラム

#include <stdio.h>

int main(void)
{
	//入力された文字列を格納する変数を宣言しておく
	char s[30];
	
	//キーボードからの入力を変数sへ格納する
	fgets(s, 29, stdin);
	
	//変数sの値をそのままディスプレイ出力する
	fputs(s, stdout);
	
	//オマケ
	fputs("error\n", stdout);
	
	return 0;
}

バイナリファイルの書き込み

Posted コメントするカテゴリー: C

バイナリファイルを書き込む場合は、ファイルのオープンモードを「wb」にします。

#include <stdio.h>

int main(void)
{

	short buf[] = {
		0x10, 0x20, 0x30
	};

	FILE *fp;
	fp = fopen("d.data", "wb");

	fwrite(buf, sizeof(short), 3, fp);

	//ファイルを閉じる
	fclose(fp);

	return 0;
}

バイナリファイルの読み込み

Posted コメントするカテゴリー: C

バイナリファイルを読み込む場合は、改行等の制御文字はなく、ひとつながりのデータとして読み込みます。

バイナリファイルを開くときはファイルモードに「b」(バイナリ)を追加します。

FILE *fp;
fp = fopen("file3.data", "rb");

オープンモードは以下のようになる

rb 読み込み専用
wb 書き込み専用
ab 追加書き込み用

バイナリファイルの読み込みは次のようになります。

#include <stdio.h>

int main(void)
{

	//ファイルを開く
	short buf[3];
	FILE *fp;
	fp = fopen("c.data", "rb");

	//データを読み込む
	//「3」は読み込み回数。fpが示す位置から2バイトのデータを3回読み込む
	fread(buf, sizeof(short), 3, fp);

	//ファイルを閉じる
	fclose(fp);

	return 0;
}

ファイルの書き込み

Posted コメントするカテゴリー: C

ファイルへの書き込みは以下のように書きます。

#include <stdio.h>

int main(void)
{
	
	FILE *fp;
	fp = fopen("b.txt", "w");
	fputs("Hello \n", fp);
	
	//指定形式での書き出し
	int a = 5;
	fprintf(fp, "%02d\n", a);
	fclose(fp);
	
	return 0;
}

別パターンを試してみる。

#include <stdio.h>
int main(void)
{
	
	FILE *fp;
	fp = fopen("b.txt", "w");
	fputs("Hello \n", fp);
	fputs("Hello1 \n", fp);
	fputs("Hello2 \n", fp);
	
	//指定形式での書き出し
	int a = 5;
	fprintf(fp, "%02d\n", a);
	fclose(fp);
	
	return 0;
}

上記の場合は

Hello 
Hello1 
Hello2 
05

という内容のb.txtファイルが新規作成されます。
fputsを繰り返し記述するのは良いのか悪いのか考えてみますが、動作上は特に問題なくファイルへの書き込みは完了しました。
書き込む内容を一旦、変数などにまとめて、最終的に一回のfputsで書き込んだほうがプログラムとしてはすっきり明確になります。

ファイルの読み込み

Posted コメントするカテゴリー: C

ファイルを読み込むプログラムを作成します。
ファイルの読み込みの判定を行わないと、a.txtがない場合に無限にループしてしまいます。

また、fgets関数は読み込んだファイル内容について、自動的に改行コードまでをひとつの文字列として認識します。

#include <stdio.h>

int main(void)
{
	char s[10];
	FILE *fp;
	fp = fopen("a.txt", "r");
	
	//ファイルオープンの判定
	if (fp == NULL) {
		printf("file null \n");
		return;
	}
	
	//ファイルを最後まで読み込む
	while(1) {
		//「10」は読み込み最大文字数
		fgets(s, 10, fp);
		printf("%s \n", s);
		if (feof(fp)) {
			printf("break!! \n");
			break;
		}
	}

	//ファイルを閉じる
	fclose(fp);

	return 0;
}

ファイル

Posted コメントするカテゴリー: C

ファイルの種類は大きく、テキストファイルとバイナリファイルがある。

テキストファイルは、主に文字データを取り扱う。
バイナリファイルは、主に音声や画像データを取り扱う。

ファイル処理の基本は、ファイルポインタを宣言して、ファイルの読み書きの場所を決定してから処理を行う。

FILE *fp; //ポインタとして宣言する

ファイルを扱うときは次の順序で行う。
①ファイルを開く
②読み書きを行う
③ファイルを閉じる

ファイルを開く場合は次のように書く。

FILE *fp;
fp = fopen("a.txt", "r");

三つめの引数はオープンモードで、C言語では次のようになります。

モード動作ファイルがあるときファイルがないとき
"r"読み出し専用正常エラー(NULL返却)
"w"書き込み専用サイズを 0 にする(上書き)新規作成
"a"追加書き込み専用最後に追加する新規作成
"r+"読み込みと書き込み正常エラー(NULL返却)
"w+"書き込みと読み込みサイズを 0 にする(上書き)新規作成
"a+"読み込みと追加書き込み最後に追加する新規作成

ファイルのオープンに成功すると、ファイルポインタを返す。
どのモードで開くかにより、前のファイルの扱いや、ファイルポインタが最初に示す位置は異なる。
オープンに失敗すると、fopen関数はNULLを返すので、NULLかどうかを判定して次の処理を決定すると良い。

ファイルを閉じるにはfclose()関数を使う。

fclose(fp);

ファイルの入出力

Posted コメントするカテゴリー: C

ファイルにはテキストファイルとバイナリファイルという種類がある。
テキストファイルは人間が読めるもので、バイナリファイルは(一般的には)解読ができない。

C言語はファイル名を直接読み込むわけではなくファイルポインタと呼ばれるものでファイルを置き換えてアクセスする。

ファイルポインタはファイルのどの部分を読み書きするか。という情報も含んでいる。

■ファイルポインタを使ったファイルの読み書きの順番

以下のような順番でファイルを操作する

①ファイルポインタの宣言
②対象のファイルを開く
③ファイルポインタを得る
④ファイルポインタを通して読み書きする
⑤操作が終わり、ファイルを閉じる

テキストファイルとバイナリファイルでは、ファイル操作時の関数の種類や引数が違うので、注意が必要。

C言語では、キーボードから入力されたデータを扱うことがある。この時に入力される値を読み込む部分を「入力部分」と呼ぶ。
キーボードから入力される値と、ファイルから読み込む値も同様に考えられる。
キーボード入力やファイルから読み込むために使うファイルを標準入出力ファイルという。
プログラムの実行開始と同時に開かれていて、いつでも使うことができる。

main関数

Posted コメントするカテゴリー: C

main関数はプログラム開始点(エントリポイント)となる特殊な関数。

以下にmain関数の書き方の例を書く。

//引数と返り値を省略
main ()
{

}

//引数を省略、返り値はvoid
void main()
{

}

//引数を省略、返り値はint
int main()
{
	return 0;
}

//引数と戻り値(int)を指定(これが基本パターンになる)
int main(int argc, char *argv[])
{
	return 0;
}

コマンドライン引数の取得
コマンドラインから引数をつけてプログラムを実行する。
その際、main関数の引数にはプログラム自身のファイル名とコマンドライン引数の情報が入る

引数         格納する情報
argc         配列argvの大きさ(コマンドライン引数の数+1)
argv[0]      プログラムファイルのパスの文字列へのポインタ
argv[1]      1番目のコマンドライン引数の文字列へのポインタ
argv[2]      2番目のコマンドライン引数の文字列へのポインタ

使用例は次のような形になる

aaaa.exe val1 val2 val3

引数argv[0]には「aaaa.exe」が入る。
argv[1]には「val1\n」、argv[2]には「val2\n」、argv[3]には「val3\n」、がそれぞれ入る

引数の受け渡し(値渡しと参照渡し)

Posted コメントするカテゴリー: C

関数を使う場合、呼び出し側と定義側の両方で引数を指定する。
呼び出し側を「実引数」といい、定義側を「仮引数」と呼ぶ。

■値渡しと参照渡し
引数を書く場合に、実引数と仮引数をまったく別もの(違うアドレス)として扱う場合は値渡しを行う。

値渡しの例

#include <stdio.h>

void addnum(int, int); //プロトタイプ宣言

void main()
{
	int a = 2, b = 3;
	int n;
	n = addnum(a, b); //関数の呼び出し(値渡し)
	printf("%d\n", n); //結果「2」となる
}

void addnum(int x, int y)
{
	int temp;
	temp = x;
	x = y;
	y = temp;
}

対して、実引数と仮引数を同じもの(同一アドレス)として扱う場合は参照渡しを行う。

参照渡しの例

#include <stdio.h>

void addnum(int *, int *); //プロトタイプ宣言

void main()
{
	int a = 2, b = 3;
	int n;
	addnum(&a, &b); //関数の呼び出し
	printf("%d\n", n);
}

void addnum(int *x, int *y)
{
	int temp;
	temp = *x;
	*x = *y;
	*y = temp; //全て参照として処理している
	
	//結果「1970678749」になる
}

プロトタイプ

Posted コメントするカテゴリー: C

■関数のプロトタイプを宣言する

これまでは「関数の定義」→「関数の呼び出し」の順番でコーディングしていた。
これを逆に行うとコンパイルエラーになる。

コンパイルエラーにならないように関数を使いたい場合は、プロトタイプという関数のひな型を呼び出し前に宣言しておく。

プロトタイプ宣言は関数の仕様にあたる部分だけを抜き出したもの。

#include <stdio.h>

int addnum(int, int); //プロトタイプ宣言

void main()
{
	int n;
	n = addnum(2, 3); //関数の呼び出し
	printf("%d\n", n);
}

int addnum(int a, int b)
{
	int x;
	x = a + b;
	return x;
}

ブログ内の「関数の定義」の例では、関数を書く順番でエラーになった書き方が、プロトタイプを宣言することによってエラーではなくなった。

変数のスコープ

Posted コメントするカテゴリー: C

関数の中で宣言した変数のことをローカル変数という。
ローカル変数を参照できる範囲は、定義した関数の中に限られる。

このような変数の有効範囲のことをスコープという。

#include <stdio.h>

void functionA(void)
{
	int y;
	return y;
}

void main()
{
	int x;
	x = 3;
	y = 4; //この時点で、変数が参照できずにエラーになる
	
}

プログラム全体で参照できる変数のことをグローバル変数といい、
どの関数の中からも参照ができる。

#include <stdio.h>

int z;

void functionA(void)
{
	int y;
	z = 2; //代入はするが、このプログラム内では関数が呼ばれないので、値の変化はなし
	return y;
}

void main()
{
	int x;
	x = 3;
	z = 5; //グローバル変数への代入
	
	//y = 4; //この時点で、変数が参照できずにエラーになる
	
}

※上記プログラムは、最終的に結果を出力しないので、なにもおきない。

関数の呼び出し

Posted コメントするカテゴリー: C

定義した関数を呼び出すには、次のように書く。

#include <stdio.h>

void dispnum(int a)
{
	printf("%d\n", a);
}

void main()
{
	dispnum(5);
	dispnum(10);
	dispnum(15);
}

戻り値を利用してなにかを行う。

#include <stdio.h>

int addnum(int a, int b)
{
	int x;
	x = a + b;
	return x;
}

void main()
{
	int n;
	n = addnum(5, 56);
	
	printf("%d\n", n); //結果「61」と表示される
}

関数の定義

Posted コメントするカテゴリー: C

関数は一連の処理をまとめたもの。
関数には処理の材料となる値のことを引数(パラメータ)をいい、処理の結果の値のことを戻り値(返り値)

次の例は2つの引数を加算し、結果を返す関数の定義

int addnum(int a, int b)
{
	int x;
	x = a + b;
	return x;
}

結果を返さない関数は次のように定義する
関数の先頭で「void」と定義することにより、結果を返さないことを示す

void dispnum(int a)
{
	printf("引数は%d\n", a);
	return;
}

引数を必要としない関数を定義する

void novalue(void)
{
	printf("not value !!\n");
}

C言語に標準で搭載されている関数は標準ライブラリ関数という
printf()やstrcpy()のことをさし、これらは定義しなくてもすぐに使うことができる

実際に動作させてみた時、注意する点は「void dispnum」と「void novalue」については「int main」の前に記述しておかないと未定義の関数とみなされ、エラーになる。

#include <stdio.h>

void dispnum(int a)
{
    printf("引数は%d\n", a);
    return;
}

void novalue(void)
{
    printf("not value !!\n");
}

int main(void)
{
	int ans;
	ans = addnum(50, 3);
	printf("%d\n", ans);
	
	dispnum(ans);
	
	novalue();
	
	return 0;
}

int addnum(int a, int b)
{
    int x;
    x = a + b;
    return x;
}

メモリの確保および操作関数

Posted コメントするカテゴリー: C

動的なメモリ確保

変数や配列を宣言すると自動的にメモリ上に領域が確保される。
プログラムが多くのデータを扱う場合、メモリを一度に用意しきれない場合があるので、無理に大きすぎるメモリ領域を一気に確保しようとするとエラーになってしまう。

この場合は動的にメモリ領域を確保する方法を使う。

動的にメモリ領域を確保するには、下記のC言語の標準ライブラリの呼び出しをする

#include <memory.h>
#include <malloc.h>
#include <stdlib.h>
//確保したメモリの先頭アドレスを入れるポインタを宣言
short *buf;

//「(short *)」の部分でmallocの戻り値に対して、型のキャストをする
buf = (short *)malloc(sizeof(short) * 2000);

(メモ)
「malloc」関数は、ヒープメモリからsizeバイトのブロックを割り当てる。
この関数を使うことにより、必要な場所で必要なだけメモリを確保することができる。

 

  • ■メモリの利用

malloc関数で確保したメモリは使用後に必ずfree関数で解放する。

//確保した後、通常の配列と同じように使える
buf[2] = 40;
  • ■メモリの解放

使い終わったら、メモリを必ず解放する

free(buf);
  • ■メモリ確保関係の関数

malloc関数の代わりにcalloc関数を使う書き方もできる。

buf = (char *)calloc(sizeof(char) * 20);

(メモ)
「calloc」関数は、メモリを確保し、要素をすべて0に初期化する

realloc関数を使う書き方もできる。

buf = (char *)realloc(buf, sizeof(char)*15);

(メモ)
「realloc」関数は、一度確保したメモリを違うサイズで確保し直す

  • ■メモリ操作関数

メモリを操作する関数はmemset、memcpyなどがある(←ごく一部の例)

//メモリの内容を全て同じ値に設定する
memset(buf, 0, 5);
//↑の例だと、配列の要素5個に対して、全て「0」を代入する動きをする

memcpy(dst, src, 5);
//↑の例、srcというコピー元のメモリ領域の先頭アドレスから5バイト分、dstのアドレスへ代入する
  • 例(書籍そのままを動かしてみる)
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>

int main(void)
{
    char *b;
    char a[4] = {20, 40, 30, 100};
    b = (char *)malloc(sizeof(char) * 200);
    
    if (!b) {
        return;
    }
    
    memcpy(b, a, sizeof(char) * 4);
    printf("%d %d %d %d \n", b[0], b[1], b[2], b[3]);
    free(b);

    //結果「20 40 30 100」と表示される
}

ポインタと配列

Posted コメントするカテゴリー: C

配列の名前そのものは、配列の最初の要素を指し示すポインタの役割をする。

//aはa[0]へのポインタを表す
int a[4];

配列の最初要素以降を呼び出すには、ポインタを加算していく。
ポインタには整数の加算と減算のみ可能。

int *p = a+2; //前から(配列の)2個目の値を指し示す
int *q = p-1; //後ろから(配列の)1個目の値を指し示す

配列の型によって、ポインタの進み方は異なる。

long a[4];
long *p = a+1; //4バイトづつ進む

char c[4];
char *q = c+1; //1バイトづつ進む

配列aがある場合、a自身はa[0]へのポインタなので*aは格納場所ある値=a[0]となる。
同時にa[1] =* (a+1)、a[2] =* (a+2)、と書くことも可能

NULLポインタ

Posted コメントするカテゴリー: C

ポインタを利用するときは、必ずその値が指し示すアドレスに値がある必要がある。

int a;
int *p;
a = *p; //ポインタがなにもないアドレスを指し示している為エラー

プログラム内で、どこも指し示していないことを明示する場合に使う。
NULLポインタはどの型のポインタにも格納できる。

int *p = NULL;

ポインタpが有効かどうかを調べるには、論理演算を使って調べることができる。

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

int main(void)
{
	
	char s[] = "test aaa";
	char c = 'd'; //ダブルクォートで囲むとエラーになる
	char *p = NULL;
	
	printf("文字列 %s の中に文字 %c ", s, c);
	
	p = strchr(s, c);
	
	if (!p) {
		printf("なし\n");
	} else {
		printf("あり\n");
	}
	
	//出力結果は「なし」になる。
	
}

strchrの関数は以下の動きをする
strの中からchrを検索して最初に発見した文字以降のアドレスを返す。文字を発見できなかった場合はNULL(\0)を返す。
尚、一般的には検索する文字はint型にcastする。

ポインタ

Posted コメントするカテゴリー: C

変数などが格納されている位置を値とする変数をポインタという。
ポインタにも型がある。

(例)char型のポインタ変数pを宣言するには次のように書く

char *p;

char* p;

//↑半角スペースの位置が違うけれど、どちらも間違いではない

ポインタへのアドレスの代入

char a;
char *p;
p = &a;

ポインタが指す値の参照

char a = 3;
char *p;
p = &a;

//値を参照してbに代入しているので「3」が代入される
char b = *p;

参考プログラム

#include <stdio.h>

int main(void)
{
	char x = 4;
	char y;
	char *p = &x;
	y = *p;
	printf("%d \n", y); //結果「4」が表示される

}