0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C 入門②

Last updated at Posted at 2019-08-09

数値処理

乱数

# include <stdio.h>
# include <stdlib.h>
# include <time.h>
 
void main(){
  int a,b;
  // 乱数の初期化
  srand((unsigned) time(NULL));
  //    1から10までの乱数を発生させる
  a = rand() % 10 + 1;
  b = rand() % 10 + 1;
  //    計算結果を出力
  printf("%d + %d = %d¥n",a,b,a+b);
}

実行結果
5 + 3 = 8

srand() srand(unsigned seed); randで発生させる乱数の系列を変更。 srand((unsigned) time(NULL));

その他関数

abs() abs(int n); 与えられた整数の絶対値を求める。 int n = abs(-10);
fabs() fabs(double d); 与えられた実数の絶対値を求める。 double d = fabs(-3.1);
pow() pow(double x,double y); xのy乗を求める。 double d = pow(3.0,2.0);
sqrt() sqrt(double d); 与えらられた実数の平方根を求める。 double d = sqrt(25.0);

ビット演算子は良くわからないので飛ばす。
わからなくても現場では困ってない
変数にはアドレスが存在し、仮にaという変数があると
&aとすることに より、変数のアドレスを取得することができる。

# include <stdio.h>
 
void main(){
    int a = 100;        //  int型の変数
    double b = 123.4;   //  double型の変数
    float c = 123.4f;   //  float型の変数(数値の後ろにfつける)
    char d = 'a';       //  char型の変数
    printf("aの値は%d、大きさは%dbyte、アドレスは0x%x¥n",a,sizeof(int),&a);
    printf("bの値は%f、大きさは%dbyte、アドレスは0x%x¥n",b,sizeof(double),&b);
    printf("cの値は%f、大きさは%dbyte、アドレスは0x%x¥n",c,sizeof(float),&c);
    printf("dの値は%c、大きさは%dbyte、アドレスは0x%x¥n",d,sizeof(char),&d);
}

実行結果
aの値は100、大きさは4byte、アドレスは0x28fddc
bの値は123.400000、大きさは8byte、アドレスは0x28fdcc
cの	値は123.400002、大きさは4byte、アドレスは0x28fdc0
dの値はa、大きさは1byte、アドレスは0x28fdb7

sizeof(サイズオブ)演算子
sizeof(int) ← int型のサイズを取得
sizeof(a) ← 変数aのサイズを取得

ポインタ
C言語には、アドレスを入れることを前提とした変数が存在します。それをポインタ変数もしくは、単にポインタと言います。

整数型ポインタ変数pの定義(intの場合)
int *p;

普通の変数との違い

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

# include <stdio.h>
 
void show(int,int,int);
 
void main(){
    int a = 100;    //  整数型変数a
    int b = 200;    //  整数型変数b
    int *p = NULL;  //  整数型のポインタ変数p
    p = &a; //  pにaのアドレスを代入
    show(a,b,*p);
    *p = 300;   //  *pに値を代入
    show(a,b,*p);
    p = &b; //  pにbのアドレスを代入
    show(a,b,*p);
    *p = 400;   //  *pに値を代入
    show(a,b,*p);
}
 
void show(int n1,int n2,int n3){
    printf("a = %d b = %d *p = %d\n",n1,n2,n3);
}

実行結果
a = 100 b = 200 *p = 100
a = 300 b = 200 *p = 300
a = 300 b = 200 *p = 200
a = 300 b = 400 *p = 400

流れとしては

①	p = &a;	pに、aのアドレスを代入	*pはaと同じものになる	100	200	100
②	*p = 300;	*pに300を代入	*pはaに等しいので、aが変わる	300	200	300
③	p = &b;	pに、bのアドレスを代入	*pはbと同じものになる	300	200	200
④	*p = 400;	*pに400を代入	*pはbに等しいので、bが変わる	300	400	400

※ポインタに変数のアドレスを設定するときは、原則的に同じ型のポインタ変数でするように

ポインタには、他の変数になりきれるという非常に面白い特徴があります。
アドレスを帰ることでそこにある変数の値と同じものになれるという感じ

# include <stdio.h>
 
//  変数の値入れ替えを行う関数
void swap(int*,int*);
 
void main(){
    int a = 1,b = 2;
    printf("a = %d b = %d¥n",a,b);
    swap(&a,&b);
    printf("a = %d b = %d¥n",a,b);
}
 
//  値の入れ替え
void swap(int* num1,int* num2){
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}

実行結果
a = 1 b = 2
a = 2 b = 1

引数にポインタを与えることにより、アドレスを与えた変数の値を変更することが出来ます。

ポインタ利用時に気をつけること
ポインタ変数は、NULLで初期化する必要があります。
初期化したまま何らかの変数のアドレスを設定しなかった 場合、コンパイルエラーは出ませんが、実行時エラーが出てプログラムは止まってしまいます

配列とポインタ

# include <stdio.h>
 
# define SIZE    5
   
void main(){
    //  サイズSIZEの配列を用意する。
    int ar1[SIZE];
    char ar2[SIZE];
    int i;
    int* p1 = NULL;
    char* p2 = NULL;
    //  値を代入
    for(i = 0; i < SIZE; i++){
        ar1[i] = i;
        ar2[i] = 'A'+i;
    }
    //  ポインタにアドレスを代入
    p1 = &ar1[0];
    p2 = &ar2[0];
    //  値を出力
    for(i = 0; i < SIZE; i++){
        printf("ar1[%d]=%d *(p1+%d)=%d ",i,ar1[i],i,*(p1+i));
        printf("ar2[%d]=%c *(p2+%d)=%c¥n",i,ar2[i],i,*(p2+i));
    }
}

実行結果
ar[0]=0 *(p1+0)=0 ar[0]=A *(p2+0)=A
ar[1]=1 *(p1+1)=1 ar[1]=B *(p2+1)=B
ar[2]=2 *(p1+2)=2 ar[2]=C *(p2+2)=C
ar[3]=3 *(p1+3)=3 ar[3]=D *(p2+3)=D
ar[4]=3 *(p1+4)=3 ar[4]=E *(p2+4)=E

配列変数 配列変数のアドレス 該当するポインタ ポインタ変数の値
ar1[0] &ar1[0] p1 *p1
ar1[1] &ar1[1] p1+1 *(p1+1)
ar1[2] &ar1[2] p1+2 *(p1+2)
ar1[3] &ar1[3] p1+3 *(p1+3)
ar1[4] &ar1[4] p1+4 *(p1+4)

ポインタへのアドレスの代入
int a[5] = { 1, 2, 3, 4, 5 };
int *p = &a[2];

配列	a[0]	a[1]	a[2]	a[3]	a[4]
該当するポインタ	p-2	p-1	p	p+1	p+2
ポインタ変数の値	*(p-2)	*(p-1)	*p	*(p+1)	*(p+2)

たぶんポインタPを宣言したら*(p-1)など毎にアドレスを変えれる

# include <stdio.h>
 
void main(){
    //  サイズSIZEの配列を用意する。
    double d[3] = { 0.2 , 0.4 , 0.6 };
    double *p1 = NULL,*p2 = NULL;
    int i;
    p1 = d; //  p1にdのアドレスを入力
    p2 = d;
    for( i = 0 ; i < 3; i++){
        printf("%f %f %f¥n",*(d+i),p1[i],*p2);
        p2++;   //  p2のアドレスをインクリメント
    }
}

実行結果
0.200000 0.200000 0.200000
0.400000 0.400000 0.400000
0.600000 0.600000 0.600000

char st[10]= "ABC";
char *p1,*p2;

p1=st;

としたとき、
st[0]=A [1]=B [2]=C [3]=\0
が入っていて、

*p1の値は st[0]と同じ A
*(p1+1)は st[1]と同じ B
という風になる

つまり

*p1=st[0]=A
*(p1+1)=st[1]=B
*(p1+2)=st[2]=C

といった感じになる

文字列の結合とコピー

# include <stdio.h>
# include <string.h>
 
void main(){
   char s[10];
   int len;
   //   文字列のコピー
   strcpy(s,"ABC");
   printf("s=%s¥n",s);
   //   文字列の結合
   strcat(s,"DEF");
   printf("s=%s¥n",s);
   //   文字列の長さ
   len = strlen(s);
   printf("文字列の長さ:%d¥n",len);
}

実行結果
s=ABC
s=ABCDEF
文字列の長さ:6

strcpy() strcpy(char* s1,char* s2); 右辺の文字列を左辺の文字列変数にコピーすstrcpy(s,"Hello");
strcat() strcat(char* s1,char* s2); 右辺の文字列を左辺の文字列変数に追加する。 strcat(s,"World");
strlen() strlen(char* s); ()に文字列を与えると、戻り値として長さを得られる。 int l = strlen("Hello");

文字列の比較

# include <stdio.h>
# include <string.h>
 
void main(){
   char s1[256],s2[256];
   printf("s1=");
   scanf("%s",s1);
   printf("s2=");
   scanf("%s",s2);
   if(strcmp(s1,s2)==0){
       printf("s1とs2は等しい¥n");
   }else{
       printf("s1とs2は等しくない¥n");
   }
}

実行結果①(入力した値が同じ場合)
s1=ABC
s2=ABC
s1とs2は等しい

strcmp() strcpy(char* s1,char* s2); 文字列

s1,s2が等しいと0、
等しくなければそれ以外の値を返す。 if(strcmp(s,"Hello")==0)
文字列から数値への変換

# include <stdio.h>
# include <stdlib.h>
 
void main(){
   char s1[] = "1000";
   char s2[] = "12.345";
   int a;
   double b;
   a = atoi(s1);
   b = atof(s2);
   printf("a=%d b=%f¥n",a,b);
}
実行結果
a=1000 b=12.345

atoi() atoi(char* s); 与えら得れた文字列を整数(int)に変える。
int n = atoi("100");
atof() atof(char* s); 与えら得れた文字列を実数(double)に変える。
double d = atof("3.14");

数値から文字列の変換

# include <stdio.h>
# include <string.h>
 
void main(){
   char s1[256],s2[256];
   int a = 100,b = 200;
   sprintf(s1,"%d",a);
   sprintf(s2,"bの値は%dです。",b);
   puts(s1);
   puts(s2);
}

実行結果
100
bの値は200です。

sprintf()関数は、printf()と同じ要領で文字列を作成できる非

常に便利な関数

構造体

構造体変数の定義
struct student data;
# include <stdio.h>
# include <string.h>
 
//  学生のデータを入れる構造体
struct student{
    int id;         //  学生番号
    char name[256]; //  名前
    int age;        //  年齢
};
 
void main(){
    struct student data;
    data.id = 1;    //  番号を設定
    strcpy(data.name,"山田太郎");   //  名前を設定
    data.age = 18;  //  年齢を設定
    //  データの内訳を表示
    printf("学生番号:%d 名前:%s 年齢:%d

¥n",data.id,data.name,data.age);
}

実行結果
学生番号:1 名前:山田太郎 年齢18

構造体の成分の変数のことを、メンバと言います。

# include <stdio.h>
# include <string.h>
 
//  学生のデータを入れる構造体
struct student{
    int id; //  学生番号
    char name[256]; //  名前
    int age;        //  年齢
};
 
//  構造体の名前をtypedefで定義
typedef struct student student_data;
 
void main(){
    int i;
    student_data data[] = {
        { 1,"山田太郎",18 },
        { 2,"佐藤良子",19 },
        { 3,"太田隆",18 },
        { 4,"中田優子",18 }
    };
    //  データの内訳を表示
    for(i = 0; i < 4; i++){
        printf("学生番号:%d 名前:%s 年齢:%d¥n",data

[i].id,data[i].name,data[i].age);
    }
}

実行結果
学生番号:1 名前:山田太郎 年齢:18
学生番号:2 名前:佐藤良子 年齢:19
学生番号:3 名前:太田隆 年齢:18
学生番号:4 名前:中田優子 年齢:18


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

「typedef」句を使った宣言の方法も

typedef struct 構造体名 { 
    メンバ1; 
    メンバ2; 
    ・・・
}; 構造体名2;

「struct 構造体名」というデータ型を「構造体名2」というデータ型に型を付け替えるということ

// 構造体
struct person1 {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
};
 
// 構造体
typedef struct person {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
} person2;

「typedef」句を使った宣言の場合「person」句を省略することもできます。

// 構造体
typedef struct {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
} person2;

初期化の方法について

「struct 構造体名」型の場合は
struct 構造体名 実体名;
「typedef」句を使った「構造体名」型の場合
構造体名 実体名;

# include <stdio.h>
 
// 構造体
struct person1 {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
};
 
// 構造体
typedef struct person {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
} person2;
 
// 一括代入用の関数
person2 init(char *name, char sex, int age, char *add, char* job) {
    person2 p2;
    p2.name = name;
    p2.sex = sex;
    p2.age = age;
    p2.add = add;
    p2.job = job;
    return p2;
}
 
int main(void) {
    // 構造体の実体を生成
    struct person1 tanaka;
    tanaka.name = "T.Tanaka";
    tanaka.sex = 'm';
    tanaka.age = 30;
    tanaka.add = "Tokyo";
    tanaka.job = "teacher";
    printf("%sは%d歳で、%sで%sをしています\n", tanaka.name, tanaka.age, tanaka.add, tanaka.job);
 
    // 構造体の実体の生成と一括初期化
    person2 sato = {"S.Sato", 'f', 25, "Osaka", "nurse"};
    /* error: expected expression before '{' token
    sato = {"S.Sato", 'f', 25, "Osaka", "nurse"};
    */
    printf("%sは%d歳で、%sで%sをしています\n", sato.name, sato.age, sato.add, sato.job);
 
    // 関数で一括代入
    sato = init("K.Sato", 'm', 35, "Nagoya", "doctor");
    printf("%sは%d歳で、%sで%sをしています\n", sato.name, sato.age, sato.add, sato.job);
 
    return 0;
}
実行結果

T.Tanakaは30歳で、Tokyoでteacherをしています
S.Satoは25歳で、Osakaでnurseをしています
K.Satoは35歳で、Nagoyaでdoctorをしています

このサンプルコードでは構造体を「struct person1」型と「person2」型で宣言しています。「struct person1」型を実体化する際には「struct person1 tanaka」と定義「person2」型を実体化する際には「person2 sato」と定義実体「sato」のメンバの初期化は定義と一緒に全メンバ一括で行っています

実体のポインタからメンバを呼び出すには、アロー演算子を使用


構造体の実体のポインタ変数名->メンバ名
# include <stdio.h>
 
// 構造体
typedef struct person {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
} person2;
 
int main(void) {
    person2 sato, *p1;
    p1 = &sato; // 実体のアドレス
    p1->age = 20; // ポインタを使ってメンバの初期化
    printf("%d\n", sato.age);
 
    person2 kato, *p2;
    p2 = &kato; // 実体のアドレス
    *p2 = *p1; // アドレス先の値を共有
    printf("%d\n", kato.age);
 
    return 0;
}

実行結果

20
20

構造体を配列で扱う方法

# include <stdio.h>
 
// 構造体
typedef struct {
    char *name;
    char sex;
    int age;
    char *add;
    char *job;
} person;
 
// 一括代入用の関数
person init(char *name, char sex, int age, char *add, char* job) {
    person p;
    p.name = name;
    p.sex = sex;
    p.age = age;
    p.add = add;
    p.job = job;
    return p;
}
 
int main(void) {
    // 構造体の実体を生成
    person p[3];
    
    // 関数で一括代入
    p[0] = init("T.Tanaka", 'm', 30, "Tokyo", "teacher");
    p[1] = init("S.Sato", 'f', 25, "Osaka", "nurse");
    p[2] = init("K.Sato", 'm', 35, "Nagoya", "doctor");
    
    for(int i = 0; i < 3; i++) {
printf("%sは%d歳で、%sで%sをしています\n", p[i].name, p[i].age, p[i].add, p[i].job);
    }
    return 0;
}

実行結果


T.Tanakaは30歳で、Tokyoでteacherをしています
S.Satoは25歳で、Osakaでnurseをしています
K.Satoは35歳で、Nagoyaでdoctorをしています

ファイルには大きくわけて、テキストファイルとバイナリファイルが存在します。

テキストファイル 文字として読めるデータ。

.txtファイル、.cファイル、.htmlファイルほか
バイナリファイル 文字として読めないデータ。(画像や音声など).pngファイル、.wavファイルほか

# include <stdio.h>
# include <stdlib.h>
 
void main() {
    FILE *file;                                   // ファイルポインタ(出力用)
    file = fopen("c:¥¥test¥¥sample.txt", "w");      // ファイルを書き込み用にオープン(開く)
    if (file == NULL) {                             // オープンに失敗した場合
        printf("ファイルが開けません。¥n");            // エラーメッセージを出して
        exit(1);                                    // 異常終了
    }
    //  ファイルにデータを書き込む
    fprintf(file,"Hello World.¥r¥n");       // ファイルに書く
    fprintf(file,"ABCDEF¥r¥n");
    fclose(file);                       // ファイルをクローズ(閉じる)
}

sample.txt
Hello World.
ABCDEF

fopen() fopen(ファイル名,モード); ファイルを、指定

したモードで開く。
戻り値が、ファイルのポインタ。 FILE* fp = open

("sample.txt","w");

モード	機能	ファイルが存在しないとき
r	テキストデータの読み込み	エラーになる
w	テキストデータの書き込み	ファイルを新規作成する
a	テキストデータへの追加書き込み	ファイルを新規作成する
r+	テキストデータを更新モードで開く(書き込み・読み込み共に可)	エラーになる
w+	テキストデータを更新モードで開く(書き込み・読み込み共に可)	ファイルを新規作成する
a+	テキストデータを更新モードで開く(書き込み・読み込み共に可)	ファイルを新規作成する
rb	バイナリデータの読み込み	エラーになる
wb	バイナリデータの書き込み	ファイルを新規作成する
ab	バイナリデータへの追加書き込み	ファイルを新規作成する
rb+ / r+b	バイナリデータを更新モードで開く(書き込み・読み込み共に可)	エラーになる
wb+ / w+b	バイナリデータを更新モードで開く(書き込み・読み込み共に可)	ファイルを新規作成する
ab+ / a+b	バイナリデータを更新モードで開く(書き込み・読み込み共に可)	ファイルを新規作成する

ファイルへの書き込み
fprintf() fprintf(ファイルポインタ,書き込み文字列,変数・・・); 文字列をファイルに書き込む fprintf(fp,"no=%d",i);

ファイルのクローズ
fclose() fclose(ファイルポインタ); 指定したファイルポインタのファイルを閉じる fclose(fp);

# include <stdio.h>
# include <stdlib.h>
 
# define SIZE    256
 
void main() {
    FILE *file;                                 // ファイルポインタ(読み込み用)
    char line[SIZE];                            //  読み込む行
    line[0] = '¥0';                             //  初期化(空文字列)
    file = fopen("c:¥¥test¥¥sample.txt", "r");  // ファイルを読み込み用にオープン(開く)
    if (file == NULL) {                         // オープンに失敗した場合
        printf("ファイルが開けません。¥n");        // エラーメッセージを出して
        exit(1);                                // 異常終了
    }
    //  ファイルのデータ読み込む
    while ( fgets(line, SIZE, file) != NULL ) {
        printf("%s", line);
    }
    fclose(file);          // ファイルをクローズ(閉じる)
}

実行結果①(入力した値が同じ場合)
Hello World.
ABCDEF

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

指定したサイズの文字列を
ファイルから読み込む。 fgets(s,10,fp);1文字ごとのファイルの読み込み

# include <stdio.h>
# include <stdlib.h>
 
void main() {
    FILE *file;                                   // ファイルポインタ(読み込み用)
    int c;                                        //  読み込む文字のコード
    file = fopen("c:¥¥test¥¥sample.txt", "r");    // ファイルを読み込み用にオープン(開く)
    if (file == NULL) {                            // オープンに失敗した場合
        printf("ファイルが開けません。¥n");               

 // エラーメッセージを出して
        exit(1);                                          

  // 異常終了
    }
    //    ファイルのデータ読み込む
    while ( (c=fgetc(file)) != EOF){
        printf("%c",(char)c);
    }
    fclose(file);          // ファイルをクローズ(閉じる)
}

fgetc() fgetc(ファイルポインタ); 指定したテキスト

ファイルか
一文字読み込む。戻り値が文字コード。EOFが終了。 c =

fgetc(fp);

fgetc()は一文字
fgets()は一行

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

# include <stdio.h>
# include <stdlib.h>
 
void main() {
    FILE* file;
    int i;
    //  書き込むデータ
    char wdata[] = { 0x10 , 0x1a , 0x1e , 0x1f };
    char rdata[4];
    //バイナリデータの書き込み
    file = fopen( "C:¥¥test¥¥test.bin", "wb" );
    if( file == NULL ){
        printf( "ファイルオープンに失敗しました。\n" );
        exit( 1 );
    }
    fwrite( wdata, sizeof(char), sizeof(wdata), file );
    fclose(file);          // ファイルをクローズ(閉じる)
    //バイナリデータの書き込み
    file = fopen( "C:¥¥test¥¥test.bin", "rb" );
    if( file == NULL ){
        printf( "ファイルオープンに失敗しました。\n" );
        exit( 1 );
    }
    fread( rdata, sizeof(char), sizeof(rdata), file );
    fclose(file);          // ファイルをクローズ(閉じる)
    //  結果を表示
    for(i = 0; i < sizeof(rdata) ; i++){
        printf("%x ",rdata[i]);
    }
    printf("¥n");
}

実行結果
10 1a 1e 1f

fwrite() fwrite(データ,データのバイト長,
データの数,ファイルポインタ); バイナリデータ
の書き込み。 fwrite(data,sizeof(int),sizeof(data),fp);
fread() fread(データ,データのバイト長,
データの数,ファイルポインタ); バイナリデータ
の読み込み。 fread(data,sizeof(int),sizeof(data),fp);

読み込むときには大きな問題があります。それは、読み込むべきファイルの大きさが分からないということ
ファイルの大きさを取得し、その分のメモリを 確保してデータを読み込むことです。それには、fseek()関数、およびftell()

関数が必要となります。

# include <stdio.h>
# include <stdlib.h>
 
void main() {
    FILE* file;
    int i,size;
    //  書き込むデータ
    char* rdata;
    //バイナリデータの書き込み
    file = fopen( "C:¥¥test¥¥test.bin", "rb" );
    if( file == NULL ){
        printf( "ファイルオープンに失敗しました。¥n" );
        exit( 1 );
    }
    //  ファイルの最後までシーク
    fseek(file, 0, SEEK_END);
    //  ファイルの大きさを取得
    size = ftell(file);
    //  メモリのサイズだけ、配列を動的に生成
    rdata = (char*)malloc(sizeof(char)*size);
    //  再るの最後までポインタを戻す
    fseek(file,0,SEEK_SET);
    fread( rdata, sizeof(char), size, file );
    fclose(file);          // ファイルをクローズ(閉じる)
    //  結果を表示
    for(i = 0; i < size ; i++){
        printf("%x ",rdata[i]);
    }
    printf("¥n");
    //  メモリ解放
    free(rdata);
}

fseek() fseek(ファイルポインタ,
移動バイト数,開始位置); ファイルを開始位置から、移動バイト数移動する fseek(fp, 0L, SEEK_END);
ftell() ftell(ファイルポインタ); 現在のファイル位

置の値をバイト数で返す。 ftell(fp);

SEEK_SET ファイルの先頭
SEEK_CUR ファイルの現在位置
SEEK_END ファイルの終端

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?