C LANGUAGE TECHNOLOGY

【C言語】データ型とは【変数定義,変数名のルールと命名規則,定数,配列,文字配列,型修飾子】

悩んでいる人

C言語のデータ型を教えて!

こういった悩みにお答えします.

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOSの授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校コンピュータサイエンス学部2021年の世界大学学術ランキングで20位)で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発
  • プログラミング歴15年以上,習得している言語: C/C++Solidity,Java,Python,Ruby,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開

こういった私から学べます.

データ型

データ型とは,データを表現する方法のことを言います.

あなたがこれまでにC言語で変数を定義した時,intという予約語を利用しましたよね.

このintとはintegerの略で整数という意味です.

C言語では,整数の他にも様々なデータ型を扱うことができます.

それでは,なぜデータ型というものが必要なのでしょうか.

コンピュータの内部では,値は2進数で表現されています.

例えば,10進数で609という整数値は16ビットの2進数では以下のようになります.

後ろの8ビット(0110 0001)だけを見ると,16進数では0x61,10進数で97,文字(アスキーコード)としてみると’a’になります.

このようにデータの解釈により値が異なります.

したがって,コンピュータ内部では2進数のビット列に,どのような意味をもたせるかということをデータ型で区別するというわけです.

C言語には,下表の4つの基本データ型がありますので,それぞれ解説していきます.

データ型意味
char文字型
int整数型
float単精度浮動小数点型
double倍精度浮動小数点型

char型(文字型)

char型は,文字型とも呼ばれ,1文字を保持することができるデータ型です.

char型のサイズが1バイトであると定義されています.

ほとんどの処理系では,8ビットの大きさを持っています .

※Digital Signal Processors(DSPs)等には,1バイトが8ビットでないものが存在します.

ちなみに,sizeof演算子で得られるバイト数もchar型の何倍かという意味になっています.

つまり,char型のサイズこそが,C言語で取り扱うデータの基本単位(バイト)になります.

したがって,厳密にはchar型のサイズは8ビットであると決まってはいないのですが,ほぼ全ての処理系で8ビットなので,そう考えてもらって大丈夫です.

あなたが利用している一般的な処理系では,char型は1バイト8ビット(1バイト)の文字型とみなすことができます.

1バイトが8ビットでないDSPsを知りたいあなたは,sizeof演算子の使い方の記事を読みましょう.

C言語 sizeof演算子
【C言語】sizeof演算子の使い方

こういった悩みにお答えします. こういった私から学べます. 目次1 sizeof演算子2 sizeof演算子でデータ型のサイズの計算3 sizeof演算子で変数のサイズの計算4 sizeof演算子でポ ...

続きを見る

下表に示すchar型の修飾子(signed,unsigned)を利用して,符号ありと符号なしを区別して記述できます.

単にcharと定義すると,符号ありか符号なしになるかは処理系依存となります.

したがって,移植性を考えると,修飾子付きで定義した方が無難です.

データ型意味
signed char符号付きchar型:-128~127
unsigned char符号なしchar型:0~255
charchar型:処理系依存

int型(整数型)

int型は,もともとCPUのレジスタの自然長の大きさを持つデータ型として定義されていました.

int型のサイズは通常は4バイト(32ビット)になります.

現在のCPUのレジスタは8バイト(64ビット)が主流なので,レジスタの自然長にはなりません.

int型は下表で示す修飾子を付けることで,意味の異なる様々な型を表すことができます.

符号に関しては,デフォルトでは(何も付けないと)signedになります.

データ型意味
signed符号あり
unsigned符号なし
short短い整数
long長い整数
long long非常に長い整数

代表的なデータ型のサイズを下表に示します.

現在,64ビットマシン(CPUとOSを含む)がメインですが,多くの処理系では過去のプログラムの移植性を考慮して,64ビットOSにおけるint型はもともとの定義8バイト(64ビット)ではなく,4バイト(32ビット)になっています.

また,64ビットマシンを利用していても,OSが32ビットである場合は,32ビットOSの欄を参照して下さい.

C言語ではデータ型のサイズは厳密には決められておらず,型のサイズ(バイト)はsizeof演算子で取得します.

同様に,データ型の最小値と最大値の定義はlimits.hをインクルードして参照します.

多くの処理系では,下表のようになっています.

移植性を考慮しなければならない場合は,sizeof演算子やlimits.h内の定義を参照して下さい.

データ型32ビットOSでのサイズ(バイト)64ビットOSでのサイズ(バイト)
char11
short22
int44
long48
long long88

int型は,以下で示すような任意の組み合わせで定義が可能です.

具体的には,3つのグループの少なくとも1つあればよく,任意の組み合わせと省略が可能です.

$$ \begin{bmatrix} signed \\ unsigned \\ \end{bmatrix} \begin{pmatrix} short \\ long \\ long long \\ \end{pmatrix} \begin{vmatrix} int \\ \end{vmatrix} $$

32ビットOSの場合,以下は同じ意味になります.

  • signed long int
  • signed long
  • signed int
  • signed

また,64ビットOSの場合,以下は同じ意味になります.

  • unsigned long int
  • unsigned long

float/double型(浮動小数点型)

実数を扱うためには,下表のfloat/double/long double型(浮動小数点型)を利用します.

その際,有効数字と丸め誤差を考慮して,通常はdouble型を利用します.

float型は精度よりもメモリ効率を優先したい場合(例えば大きな配列を確保する際にdouble型ではサイズが大きすぎて領域を確保できない場合等)に利用します.

また,long double型は,double型よりもさらに有効数字と表現可能な範囲が大きくなっています.

処理系によっては,long double型を単にdouble型とみなしてコンパイルするものもあります.

データ型名称サイズ(バイト)
float単精度浮動小数点型4
double倍精度浮動小数点型8
long double拡張倍精度浮動小数点型8以上(8,10,12,16等)

私の環境では,long double型のサイズは16バイトでした.あなたの環境でも以下の記事を参考にsizeof演算子を利用して調べてみましょう.

C言語 sizeof演算子
【C言語】sizeof演算子の使い方

こういった悩みにお答えします. こういった私から学べます. 目次1 sizeof演算子2 sizeof演算子でデータ型のサイズの計算3 sizeof演算子で変数のサイズの計算4 sizeof演算子でポ ...

続きを見る

変数定義

変数定義とは,変数を入れる箱を自分で用意することです.

変数とは,値(データ)の入れ物(箱)であり,名前が付けられています.

また,変数には,利用した値の種類(整数や実数等)による型があります.

C言語では,全ての変数は利用する前にその型と一緒に定義します.

また,その箱の中身(変数の値)は様々に変更できます.

変数はコードを書くために必要です.

変数を利用しないで複雑なコードを書くことはほぼ不可能です.

この理由として,変数を利用してデータの保存やプログラムの制御を行っているからです.

変数定義は,「型宣言子 変数名の並び」のようにします.

例えば,以下のように変数定義を行います.

また,この変数定義には,宣言も含まれています.

つまり,これらの変数を定義することが宣言することにもなります.

※変数宣言のみの方法として,extern宣言があります.

extern宣言の実例を知りたいあなたは,関数とはを読みましょう.

C言語 関数
【C言語】関数とは【プロトタイプ宣言,引数,記憶クラス指定子とスコープ,関数内外の変数の初期化】

こういった悩みにお答えします. こういった私から学べます. 目次1 関数2 関数のコード例3 関数のプロトタイプ宣言4 関数の引数5 記憶クラス指定子とスコープ5.1 変数定義5.2 変数の分類5.3 ...

続きを見る

変数定義のコードは以下になります.

実行結果は以下になります.

ここで重要なのが,9行目のint型変数aの定義です.

この行を削除してコンパイルしてみて下さい.

aが宣言されていない(’a’ undeclared)という趣旨のメッセージが出てコンパイルに失敗します.

このように,C言語では変数を利用する時に必ず定義または宣言が必要なことを覚えましょう.

先の例では変数を1つだけ定義しましたが,複数個あるときは以下のように,カンマで区切って並べて書くことができます.

これは,以下のように書くのと同様です.

また,以下の書き方もできますので,あなたがコードを理解しやすいように書きましょう.

変数名のルールと命名規則

C言語の変数名には以下のルールがあります.

  • 1文字目には英字(大文字,小文字,アンダースコア_)
  • 2文字目には英数字(大文字,小文字,数字,アンダースコア_)
  • 大文字,小文字は区別
  • 変数名は長さが31文字までは保証

これらのルールを守らないと,コンパイルエラーになります.

※変数名の長さが32文字以上の場合は処理系依存の未定義の動作になります.

これ以外にも慣習的に多くのコーディングスタンダードでは,以下のような変数名の命名規則(付け方)を行うことが多いです.

  • 局所変数名はすべて小文字
  • 記号定数はすべて大文字

また,この命名規則は,関数名,ラベル名,タグ名等にも当てはまります.

変数や関数名には,意味のある名前を付けましょう.

その変数や関数の意味を端的に表す簡潔な名前をつけるのが良いです.

その際,変数名はローマ字より,英語で付けましょう.

variable_definitions.cで利用している変数名のように,a,b,cのような名前を付けることは基本的には避けるべきです.

また,tmp0,tmp1,tmp2等の命名もしないようにしましょう.

ただし,ループの制御変数として,i,j,k等を利用することは許容範囲内です.

ローカル変数は小文字で定義し,グローバル変数は最初の1文字を大文字にして区別するようにしましょう.

#defineを利用した定数はすべて大文字で定義するようにしましょう.

Linux環境でC言語のコードを書く時は,スネークケースのように,変数や定数の単語と単語の間はアンダースコア’_’でつなぐようにしましょう.

他にも変数名をつける記法はありますが,ご参考までに.

定数

定数は,変数と同様にルールがありますので,そのルールをデータ型別に下表にまとめます.

※2進数リテラルはC11/C17規格では未採用でGCC拡張として実装されていますが,C23規格で正式採用の予定です.C++14で2進数リテラルが導入されたことが影響している可能性があります.

定数のデータ型ルールと例
int型(整数型)定数unsigned intの場合は最後にUまたはuが付きます.
16進数:最初に0x(または0X)が付きます.(例:0x61,0x7fff,0xdeadbeef)
8進数:最初に0が付きます.(例:05,0123U)
2進数:最初に0b(または0B)が付きます.(例:0b1,0b1010)
10進数:16進数,8進数,2進数以外の整数です.(例:1003,123u,-4567)
long型定数long型定数の最後にLまたはlが付きます.
Unsigned long型の場合は最後にULまたはulが付きます.
例:-123L,0x61L,07l,0x4567ul,0b100100ul
long long型定数整数定数の最後にLLまたはllが付きます.
unsigned long long型の場合は最後にULLまたはullが付きます.
例:-123LL,0x61LL,07ll,0x345ull,0b101010ull
double型(浮動小数点型)定数小数点,及びEまたはeも利用して指数表現が可能です.(例:123.45,-3.14e-25,6.45E12)
※3.14e-25は3.14*10^-25と同じ意味です.
C99からは16進数で表記可能です.先頭に0x,仮数部の後にp,最後に指数部(省略可能).(例:0x1p,0x2p3,0x2.5p4)
float型定数小数点,及びEまたはeを利用して指数表現が可能です.
浮動小数点数の最後にFまたはfが付きます.例:123.45f,-3.14e-25f,6.45E12F,0x2.5p4f
long double型定数小数点,及びEまたはeを利用して指数表現が可能です.
浮動小数点数の最後にLまたはlが付きます.
例:123.45l,-3.14e-25l,6.45E8L,0x2.5p5L
文字定数シングルクォーテーション’で挟みます.
例:’a’,’l’,’#’
特殊な文字(エスケープシーケンス)は,下表のように表現します.
文字列定数ダブルウォーテーション”でくくられた文字の列です.
文字の最後にはNULL文字(’\0’:char型(1バイト)の0)が自動的に入ります.
文字列中のダブルウォーテーションは\”で表現します.

エスケープシーケンスは下表になります.

意味書き方
改行’\n’
復帰’\r’
タブ’\t’
バックスペース’\b’
ベル’\a’
\自身’\\’
シングルクォーテーション’\’’
ダブルウォーテーション’\”’
16進ビットパターン’\xhh’(hhは16進数)
8進ビットパターン’\0oo’(ooは8進数)

各種データ型を利用したコード

各種データ型を利用したコードは以下になります.

実行結果は以下になります.

11~13行目のfloat/double/long double型で1.0 / 3.0の計算結果を比較すると,精度の違いがわかります.

配列

配列とは,様々な要素が一定の規則により並べられているデータの集合のことです.

例えば,箱が5個あって,その中にそれぞれ本が何冊か入っていたとすると,それは配列で表現できます.

この状況をコードとして書く場合,箱を変数とみなし,箱(変数)に順番に番号を付けていきます.

そして,その番号を利用して個々の箱(変数)を表します.

その番号のことを,配列の添字(そえじ:インデックス)と言います.

配列を利用するコード

配列を利用するコードは以下になります.

5個の変数を入力して配列に格納し,これらの合計値と平均値を計算します.

11行目の以下の部分が配列を定義しているところです.

#define文でMAXを5と定義しているので,xという名前が付いた5個の要素を持つ配列を定義したことになります.

100個の変数で別々の名前を付けて定義して利用しても間違いではありませんが,コードを書くのが複雑になります.

その点,配列利用すると添字を変更するだけでデータにアクセスできるので,for文等を利用して簡単にコードを書けます.

配列にアクセスするためには,添字を利用して以下のように書きます.

これは,xという配列の0番目の要素に2.3を代入する文です.ここで,C言語の配列の添字は,必ず0から始まります(0オリジンと言います).

例えば,以下の配列は,配列要素が5つのint型の配列を定義したことになります.

また,配列を定義した時に初期化したい場合は以下のように{}で囲んだ後に各々の添字に格納する要素を書きます.

以下の場合,y[0]に1,y[1]に2,y[2]に3,y[3]に4,y[4]に5を格納します.

また,C99から導入された指示付きの初期化指定子を利用して以下のように初期化しても同様になります.

以下のように指示付きの初期化指定子を利用しない添字(y[0],y[1],y[3])は0に初期化されます.

配列の定義時に初期化するコード

配列の定義時に初期化するコードは以下になります.

実行結果は以下になります.

文字配列

C言語には,文字列型というデータ型は存在しません.

それでは,C言語で文字列は扱えないのかと疑問に思うかもしれませんが,そうではありません.

文字はキャラクタコード(通常はASCIIコード)で表現されるので,1文字は1バイトの数値で表現できます.

※他にもEBCDICコードやUnicode等がありますが,キャラクタコードは一般的にはASCIIコードの事を指します.

したがって,1文字はchar型(1バイトの整数型)の変数として扱うことができます.

それでは,1文字を代入できるchar型の変数codeを定義してみましょう.

この場合はsigned,unsignedのどちらでも構いません.

そして,変数codeにアルファベットのCという1文字を代入するには,以下のようにします.

このように,通常の変数と同じように扱うことができます.

しかし,実際には変数codeにCという文字が入るのではありません.

Cという文字のASCIIコード(0x43)が代入されます.

実際には数値が入るわけですから,このCという文字のASCIIコードで’C’は整数型変数にも代入できます

char型は1文字を扱えることがわかりましたが,文字列はどのようにして扱うのでしょうか.

文字列とは1文字1文字が連続している文字の集合なので,配列の考え方がそのまま利用できます.

すなわち,char型の配列変数(文字配列)を定義して,文字列用の変数として利用します.

その時に定義した配列の大きさが,代入できる文字列の長さになります.

例えば,以下のように変数定義します.

これは10文字まで扱える変数stringの定義ができました.

この変数には,10文字(要素)まで代入できるのですが,実際には文字列の終わりを示すコード(’\0’)が文字列の最後に入るので,代入できる文字数は9文字までになります.

文字配列を作成して表示するコード

文字配列を作成して表示するコードを以下に示します.

最初に文字列stringに1文字ずつ入力して文字列を作成し,printf関数で表示しています.

この時,文字配列の最後にNULL文字('\0')を代入することを忘れないようにして下さい.

次に,scanf関数とprintf関数で文字列の入出力を行っています.

実行結果は以下になります.

string.cの13~16行目で1文字ずつ配列に格納した場合と,scanf関数で"abc"と入力した場合(4行目の行末)の結果が,同じになっていることがわかります(3,5行目).

文字配列の定義時に初期化するコード

文字配列の定義時に初期化するコードは以下になります.

実行結果は以下になります.

注意点として,string2.cの14行目でstr4[4]に'e'が格納されていますが,str4[3]が'\0'なので文字列としては"abc"になります.

型修飾子

型修飾子は,変数の特別な性質を表すために付けます.

const

constを付けると,読み込み専用の変数を定義できます.

変数の初期化のみが許され,その後の代入は不可能になります.

constを付けると,読み込み専用メモリ(ROM等)に置いてよい変数を明示的に指定できます.

関数の引数に利用すると,その関数内での変更が不可能になり,引数が不用意に変更されることを防ぐことができます.

constを変数に付けた場合は以下になります.

どちらも初期値を代入した後に値を変更しようとするとコンパイル時に「error: assignment of read-only location」や「error: increment of read-only variable」とメッセージが表示されてコンパイルエラーになります.

ポインタ変数にconstを付ける場合,アスタリスクの前後でポインタにconstを付けると以下の意味になります.

  • アスタリスクの前にconst:ポインタが指す中身が読み込み専用であることを保証します.ただし,ポインタ変数の値自体は変更できることに注意して下さい.
  • アスタリスクの後にconst:ポインタが指す中身を書き込み可能になりますが,ポインタ変数の値自体は読み込み専用になります.
  • アスタリスクの前後にconst:ポインタが指す中身とポインタ変数の値が読み込み専用(定数)になります.

ポインタ変数にconstを付ける例を示します.

ポインタ演算子を知りたいあなたは,ポインタとはを読みましょう.

C言語 ポインタ
【C言語】ポインタとは

こういった悩みにお答えします. こういった私から学べます. 目次1 ポインタ2 ポインタ変数2.1 ポインタ演算子の使い方2.2 ポインタ変数を利用するコード3 ポインタと関数の引数:値渡しと参照渡し ...

続きを見る

volatile

volatileを付けると,コンパイラの最適化を抑制することができます.

例えば,a,bがint型の変数の場合,以下の演算をするとします.

しかし,コンパイラの最適化により,以下のコードに変換されてしまいます.

コンパイラの最適化を抑制したい場合,volatile int型で定義すれば,意味どおりにマシン語に変換されます.

このように,volatileばOSや組込み開発でCPUの制御レジスタをアドレスを利用して直接操作する場合に利用します.

他には,マルチスレッドプログラミングで,ある関数では値を変更しないけど他の関数でその値を変更する場合,その関数内では変更されない(定数である)とコンパイラが勘違いしてコードを削除してしまいます.

マルチスレッドプログラミングを利用するコードは以下になります.

thread_func関数ではグローバル変数のaの値を0に設定し,aが0の間はwhileループを回ります.

コンパイラの最適化オプションを付けない場合の実行結果は以下になります.正常に動作しました.

コンパイラの最適化オプション-O2を付けた場合の実行結果は以下になります.

この場合,thread_func関数ではaの値は0以外に変更されない(定数である)と勘違いして,コンパイラの最適化オプションを付けた場合は無限ループになってしまいました.

そこで,以下のようにグローバルのint型の変数aの定義でvolatileを付けてみましょう.

実行結果は以下になります.正常に動作しました.

コンパイラの最適化と戦うあなたへの記事もあわせて読むことをおすすめします.

C言語 コンパイラ最適化
【C言語】コンパイラの最適化と戦うあなたへ

こういった悩みにお答えします. こういった私から学べます. 目次1 C言語のコンパイラの最適化による不具合2 C言語からアセンブリ言語への変換箇所を特定する方法3 ループ処理のコード例4 コンパイラの ...

続きを見る

restrict

restrictは,C99から導入された型修飾子で,コンパイラに「aliasが存在しないと仮定した最適化を許す」ことを伝えます.

aliasが存在しないとは,メモリ領域が重複しない(別名がない)ことを意味します.

ここで,restrictはポインタ変数には利用できますが,通常の変数には利用できないことに注意して下さい.

restrictキーワードを理解するために,/usr/include/string.hにあるmemcpy関数memmove関数のプロトタイプ宣言を見てみましょう.

※__THROWや__nonnull ((1, 2))のような見なれない構文の解説はひとまず置いておきます.

ここで利用している__restrictはrestrictのことを表します.(__restrictはもしコンパイラのバージョンが古い場合は空定義になるマクロです.)

memcpy関数はメモリ領域をコピーしますが,コピー元の領域とコピー先の領域が重なってはならない制約があるので,restrictを付けることができます.

これに対して,memmove関数はmemmcpy関数と同様にメモリ領域をコピーしますが,コピー元とコピー先の領域が重なっていてもよいので,restrictを付けられません.

まとめ

C言語のデータ型を紹介しました.

具体的には,変数定義,変数名のルールと命名規則,定数,配列,文字配列,型修飾子を解説しました.

データ型はC言語の基本的な内容ですが結構奥が深いので,何度も読み込んで理解しましょう.

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

友だち追加

独学が難しいあなたは,C言語を学べるおすすめのオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!

-C LANGUAGE, TECHNOLOGY
-, , , , , , , , , ,