ホームページへ戻る
 

上へ戻る
 

        C言語入門  その1     BY H.Y
2月 27 16:00:31 JST 2002

1。C言語の紹介
1)サンプルコード count.c
2)コメント
(a) // この行だけ
(b) /* ---------
閉じるまでがコメントの範囲 */
3)定数の表現
(1)8進数 : 0376 //頭に 0 を付ける
(2)10進数 : 254
: 123.456
: 1.23456e2 // 1.23456 x 100
(3)16進数 : 0xfe, 0xFE //頭に 0x, 0X を付ける
(4)文字 : 'A', 'b' //シングルクォートで囲む
(5)制御文字
\n : 改行
\r : キャリッジリターン
\t : タブ
\f : 改ページ
\v : 水平タブ
(6)文字列(リテラル): ダブルクォートで囲む
"This is string."
"This is octal number \376" //0376
重要:文字列の最後は必ず0でターミネートされる。
4)コンパイルの流れ
(1)前処理 プリプロセッサ が実行する
#include や #define 等の処理を実行する。
例: gcc -E -o argv.i argv.c
argv.i が前処理済みのソースファイル
(2)コンパイル コンパイラが実行する。
プログラムをアッセンブラコードへ展開、最適化を実行する。
例1: gcc -x cpp-output -S argv.i -o argv.S
-x: 言語の指定
-S: アッセンブルを実行しない。
例2: gcc -S -o argv.S argv.c
argv.S がアッセンブラコードのファイル
(3)アッセンブル アッセンブラが実行する。
アッセンブラコードを機械語に翻訳してオブジェクファイルを作成。
例1: gcc -x assembler -c argv.o argv.S
//argv.S をアッセンブルする
例2: gcc -c -o argv.o argv.c
argv.o がオブジェクトファイル
(4)リンク リンカーが実行する。
以下のファイルを結合して実行ファイルを作成する。
1.オブジェクトファイル
2.ランタイムライブラリ(スタティックリンクライブラリ)
3.スタートアップファイル
例1: gcc -o argv argv.o //argv.o をリンクする
例2: gcc -o argv argv.c
argv が実行ファイル
通常はこの方法で前処理からリンクまでを実行する。

2。C言語の特徴
1)大文字小文字の区別
2)C言語は関数の集合
sub1(){ //サブルーチン
}
sub2(){
}
main(){ //main()はプログラムが始まる最初の関数。
}
3)関数呼出しの引数は値で渡す func1.c
func(int i, int *j)
i が値を渡していいる。
*j はあとで説明するが、ポインター渡し
参考: java は参照渡し
4)再帰読みだし fact.c
fact() の内部で自分自身をコールしている。
./fact 整数値 で実行する。整数値は16以下が有効

結果:
(1) 10*fact(10-1)
(2) 10*9*fact(9-1)
--------------
(10) 10*9*8*7*6*5*4*3*2*1
課題:なぜ整数値は16以下が有効か?
ヒント i は4バイトの符号付きの整数
5)演算子
(1)算術演算子
+ - / * %
k = i % j; k は i/j の剰余
(2)比較演算子
> < >= <= ==
!= // 等しくないを意味する
if( a == b) // if( a = b ) は常に成立する。
(3)論理演算子
&& : AND 論理積
|| : OR 論理和
! : NOT 論理否定
^ : EOR 排他的論理和

if( a == b && c != d)

a = 0xAAAA;
b = a ^ 0x0; // b = a
b = a ^ 0xFFFF // b = 0x5555 = !a
(4)ビット演算子
& : AND
| : OR
>> : shift right
<< : shift left
~ : NOT ビット反転
例1
a = 0xAAAA;
b = 0xFFFF;
a = a & b; または a &= b; // a = 0xAAAA
例2
a = 0xAAAA;
b = 0xFFFF;
a = a | b; または a |= b; // a = 0xFFFF
例3
a = 0x8000;
a = a > 8; または a >= 8; // a = 0x0080
例4
a = 0x0001;
a = a < 15; または a <= 15; // a = 0x8000
例5
a = 0xAAAA;
a ~= a; または a = ~a; // a = 0x5555;
(5)代入演算子
= += - -= *= /= %=
&= ^=

i = 10;
i += 20; //i = 30
(4)を参照のこと
(6)3項演算子
条件 ? 式1 : 式2
c = (a > b) ? a : b
if(a > b)
c = a;
else
c = b;

c = (a < b) ? a : (a > b) ? b : 0 ;
if(a < b)
c = a;
else if( a > b)
c = b;
else
c = 0;

(7)インクリメント/デクリメント
++ -- // dec.c dec2.c 参照
for(i=0; i<10; i++)

for(j=100; j>0; --j)
(8)ポインターを扱える
ポインターとは、
型をもったアドレスのこと。
そしてポインターの値は演算できる。

point2.c 参照
//----- 例1 アドレスの型---------------------
int i , j;
int *pi; //int型のアドレスがpiに代入される
pi = &i;
j = *pi; // j = i;
int型のアドレスとは、次の値が +4 になることを意味する。

//------ 例2 ポインタの演算------------------
int i;
int a[10];
char a[10], b[10]={Test text};
int *pa; //int型のポインター変数
char *pb; //char型のポインター変数
i = 0;
for(pa=a,pb=b; i<10; i++)
*pa++ = *pb++; // b[9] = 0

配列 a[] の最初のデータは a[0]
pa = a; とするとa[0]のアドレスが pa に代入される。
pa++; pa には a[1] のアドレスが代入される。
int型なので物理アドレスは +4 される。
pb = b; とするとb[0]のアドレスが pb に代入される。
ba++; pa には b[1] のアドレスが代入される。
char型なので物理アドレスは +1 される。
//---- 例3 ポインターの示す内容 --------------
int *pi, idata; //ポインターの宣言
*pi = 1234; //ポインターの参照
この意味は p が示すアドレスに データー値 1234 を
代入することを意味する。
idata = *pi; //idata = 1234
//----- 例4 ポインターの参照と演算 ------------
int *pi;
int data1 = 1234;
int data2 = 5678;

*pi++ = data1; //pi の最初のアドレスに data1 を代入
//その後アドレスが +4 加算される
*p = data2; //加算されたアドレスに data2 を代入

(9)構造体を定義して参照できる。
struct1.c 参照

(10)プリプロセッサの機能
(1)#define マクロ定義
(a)単純マクロ定義
#define MAX_LEN 100
#define FOREVER for(;;)
(b)パラメータ付きマクロ定義
#define DEBUG_PRINT(s) printf("%s\n",s)
(2)#undef マクロ定義の取消
#undef MAX_LEN
(3)#include ファイルの包含
#include <stdio.h>
(4)#typedef 型の定義
#typedef uchar unsigned char
(5)#if, endif
#if MAN //if MAN==1
#if 0 //endif まで全てコメント
-----
#endif

(6)#ifdef, elif, else
#ifdef DEBUG
printf("data=%d",data);
#endif

#ifdef INTEL
cpu = "intel-386";
#elif MOTOROLA
cpu = "68320";
#else
cpu = "ATHRON"
#endif

(7)#ifndef
#ifnde DEBUG
#define DEBUG 1
#endif
3。文法
1)タイプ(型)
char : 1 バイト符号付きデータ
short : 2 バイト符号付きデータ
int : 4 バイト符号付きデータ
log : 4 バイト符号付きデータ
unsigned char : 1 バイト符号無しデータ
unsigned short : 2 バイト符号無しデータ
unsigned int : 4 バイト符号無しデータ
unsigned long : 4 バイト符号無しデータ
double : 8 バイト浮動小数点データ
long double :
float : 4 バイト浮動小数点データ
void : なんでもない型
関数の戻り値が void の場合は戻り値が無い
汎用ポインタとしても使う
2)型のキャスト...... 型を明示的に変換すること
char *cp;
int i;
cp = (char *)i; // i は int型 の変数であるが、
// cpは char型 のポインター変数
3)データの型と型変換
char c;
double f;
int i;
i = c + f; //c と f をdouble型に変換して演算後,intに変換する。
明示的にすると
i = (int)c + (int)f; // int型に変換して演算
i = (int)((double)c + f); //double型に変換して演算後int型へ
4)sizeof 演算子
sizeof 型
sizeof (型名)
--------- 例1 ----------
short s
int i, x;
x = sizeof(s); // x = 2
x = sizeof(s + i); // x = 4
---------- 例2 -----------
struct st{
int i;
char *cp;
} st;
int x;
x = sizeof(struct st); // x = 8
x = sizeof(int); // x = 4

5)ポインタ操作演算子 * と &
演算子 * はポインタの示す内容
演算子 & はデータのアドレス
------- 例 ------------
char *pc, c1, c2, buf[100];
pc = buf; // pc = &buf[0] と同じ
//ポインタにはアドレスを代入できる。
c1 = *pc; // c1 = buf[0] と同じ
c2 = *(pc + 3) // c2 = buf[3] と同じ
+3 は []内のindex の値が +3 されること。
アドレスの絶対値が +3 ではない。
---point3.c 参照---
6)スコープ(scope)
名前(識別子)の通用する適用範囲のことをスコープという。
(1)ファイルスコープ
名前の宣言されている場所からファイルの最後までの範囲で通用する。
(2)ブロックスコープ
ブロック内部でのみ通用する名前。
(3)関数プロトタイプスコープ
関数のプロトタイプ宣言内でのみ通用する名前。
(4)関数スコープ
名前付きラベルの通用する範囲
例:
int tmp; //file scope
int func(int er); //function prototype scope

int func(int e){
int i; //block scope

for(i = 0; i < 10; i++){
int j; //block scope
for(j = 0; j < 10; j++){
tmp = i * j + j;
if(tmp > 50)
goto End;
}
}
end: //function scope
}

4。制御文
1)if else
2)switch case
3)while
4)for
5)do while //必ず一度内部を実行する。
6)break
7)continue
8)goto
9)return
5。データの宣言と参照
1)記憶クラス
(1)auto (自動変数) 関数内部で宣言され、関数内部のみで有効
auto は省略出来るので、通常は付けない。
関数が終了すると自動変数はメモリ上から失われる。
自動変数は初期化されない。
通常自動変数はメモリ上のスタックを使用するので、余り大きな
サイズは使用しないようにする(例えば 10Kバイト)。
(2)static
静的な記憶クラスをもつ変数はプログラムの起動時に一度だけ初期化
される。またプログラムの動作中に値を失うことがない。

1.内部的に static 関数内で宣言
永久的に変数領域は存在する。
2.外部的に static 関数外で宣言
ファイル外で参照できない静的変数/関数。
例:
static int x; //x は外から参照できない。
int y; //z は外から参照できる。

void func(){ //func()は外から参照できる。
static int z; //z は静的な記憶クラス
int zz; //zz は自動的な記憶クラス
//z,zz 共に外から参照できない。
-----
}
static void func2(){ //func2() は外から参照できない。
------
}
静的変数の値の保存は static.c を参照のこと。

(3)extern 複数のソースにまたがる変数や関数の定義と参照
extern int i;
extern void func1();
(4)register レジスタ変数 引数と自動変数のみ定義可
処理の高速化のためにコンパイラに指示をあたえる。
メモリを使わずCPUのレジスタを使用する。
---------例1----------------------
func1(register x,register y){
register int i;
register char c;
................
}
register変数のアドレスはアクセスできない。
register int i;
int *cp;
cp = &i; は許されない。
(5)初期化
char ch[] = {"これは初期化です。"};
//文字列の最後な必ずヌルでターミネートされる。
char cch[] = {'a','b','c','\0'}; // '/0' はヌル
char c = '\';
int i = 0;
registr j = 100;
int ii[5] = (1,2,3); // (1,2,3,0,0) と同じ

自動変数の配列や構造体は初期化できない。
サンプル auto.c
(6)複雑な宣言
int id;
int *ip;
int *ii[100]; //int型のポインタの配列
int *func(); //int型のポインタを返す関数
int (*func1)(); //関数func1()へのポインタ
int (*func2)[100]; //関数へのポインタの配列
(7)複雑な型変換(キャスト)
(int)x //x を整数 へキャスト
(int *)x //x を整数への ポインタへキャスト
(int *[100])x //x を整数へのポインタの配列にキャスト

(int *( ))x //x を整数へのポインタを返す関数へキャスト
(int (*)( ))x //x を整数を関数値として返す関数へのポインタへ
キャスト
(8)const
const型のオブジェクトの値は変更不可。
const int pai = 3.1416;
int const pai = 3.1416; //どちらも同じ
これ以降 pai は変更できない。
(9)volatile
volatile型のオブジェクトは最適化の影響を受けない。
(10)メモリの直接読み込み mem-read.c mem-read2.c
Linuxの場合はカーネルで保護された領域を直接参照すると
「egmentation fault」で異常終了する。

char cdata; //共通
----------------------------------------------
char *cp = (char *)(0x123456);
cdata = *cp; //cdata=メモリアドレス0x123456の内容
-----------------------------------------------
#define M123456 (*(char *)0x123456)
cdata = M123456;
----------------------------------------------
char *p1 = (char *)0x123456;
char *p2= (char *)(0x123456);

cdata = *p1;
cdata = *p1;
-----------------------------------------------

6。複雑なデータ構造
1)ポインタと配列
(1)ポインタの場合 --- pointer2.c---
int a=1, b=2, c;
func(a, b, &c);
printf("%d + %d = %d /n", a, b, c);

func(int s1, int s2, int *s){
*s = s1 + s2;
}

s++ が出来る。
(2)配列のばあい ---array.c array2.c----
char c buf[100];
movstr(cbuf, "これは配列のテストです。");
printf("%s\n",cbuf);

movstr(char buf[],char *str){
int i = 0;

while(*str)
buf[i++] = *str++;
buf[i] = '\0';
}

buf++ は出来ない。//演算できるのはポインタのみ
(3)関数へのポインタ ---- func-p.c ,func-p2.c------
extern f1(),f2(),f3(),f4();
static int (*func2[])() =
{(*)f1(), (*)f2(), (*)f3(), (*)f4()};
(4)配列とポインタの違いの例
char *pa[] = {"abcde", "abc", "f", ""};
char aa[][6] = {"abcde", "abc", "f", ""};

*pa[] で確保されたメモリのサイズは 6+4+2+1=13バイト
sizeof(aa) = 24 // 6 * 4 = 24 バイト
(5)関数の引数としての配列はポインターである。
func( char ch[]) これは
func( char *ch) と同じこと

(6)void 型ポインタ
任意の型のオブジェクトをさすポインタ。
viod型のポインタはキャスト無しで他の型のポインタと相互に
代入できる。
但し void型のポインタの先の参照はできない。void型のポインタ
の先にはオブジェクトがないので。
例:
void *malloc(size_t size); //戻り値は void 型のポインタ。
int *ip;
char *cp;
ip = malloc(sizeof(int) * 100);
cp = malloc(sizeof(char) * 100);
(char *)malloc() の必要は無い。
(7)ポインタの値がNULLのときはその先に代入してはならない。
char *p=NULL;
*p = 'A'; //これはダメ


2)構造体 struct で定義する。 --- struct1.c ,struct2.c ------
struct st{ //stは構造体のタイプの定義
int a;
float f;
log l;
char c;
char ch[100];
}ast1; //タイプst の構造体の実態が ast1

struct st ast2;
struct st astt[10]; //st型の構造体配列 astt

sprcpy(ast.ch, "これは構造体の文字です。");
ast.a = 0;
astt[0].l = 123.456; //構造体のメンバ

memcpy(&ast1, &ast2);
printf("%s\n",ast2.ch);


memcpy(stuct st *s1, struct st *s2){
int j;

for(j=0; j<100; j++)
s2->ch[j] = s1->ch[j]; //ポインタのメンバのアクセス
}
これは
(*s2).ch[j] = (*s1).ch[j]; でもよい。
3)ビット演算子
----- bit.c -----------------
4)プリプロセッサ
(1)#include
#include <stdio.h>
#include </usr/local/kylix/includ/kylix.h>
#include "test.h"
(2)#define
#define uchar unsigned char //これは良くない
#typedef unsigned char uchar; を使うべきである。
#define BUFSIZE 200
#define max(x,y) x > y ? x : y
e = max(x,y);
e= ( x > y ? x : y );
--- macro.c----------
(3)#undef
#define SIZE 100
a = SIZE;
#define SIZE 80
b = SIZE;
#undef SIZE
c = SIZE;
これは
a = 100;
b = 80;
c = 100; と同じになる。
(4)if else endif
#define SMP 1
#ifdef SMP
CPU = 2;
#else
CPU = 1;
#endif
(5)ifndef else endif
7。標準関数
1)入力
int getchar(void)
画面にエコーしながら、標準入力から1文字を取得する。
バッファリングしながら読み込むので改行を入力するまで戻らない。
------ echo.c --------------------
#include <stdio.h>

int main(){
int c,cc;
int count=0;

do{
c = getchar();
count++;
printf("%d %x\n",count, c);
}while(c != '\n');
printf("total count=%d char=%x\n",count,c);

return 0;
}
-------- sub getch() 3term.c で使用 -----
char getch(){
int a, b;

do{
a = getchar();
if( a != '\n')
b = getchar();
}while(b != '\n');

return a;
}
--------------------------------------

2)getc(FILE *stream) fgetc()と同じ
ストリームから1文字取得する。
----------- echo2.c --------------
#include <stdio.h>

int main(){
char c;

c = getc(stdin);
printf("%c\n",c);

return 0;
}
------------------------------------
3)char *gets() これは推奨されない。
4)char *fgets(char *s, int n, FILE *fp)

5)int putchar(int c) putc(int c,stdout) と同じ
6)putc(int c,FILE *stream) 1文字書きだし
7)puts(char *s) 1行書き出し
8)fputs(char *s, FILE *fp)
9)int ungetc(int c)
10)int scanf(char *format, arg1,arg2,.....)
scanf("%d%o%x",&d,&o,&x);
d:10進数 %5d
o:8進数
x:16進数
e:整数
f:浮動小数点 %10.5f 小数点以下 5 桁
c:文字
s:文字列
[a-Z]:最初の文字から英字の文字列をセット
[^A-D]:最初も文字からA-Dでない文字列をセット
textt="aacbde123654"
scanf("%[abc]%[^abc]",&s1, &s2)
s1 = "aacb\n"
s2 = "123654" になる。
11)int fscans(FILE *fp,char *format, arg1,arg2,......)
12)printf(char *format,arg1,arg2,......)
char ch[] = {"This is the test file of printf."};
int i;

i = sizeof(ch);
printf("%s size = %d\n", ch, i);

練習プログラムを作成する。
------------- ファイル操作関数 file1.c -------------
13)fprintf(FILE *fp, char *format, arg1,arg2,......)
14) FILE *fopen(char *fname,char *mode)
mode
"r" :読みだし用にオープン
"r+" :既存のファイルを更新ようにオープン
"w" :書き込み用にオープン(上書きする)
"w+" :新しいファイルを書き込み用にオープン
"a" :ファイルを追加する
"a+" :追加のためにオープン
"t" :ファイルがテキストモード
"b" :ファイルがバイナリモード
15) int fclose(FILE *fp)



16)int fread(char *p, int size, int count, FILE *fp)
17)int fwrite(char *p, int size, int count, FILE *fp)
18)int fseek(FILE *fp,long offset, int prtname)
ptrname 0 :ファイルの先頭から
1 :現在位置から
2 :ファイルの終りから offset =< 0
19)int ftell(FILE *fp)
---------------- 編集変換の関数--------------------
20)sprintf(char *s, char *format, arg1,arg2,.....)
21)sscanf(char *s, char *format,arg1,arg2,......)
22)atof(char *s)
23)atoi(cha *s)
24)char *strcat(char *s1, char *s2) // cat s2 >> s1
25)char *strncat(char *s1, char *s2, int n)
26)int strcmp(char *s1, char *s2) //s1-s2 大きさの比較
26)int strncmp(char *s1, char *s2, int n)
27)char *strcpy(char *s1, char *s2) //s2 -> s2 へコピー
28)char *strncpy(char *s1, char *s2, int n)
29)int strlen(char *s)
30)char *strchr(char *s,char c) //文字 c の位置を返す
31)char *strpbrk(char *s, char *cset) //文字列 cset の位置を返す
------------- 文字操作の関数 -------------------------
32)int isalpha(int c) //c が英文字の時 0 をかえす
33)int isdigit(int c) //c が数字の時 0 をかえす
34)int isupper(int c) //c が大文字の時 0 をかえす
35)int islower(int c) //c が小文字の時 0 をかえす