C言語編 目次

 関数設計

 

・関数名の命名規則
・プログラミングに出る!英単語

 ポインタ

 

・データ型とポインタ

 データ型

 

・char *とconst char *は違う
・符号付きと符号なし

 演算子

 

・三項演算子とデータ型の問題

 制御構文

 

・条件式で代入する
・三項演算子を使ったswitch

 構造体

 

・構造体のサイズとアライメント
・構造体メンバのサイズを知る

 配列

 

・配列使用時の注意
・配列の要素数を知る

 メモリ管理

 

・メモリスタック
・動的メモリ確保とメモリリーク

 モジュール設計

 

・モジュール分割
・汎用モジュールとアプリ依存モジュール

 パフォーマンス
  徹底チューニング

 


・どんな処理に時間がかかるのか
・ファイル入出力の効率化
・アルゴリズムを考える1
・アルゴリズムを考える2

 プリプロセッサの便利機能


・2重インクルード防止

 


トップページへ戻る

データ型とポインタ

 今回はCの目玉?ともいうべきポインタについてです。いまさらポインタかよ!と思わないでください。今回のトピックは結構重要ですよ。
 では、ポインタをおさらいしておきましょう。

int main(int argc, char *argv[])
{
    int    i = 3;
    int    *pi = NULL;
    char   str[32] = "sample";
    char   *pstr = NULL;

    pi = &i;
    pstr = str;

    printf("i = %d\n", *pi);
    printf("str = %s\n", pstr);

    return 0;
}

 piはint型のポインタ、pstrはchar型のポインタです。ポインタも一つの変数です。中身には通常、メモリアドレスが入ります。

 メモリアドレスが入りますので、必然的にポインタ変数のサイズは4バイトになります。int型のポインタでも、char型のポインタでも、構造体のポインタでも、全部サイズは4バイトです。32bitのOSであればメモリ空間は4バイトですからね。(これが64bitのOSになると8バイトになるのでしょうか。それはここでは取り上げません。)

 ここまではどんなCの参考書にも書いてあります。でもここからはもっと掘り下げてみます。

 まず、ポインタはちゃんとした変数です。変数ということは「データ型」を持っています。変数という視点で見れば、intもint型のポインタも同じ扱いで扱わなければいけませんよね。

 int型のポインタ変数のデータ型は「int *」です。上のプログラムで言えば、「int *」というデータ型の変数「pi」なんですね。同じように「char *」というデータ型の変数「pstr」ということになります。これは「int」というデータ型の変数「i」と言っているのと同じ扱いをしているわけです。

 「何を当たり前のことを…」と思っているかもしれませんが、結構見落としがちなんですよ。ここは。

 ではそれを踏まえて、一つ大事なことを書きましょう。それは「違うデータ型のポインタ変数には互換性がない」ということです。要するに代入できないということです。次のプログラムを見てください。

int main(int argc, char *argv[])
{
    int             *pi = NULL;
    long            *pl = NULL;
    unsigned long   *pul = NULL;
    short           *ps = NULL;
    char            *pstr = NULL;
    
    pl   = pi;      // コンパイルエラー
    pul  = pl;      // コンパイルエラー
    ps   = pul;     // コンパイルエラー
    pstr = ps;      // コンパイルエラー
    pi   = pstr;    // コンパイルエラー
    
    return 0;
}

 データ型の違うポインタは互換性がありませんので、上の例ではすべてコンパイルエラーになります。「サイズは全部4バイトで、メモリアドレスが入っているんだから代入してくれてもよさそうなのに」と思うかもしれませんが、サイズが同じであっても、「データ型」が違うので代入できないんです。int *とlong *でさえ互換性がないんです。

 もちろん無理矢理キャストすれば代入できます。それはポインタ変数でない普通の変数と全く一緒です。普通の変数でも互換性がなければ代入できませんよね。

 ポインタ変数もデータ型をもったちゃんとした変数だということが、少しはおわかりいただけたでしょうか?Cの入門書のデータ型の紹介のところに、char、int、float...と並んでいるところに、char *、int *、float *...というのもちゃんと並べて書いておいてほしいものですね。

 それにしても、一見ささいな事のように思えますが、なぜこれが重要なのでしょうか。それはC++を使い始めたときに初めてわかります。C++では「データ型」を非常に気にします。ポインタ型のデータ型もCよりさらに高度な使い方をします。「データ型」が1つのテーマだと言ってもいいでしょうね。クラスもデータ型ですので…。

 C++を知っている人にとっては、今回のトピックは「ニヤリ」というところでしょうか。この辺がCとC++の間にあるギャップ(C+?)の部分なんですよね。