#1. はじめに
今回はC言語の基礎について記載する.
##実行環境
###OS
一応,バージョンは最新にしておく.
###テキストエディタ
- mi
- Visual Studio Code(日本語化は必要に応じてする)
その他,自分に合ったテキストエディタを使用する.
※インデント機能や予約語の色分け機能など,C言語プログラミングに向いたテキストエディタを使用すること.
テキストエンコーディング:UTF-8
改行コード:LF(UNIX)
##ソース作成から実行まで
- テキストエディタで
***.c
の形式で保存.***
の部分(ソースファイル名)は自分で決めて良い. -
ターミナル.app
を開き,以下を入力.$
は入力せず,それ以降を入力しreturnキーを押下.
ソースファイル名
と実行ファイル名
は自分で決めて,置き換えること.
保存先はデスクトップとする.
$ cd desktop
$ gcc -o 実行ファイル名 ソースファイル名.c
$ ./実行ファイル名
上では
- 保存先(デスクトップへ移動)
- コンパイル(ソースファイルと同じフォルダに,実行ファイルの生成)
- 実行
をしている.
※実際は,ソースファイル名と実行ファイル名は同じにした方がわかりやすい.
例
保存先:デスクトップ直下にあるsrc
フォルダ
ソースファイル名:test.c
実行ファイル名:test
$ cd desktop/a
$ gcc -o test test.c
$ ./test
gccコマンドでerror
やwarning
が発生した際は,プログラムの記述に何らかの誤りがある.
誤りを正し,保存してから再びgccコマンドでコンパイルをする.
※保存を忘れると,修正が反映されない.
##用語
ブロック:中括弧{ }
,またはその内部.この中の処理が1つの場合は,{ }
を省略できる.
変数:値(整数,小数,文字)を入れる箱.同じ変数に再び値を代入すると,値は上書きされる.
##フォーマット
使用頻度が高いものを示す.
型 | 説明 | 入力 | 出力 |
---|---|---|---|
int | 整数 | %d | %d |
double | 小数 | %lf | %f |
char | 文字 | %c | %c |
char[ ] | 文字列 | %s | %s |
※double型の入力フォーマットである%lf
のl
を忘れないこと.
※文字とは1文字のみであり,文字列とは複数の文字の並び.
##無限ループ時の対処
プログラムに誤りがあり無限ループに陥ってしまった場合は,ターミナルで
control
+c
キー押下で終了する.
これでも終了しない場合は,macの強制終了で停止を試みること.
ちなみにターミナルの画面をクリアしたい際は,ターミナルで
control
+l
キーを押下する.
#2. 内容
##2.1. 基本構文
int main(void)
{
return 0;
}
基本的にプログラムは,main
関数内部かつreturn 0;
より上に書く.
##2.2. コメント(注釈)
プログラムには影響しない.プログラムを作成・共有する際のメモ.
//
以降から行末まで(=1行コメント),/*
と*/
で囲った部分(=複数行コメント)がコメントアウトされた部分となる.
int main(void)
{
//1行コメント
/*
複数行コメント
複数行コメント
*/
return 0;
}
##2.3. 出力(表示)
printf()
という関数を用いる.
また,printf()
という関数はstdio.h
という標準ライブラリに定義されているため,これを読み込む処理が必要となる.このようなライブラリ読み込みは先頭行に書く.
詳しくは下記サイト等を参考にするとよい.
文字列は" "
で囲むこと.
改行は\n
(Windows環境なら¥n
)で表現.
※処理の末尾にはセミコロン;
を付け忘れないこと.
#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型を例に挙げる.
#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+5
はa+=5
(加算代入演算子という)と短縮できる.
a=a+1
はa++
(後置増分演算子という),または++a
(前置増分演算子という)と短縮できる.
※a=a++
ではないので注意.上の後置増分演算子の場合は,短縮の際a=
は不要である.
(前置,後置の違いはここでは触れないが,単体で扱う際はどちらも同じ結果となる)
#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
に定義されている.
#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番目」のようにずれていることに注意
#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
という関数で調べてみるとわかる)
#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文
#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つの場合は,中括弧{ }
は省略できる.これを次に表す.
#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");
}
論理値にtrue
とfalse
があり,数値の場合は0以外の数(便宜的に1)がtrue
,0がfalse
である.
true
とは条件を満たすこと(真)を,false
とは条件を満たさないこと(偽)を示す.
ちなみに,true
とfalse
という2種類のみの値を扱うbool
型がある.
これはstdbool.h
に定義がある.読み込むことで使用できる.
#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
句の値が整数である場合のみ使用することができる.そのため,小数・文字列などをもとに条件分岐はできない.
#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(初期化;継続処理;増分){
繰り返す処理
}
#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(;;){
printf("test\n");
}
###2.10.2. while文
while(継続条件){
繰り返す処理
}
#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){
printf("test\n");
}
#include<stdbool.h>
while(true){
printf("test\n");
}
###2.10.3 do-while文
while文との違いは次の構文から分かるが,処理をしてから条件を満たしているかを比較する.
つまり,最初から条件を満たしていなくても最低1回は処理を行う.
do{
繰り返す処理
}while(継続条件);
#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文でも用いる.
#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文
条件を満たす時,本来繰り返す処理をスキップする.
#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
単体ではプログラムが複雑になってしまうため,こちらを使う方が良い.
#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. 引数なし・戻り値なし
#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関数と自作関数で変数名が異なっていても良い.ただ,名前は同じ方がわかりやすい.
#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つのみ.
関数を呼び出すことで値が返ってくるため,呼び出した関数を変数や配列に格納する形式で記述する.
引数同様に変数や配列に格納することなく,値を直接指定することもできる.
#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. 引数あり・戻り値あり
#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] |
#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. おわりに
深くは記載しません.