8
12

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-03-26

#1. はじめに
今回はC言語の基礎について記載する.

##実行環境
###OS

一応,バージョンは最新にしておく.

###テキストエディタ

その他,自分に合ったテキストエディタを使用する.

※インデント機能や予約語の色分け機能など,C言語プログラミングに向いたテキストエディタを使用すること.

テキストエンコーディング:UTF-8
改行コード:LF(UNIX)

##ソース作成から実行まで

  1. テキストエディタで***.cの形式で保存.***の部分(ソースファイル名)は自分で決めて良い.
  2. ターミナル.appを開き,以下を入力.$は入力せず,それ以降を入力しreturnキーを押下.

ソースファイル名実行ファイル名は自分で決めて,置き換えること.
保存先はデスクトップとする.

terminal
$ cd desktop
$ gcc -o 実行ファイル名 ソースファイル名.c
$ ./実行ファイル名

上では

  • 保存先(デスクトップへ移動)
  • コンパイル(ソースファイルと同じフォルダに,実行ファイルの生成)
  • 実行

をしている.

※実際は,ソースファイル名と実行ファイル名は同じにした方がわかりやすい.

保存先:デスクトップ直下にあるsrcフォルダ
ソースファイル名:test.c
実行ファイル名:test

terminal
$ cd desktop/a
$ gcc -o test test.c
$ ./test

gccコマンドでerrorwarningが発生した際は,プログラムの記述に何らかの誤りがある.
誤りを正し,保存してから再びgccコマンドでコンパイルをする.
※保存を忘れると,修正が反映されない.

##用語
ブロック:中括弧{ },またはその内部.この中の処理が1つの場合は,{ }を省略できる.
変数:値(整数,小数,文字)を入れる箱.同じ変数に再び値を代入すると,値は上書きされる.

##フォーマット

使用頻度が高いものを示す.

説明 入力 出力
int 整数 %d %d
double 小数 %lf %f
char 文字 %c %c
char[ ] 文字列 %s %s

※double型の入力フォーマットである%lflを忘れないこと.

※文字とは1文字のみであり,文字列とは複数の文字の並び.

##無限ループ時の対処
プログラムに誤りがあり無限ループに陥ってしまった場合は,ターミナルで
control+cキー押下で終了する.

これでも終了しない場合は,macの強制終了で停止を試みること.

ちなみにターミナルの画面をクリアしたい際は,ターミナルで
control+lキーを押下する.

#2. 内容

##2.1. 基本構文

test1.c
int main(void)
{

    return 0;
}

基本的にプログラムは,main関数内部かつreturn 0;より上に書く.

##2.2. コメント(注釈)
プログラムには影響しない.プログラムを作成・共有する際のメモ.
//以降から行末まで(=1行コメント),/**/で囲った部分(=複数行コメント)がコメントアウトされた部分となる.

test2.c
int main(void)
{
    //1行コメント
    
    /*
       複数行コメント
       複数行コメント
    */

    return 0;
}

##2.3. 出力(表示)

printf() という関数を用いる.

また,printf()という関数はstdio.hという標準ライブラリに定義されているため,これを読み込む処理が必要となる.このようなライブラリ読み込みは先頭行に書く.
詳しくは下記サイト等を参考にするとよい.

文字列は" "で囲むこと.
改行は\n(Windows環境なら¥n)で表現.

※処理の末尾にはセミコロン;を付け忘れないこと.

test3.c
#include<stdio.h>  //printf()を使用するため,これが定義されている stdio.h を読み込む
int main(void)                           //main関数
{                                        //main関数の有効範囲開始端
    printf("QiitaでC言語プログラミング\n"); //指定した文字列の表示

    return 0;                            //main関数の終了を表す.
}                                        //main関数の有効範囲終了端

ちなみに,\¥)はエスケープ文字といい,特殊文字を表現する際に用いる.
例えば,

printf("   文字列は\"\"で囲うこと.改行には\\nと書く   \n");

とすると,

   文字列は""で囲うこと.改行には\nと書く

と表示される.
ダブルクォーテーション" "は二重になってしまうため,\を直前に置くことで表示する.
また,\自身も特殊な意味を持つ文字のため,"\\"とすることで\と表示する.

% を表示するには次のようにする.

printf(" %% \n");

こうすると

 %

と表示される.

##2.4. 変数(int,double,char)
変数名は文字(アルファベット)またはアンダースコア_から始まること.
定義する際は,直前にデータ型を記述する.これによって,どのようなデータかを決める.

特によく用いるデータ型としてint型,double型,char型を例に挙げる.

test4.c
#include<stdio.h>
int main(void)
{
    int a=10;      //整数型変数aに10を代入
    double b=3.5;  //小数型変数bに3.5を代入
    char c='A';    //文字型変数cにAを代入.シングルクォーテーションで文字を囲むこと

    printf("aの値は%dです\n",a);   //表示,intの型出力フォーマットは%dであり, ,a で変数を指定
    printf("bの値は%fです\n",b);   //表示,doubleの型出力フォーマットは%fであり, ,b で変数を指定
    printf("cの文字は%cです\n",c); //表示,charの型出力フォーマットは%cであり, ,c で変数を指定

    return 0;
}

なお,上のように変数の宣言と同時に値を代入することを初期化といい,その値を初期値という.

ただし,上の場合は小数表示において,小数点以下第6位まで表示される.
例として,小数第3位まで表示したい場合は次のようにする.

double b=1.234;
printf("%.3f\n",b);

また,今回変数を使った部分に直接値を設定することも可能である.

printf("整数=%d\n",10);
printf("小数=%f\n",3.5);
printf("文字=%c\n",'A');
printf("文字列=%s\n","ABCabc012");

printf("5+10=%d\n",5+10); //計算結果を表示することも可能.四則演算については次項で説明する.

##2.5. 四則演算

加算に+,減算に-,乗算に*,除算に/,剰余演算に%を用いる.
自身の変数で演算して代入することも可能.
また,その際は短縮表記もある.
例えば,a=a+5a+=5(加算代入演算子という)と短縮できる.
a=a+1a++(後置増分演算子という),または++a(前置増分演算子という)と短縮できる.

a=a++ではないので注意.上の後置増分演算子の場合は,短縮の際a=は不要である.
(前置,後置の違いはここでは触れないが,単体で扱う際はどちらも同じ結果となる)

test5.c
#include<stdio.h>
int main(void)
{
    int a;

    a=10;
    a=a+5;               //10+5をaに代入,短縮系:a+=5
    printf("a=%d\n",a);

    a=10;
    a++;               //10+1をaに代入,++aも同様
    printf("a=%d\n",a);

    a=10;
    a=10-4;              //10-4をaに代入,短縮系:a-=4
    printf("a=%d\n",a);

    a=10;
    a--;               //10-1をaに代入,--aも同様
    printf("a=%d\n",a);

    a=10;
    a=10*3;              //10×3をaに代入,短縮系:a*=3
    printf("a=%d\n",a);

    a=10;
    a=10/2;              //10÷2をaに代入,短縮系:a/=2
    printf("a=%d\n",a);

    a=10;
    a=10%3;              //10÷2の余りをaに代入,短縮系:a%=3
    printf("a=%d\n",a);


    return 0;
}

##2.6. 入力

scanf() という.
printf()同様にstdio.hに定義されている.

test6.c
#include<stdio.h>  //scanf,printf()を使用するため,これらが定義されている stdio.h を読み込む
int main(void)
{
    int a;
    double b;
    char c;

    printf("整数の入力---->"); //整数入力を促す表示,これがないと入力待ちが分かりづらい
    scanf("%d",&a);          //入力,int型の出力フォーマットは%dであり, ,&a で変数(厳密にはアドレス)を指定
    printf("小数の入力---->"); //小数入力を促す表示,これがないと入力待ちが分かりづらい
    scanf("%lf",&b);          //入力,double型の出力フォーマットは%lfであり, ,&b で変数(厳密にはアドレス)を指定
    printf("文字の入力---->"); //文字入力を促す表示,これがないと入力待ちが分かりづらい
    scanf("%c",&c);          //入力,int型の出力フォーマットは%cであり, ,&c で変数(厳密にはアドレス)を指定

    printf("入力された整数は%dです\n",a); //int型変数aの表示
    printf("入力された小数は%fです\n",b); //double型変数bの表示
    printf("入力された文字は%cです\n",c); //char型変数cの表示

    return 0;
}

scanf()において,アドレス演算子&の付け忘れに注意(ここではアドレス演算子の意味には触れない)

##2.7. 配列
一つの変数に複数の値を保持するための仕組み.
0から始まる整数の添え字で個々の要素を区別する.

宣言時にデータを格納(初期化)してる場合に限り,確保する要素数(メモリ量)を省略できる.配列長はその大きさとなる.
例を次に示す.

int a[]={10,3,0,-5,2}; //確保するメモリ量を省略.データを5つ入れてるので int a[5]=・・・ と同じ

int b[]; //このように初期化(宣言時にデータを格納)していない場合はエラーとなる.この場合は,確保するメモリ量は省略してはならない.

※「1つ目の値は要素0番目,2つ目の値は要素1番目,・・・,n個目の値は要素n-1番目」のようにずれていることに注意

test7.c
#include<stdio.h>
int main(void)
{
    int a[3]={3,5,1};          //int型配列で要素を3つ確保
    double b[3]={3.4,5.0,2.8}; //double型配列で要素を3つ確保
    char c[3]={'d','4','B'};   //char型配列で要素を3つ確保

    a[1]=4;                    //要素の上書き,5 → 4
    b[0]=3.2;                  //要素の上書き,3.4 → 3.2
    c[2]='A';                  //要素の上書き,'B' → 'A'
 
    //要素を指定して1つずつ表示
    printf("a[0]=%d\n",a[0]);  
    printf("a[1]=%d\n",a[1]);
    printf("a[2]=%d\n",a[2]);

    printf("b[0]=%f\n",b[0]);  
    printf("b[1]=%f\n",b[1]);
    printf("b[2]=%f\n",b[2]);

    printf("c[0]=%c\n",c[0]);  
    printf("c[1]=%c\n",c[1]);
    printf("c[2]=%c\n",c[2]);

    return 0;
}

配列の要素を1つずつ表示するのは面倒だが,繰り返し文を使うことで楽にできる.これについては後述する.

##2.8. 文字列

C言語にはStringという文字列を格納する変数の型がない.
そこで,char型配列で実現する.

格納文字列長+1の要素を確保すること.余分に1つ確保するのは空文字,ヌル記号,終端記号と呼ばれる\0を格納するため(ここではヌル記号の意味には触れない).
ヌル記号\0はヌルポインタNULLとは異なる(ここではヌルポインタの意味には触れない).

一応言っておくと,\0で1文字という扱いである.

さらに言うと,全角文字の場合は半角文字よりもメモリを多く消費する.半角文字3文字分(=要素3つ分)である.
string.hに定義されているstrlenという関数で調べてみるとわかる)

test8.c
#include<stdio.h>
int main(void)
{
    char str1[10]="test";
    char str2[20]="message";
    char str3[15]={'A','a','0'};
    char str4[20];
    char str5[20];

    printf("文字列str4の入力---->");
    scanf("%s",&str4[0]);          //要素0番目から入力

    printf("文字列str5の入力---->");
    scanf("%s",str5);              //&をなくすと,先頭(要素0番目)からの入力となり,要素番号の指定もなくす必要がある

    str2[7]='!';                   //要素7番目(文字列の8文字目)を指定し文字の上書き

    printf("str1=%s\n",str1);
    printf("str2=%s\n",str2);
    printf("str3=%s\n",str3);
    printf("str4=%s\n",str4);
    printf("str5=%s\n",str5);

    return 0;  
}

ちなみに,main関数のreturn 0;を省略して

char str[4];
printf("%s\n",str);

とすると文字化けする.
また,格納文字列長+1の要素を確保せずに文字列の標準関数を使用すると,

[User:desktop user]$ ./string
Abort trap 6

のようなエラーが発生する.
例えばstring.hからstrcpyという関数を使用する際,コピー先の配列の要素数が格納文字列長以下(ヌル記号\0が格納できない)の時に,上のエラーが発生する.

##2.9. 条件分岐

条件式は次の通り.
バーティカルバー|は,Qiitaの表の仕様の都合により全角で書いていますが,実際に使う際は半角に直すこと.

条件式 意味
a==b aとbは等しい
a!=b aとbは等しくない
a<b aはbより小さい(未満)
a>b aはbより大きい(超過)
a<=b aはbより小さいか等しい(以下)
a>=b aはbより大きいか等しい(以上)
a&&b aでありb(aかつb,論理積(AND))
a||b aまたはb(論理和(OR))
!a !aではない(否定(NOT))

###2.9.1. if文

test9-1-1.c
#include<stdio.h>
int main(void)
{
    int a=10;

    if(a<5){
        printf("aは5より小さい\n");
    }
    else if(a==5){
        printf("aは5と等しい\n");
    }
    else{
        printf("aは5より大きい\n");
    }

    return 0;
}

上のようにif文の中の処理が1つの場合は,中括弧{ }は省略できる.これを次に表す.

test9-1-2.c
#include<stdio.h>
int main(void)
{
    int a=10;

    if(a<5) printf("aは5より小さい\n");
    else if(a==5) printf("aは5と等しい\n");
    else printf("aは5より大きい\n");

    return 0;
}

また,if文の条件を次のように表す場合がある.

int a=5;
if(a){
    printf("aは0ではない\n");
}else{   //if文で指定する場合は,if(!a)とする
    printf("aは0である\n");
}

論理値にtruefalseがあり,数値の場合は0以外の数(便宜的に1)がtrue,0がfalseである.
trueとは条件を満たすこと()を,falseとは条件を満たさないこと()を示す.

ちなみに,truefalseという2種類のみの値を扱うbool型がある.
これはstdbool.hに定義がある.読み込むことで使用できる.

test9-1-3.c
#include<stdio.h>
#include<stdbool.h>
int main(void)
{
    bool flag=true;

    if(flag) printf("flagはtrue\n");
    if(!flag) printf("flagはfalse\n");

    return 0;
}

###2.9.2. switch文
if文とは異なり,switch()の括弧の中(式)やcase句の値が整数である場合のみ使用することができる.そのため,小数・文字列などをもとに条件分岐はできない.

test9-2.c
#include<stdio.h>
int main(void)
{
    int a=1;

    switch(a){  //aをswitch文の判定にかける
        case 0: //aが0のとき
            printf("case 0\n");
            break;
        case 1: //aが1のとき
            printf("case 1\n");
            break;
        case 2: //aが2のとき
            printf("case 2\n");
            break;
        default: //aが上記以外のとき
            printf("default\n");
            break; //switch文から抜ける
    }

    char b='A'; //'A'はASCIIコードにおいて10進数で65である

    switch(b){  //bをswitch文の判定にかける
        case 30: //bが0のとき
            printf("case 30\n");
            break;
        case 48: //bが48のとき
            printf("case 48\n");
            break;
        case 65: //bが65のとき
            printf("case 65\n");
            break;
        default: //bが上記以外のとき
            printf("default\n");
            break; //switch文から抜ける
    }

    char c='b';

    switch(c){  //bをswitch文の判定にかける
        case 'a': //cが'a'のとき
            printf("case 'a'\n");
            break;
        case 'b': //cが'b'のとき
            printf("case 'b'\n");
            break;
        case 'c': //cが'c'のとき
            printf("case 'c'\n");
            break;
        default: //cが上記以外のとき
            printf("default\n");
            break; //switch文から抜ける
    }

    return 0;
}

※各case句とdefault句で必ずbreakを書くこと.breakについては後述する.
これを忘れると,分岐条件に関係なくswitch文を上から順に実行してしまう.

##2.10. 繰り返し処理

同じ処理を繰り返すためのもの.特に配列との相性が良く,併せて使うことが多い.

###2.10.1. for文

構文
for(初期化;継続処理;増分){
    繰り返す処理
}
test10-1.c
#include<stdio.h>
int main(void)
{
    int a[5]={5,3,7,2,10};
    int i;

    for(i=0;i<5;i++){
        printf("%d個目のデータは%d\n",i+1,a[i]);
    }

    return 0;
}

for文の継続条件を指定しない場合,無限ループとなる.脱出の仕方は後述.

for文による無限ループ
for(;;){
    printf("test\n");
}

###2.10.2. while文

構文
while(継続条件){
    繰り返す処理
}
test10-2.c
#include<stdio.h>
int main(void)
{
    int a[5]={5,3,7,2,10};
    int i=0;
    
    while(i<5){
        printf("%d個目のデータは%d\n",i+1,a[i]);
        i++;
    }

    return 0;
}

while文の継続条件を**真(true)**にした場合,無限ループとなる.脱出の仕方は後述.

while文による無限ループ1
while(1){
    printf("test\n");
}
while文による無限ループ2
#include<stdbool.h>
while(true){
    printf("test\n");
}

###2.10.3 do-while文

while文との違いは次の構文から分かるが,処理をしてから条件を満たしているかを比較する.
つまり,最初から条件を満たしていなくても最低1回は処理を行う.

構文
do{
    繰り返す処理
}while(継続条件);
test10-3.c
#include<stdio.h>
int main(void)
{
    int a[5]={5,3,7,2,10};
    int i=0;

    do{
        printf("%d個目のデータは%d\n",i+1,a[i]); //最低1回は実行される
        i++;
    }while(i<0);  //iが0未満,絶対に条件を満たすことはない


    return 0;
}

##2.11. break文

条件を満たす時,最も内側のループを1つだけ抜ける.
ループではないが,switch文でも用いる.

test11.c
#include<stdio.h>
int main(void)
{
    int i,j;

    for(i=0;i<3;i++){
        for(j=100;j>90;j--){
            printf("i=%d,j=%d\n",i,j);
            if(j==95) break;
        }
    }

    return 0;
}

##2.12. continue文

条件を満たす時,本来繰り返す処理をスキップする.

test12.c
#include<stdio.h>
int main(void)
{
    int i;

    for(i=0;i<5;i++){
        if(i==2) continue;
        printf("i=%d\n",i);
    }

    return 0;
}

##2.13. goto文

条件を満たす時,指定された場所へジャンプする.
多重ループから一気に抜ける時などに用いる.break単体ではプログラムが複雑になってしまうため,こちらを使う方が良い.

test13.c
#include<stdio.h>
int main(void)
{
    int i,j;

    for(i=0;i<3;i++){
        for(j=100;j>90;j--){
            if(j==95) goto abc; //jが95の時,abcへジャンプする
            printf("i=%d,j=%d\n",i,j);
        }
    }

    abc:; //ジャンプ先.これの名前は任意で設定可.セミコロンは処理を入れる時の末尾につけるものであり,この場合はなくても良い
 
    printf("End\n");

    return 0;
}

##2.14. 関数
main関数の他に関数を自作することができる.自作関数は複数書くことができる.

自作した関数はmain関数の下に書く.ただ,これではmain関数内で自作関数が呼び出された時にコンピュータは理解できない.プログラムはコードの上から下へ順に読み,実行するためである.
そのため,main関数の上にはプロトタイプ宣言を書く.書き方としては,関数の定義と同じく次のように書く.

戻り値 関数名(引数);

引数:その関数に渡す値.
戻り値:呼び出した側に返す値.

###2.14.1. 引数なし・戻り値なし

test14-1.c
#include<stdio.h>

void sub(void);   //引数や戻り値はない場合は void と書く

int main(void)    //main関数の戻り値の int は return 0; の 0 のデータ型のこと
{
    sub();        //呼び出しは 関数名() と書く
    
    return 0;
}

void sub(void)
{
    printf("sub\n");
}

自作関数において,戻り値がない場合はreturnを書かない.または次のように書いても良い.

void sub(void)
{
    printf("sub\n");

    return; //戻り値を指定しない
}

###2.14.2. 引数あり・戻り値なし

変数の有効範囲はブロック{ }単位である.そのため,main関数で宣言した変数は自作関数では使えない.とはいえ.同じ変数名で自作関数に宣言してもmain関数のものとは全く別物の変数になる.そのため,引数に渡したい変数を書いて渡すことで実現する.
引数には変数や配列だけでなく,数値・文字・文字列を直接渡すことができる.
また,引数は複数渡すことができる.

同じデータを扱う変数でも,main関数と自作関数で変数名が異なっていても良い.ただ,名前は同じ方がわかりやすい.

test14-2.c
#include<stdio.h>

void sub(int e,double f,char g,char h[],int i,double j,char k,char l[]); //関数の定義と同じものを書く

int main(void)
{
    int a=10;
    double b=3.5;
    char c='A';
    char d[10]="test";

    sub(a,b,c,d,8,4.7,'b',"Qiita"); //宣言時にデータ型は書いたので,呼び出し時は変数名・配列名のみ.値は変数や配列に格納することなく直接渡すことも可能

    return 0;
}

void sub(int e,double f,char g,char h[],int i,double j,char k,char l[]) //関数の定義.変数名はmain関数とは異なっていても良い
{
    printf("e=%d\n",e);
    printf("f=%f\n",f);
    printf("g=%c\n",g);
    printf("h=%s\n",h);
    printf("i=%d\n",i);
    printf("j=%f\n",j);
    printf("k=%c\n",k);
    printf("l=%s\n",l);
}

###2.14.3. 引数なし・戻り値あり

戻り値に設定できる値は1つのみ.
関数を呼び出すことで値が返ってくるため,呼び出した関数を変数や配列に格納する形式で記述する.
引数同様に変数や配列に格納することなく,値を直接指定することもできる.

test14-3.c
#include<stdio.h>

int sub1(void);
int sub2(void);

int main(void)
{
    int a=sub1();
    int b=sub2();

    printf("a=%d\n",a);
    printf("b=%d\n",b);
   
    return 0;
}

int sub1(void)
{
    int n=10;

    return n;
}

int sub2(void)
{
    return 100;
}

###2.14.4. 引数あり・戻り値あり

test14-4.c
#include<stdio.h>

int f(int x);

int main(void)
{
    int x,y;

    printf("f(x)=5x+3を計算します\n");
    printf("整数xを入力---->");
    scanf("%d",&x);

    y=f(x);

    printf("y = f(%d) = 5×%d+3 = %d\n",x,x,y);

    return 0;
}

int f(int x)
{
    return 5*x+3;
}

##2.15. 2次元配列

各要素を表で表すと次のようになる.

[0] [0] [0] [1] [0] [2] ・・・ [0] [n]
[1] [0] [1] [1] [1] [2] ・・・ [1] [n]
[2] [0] [2] [1] [2] [2] ・・・ [2] [n]
・・・ ・・・ ・・・ ・・・ ・・・
[n] [0] [n] [1] [n] [2] ・・・ [n] [n]
test15.c
#include<stdio.h>
int main(void)
{
    int a[3][2] = {{5, 0}, {3, 7}, {-4, 2}}; //3行4列
    //a[3][2]により,2つの要素を3つ分作る

    for (int i = 0; i < 3; i++){             //行表示
        for (int j = 0; j < 2; j++){         //列表示
            printf("%3d", a[i][j]);
        }
        printf("\n");                        //次の行へ
    }

    return 0;
}

#3. おわりに

深くは記載しません.

8
12
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
8
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?