最終更新日 2004/07/08


C言語
▼変数の長さはいくつまで?
▼三連文字を使ってみる
▼"??/"をprintf()で出力する
▼構造体のサイズを調べてみる
▼異なる型の値を返す関数
▼関数のポインタを利用する
▼可変引数関数を作ってみる
▼オフセットマクロを使ってみる
▼#includeの機能



変数の長さはいくつまで?

    #include <stdio.h>
    
    void main()
    {
        int abcabcabc ・・256文字以上・・abc = 1;
    
        printf("abc = %ld\n", abcabcabc ・・256文字以上・・abc);
    }
    
●コンパイル時のメッセージ
warning C4786: 'abcabcabc ・・256文字以上・・abc'
: デバッグ情報で識別子が 255 文字に切り捨てられました。

●実行結果
abc = 1;


256文字以上の変数名を使用すると警告文が出るが、プログラム動作には
影響はないです。
しかし、デバッグ中に変数を参照できなくなります。
昔、(16ビットの時代)は最大31文字だったみたいです。



▲このページのトップへ戻る

三連文字を使ってみる
    ??=include <stdio.h>
    
    void main()
    ??<
        char buff??(10??) = "abcde";
        int a = 0;
        unsigned int b = 0xfedcba98;
    
        a ??!= 1;
        a ??'= 1;
        b = ??-b;

        printf("buff = %s??/n", buff);
        printf("a = %ld??/n", a);
        printf("b = %x??/n", b);
    ??>
    
なんか文字化けしたように見えますが、ちゃんとコンパイル通りますし、
実行もできます。

上のソースは、
    #include <stdio.h>
    
    void main()
    {
        char buff[10] = "abcde";
        int a = 0;
        unsigned int b = 0xfedcba98;
    
        a |= 1;
        a ^= 1;
        b = ~b;
    
        printf("buff = %s\n", buff);
        printf("a = %ld\n", a);
        printf("b = %x\n", b);
    }
    
と同じ意味になります。



▲このページのトップへ戻る

"??/"をprintf()で出力する
"??/"は"\"と三連文字で定義されているので、実際に"??/"を出力するときは、
ちょっとコツがいります。
(printf("??/");はコンパイルエラーになります。)

出力するには、次のようにします。
    #include <stdio.h>
    
    void main()
    {
        char buff[10];
    
        //一つ一つセットした後出力
        buff[0] = '?';
        buff[1] = '?';
        buff[2] = '/';
        buff[3] = '\0';
    
        printf("%s\n", buff);
    
        //ASCIIコードでセットした後出力
        buff[0] = 0x3f;
        buff[1] = 0x3f;
        buff[2] = 0x2f;
        buff[3] = '\0';
    
        printf("%s\n", buff);
    
        //ヘルプに載ってるやりかた
        printf("\?\?/\n");
    
        //一文字ずつ出力
        printf("?");
        printf("?");
        printf("/\n");
    
        //文字列を連結させるやりかた
        printf("?""?""/\n");
    }
    
実行結果:
??/
??/
??/
??/
??/



▲このページのトップへ戻る

構造体のサイズを調べてみる
構造体はメモリ格納時に、すばやくアクセスできるよう最適化されています。
よって構造体のサイズにはちょっと気をつける必要があります。

    #include <stdio.h>
    
    //n にパックサイズを入力する
    //#pragma pack(n)
    
    struct TEST
    {
        char a[3];
        short b;
        int c;
        double d;
    };
    
    void main()
    {
        printf("構造体サイズ = %ld\n", sizeof(TEST));
    }
    
    
    結果:
    n = 1のとき
      構造体サイズ = 17
    n = 2のとき
      構造体サイズ = 18
    n = 4のとき
      構造体サイズ = 20
    n = 8のとき
      構造体サイズ = 24
    n = 16のとき
      構造体サイズ = 24
    

構造体がどのように最適化されるかは、
#pragma pack(n)
で指定できます。
デフォルトは、n = 8 です。

実際のメモリには、
n = 1のとき
aaabbccccdddddddd
n = 2のとき
aaa0bbccccdddddddd
n = 4のとき
aaa0bb00ccccdddddddd
n = 8のとき
aaa0bb00cccc0000dddddddd
n = 16のとき
aaa0bb00cccc0000dddddddd

のように格納されます。
0 は、最適化のために埋められた領域です。

構造体のサイズが必要なときは、構造体の中身を直接計算したものではなく、
sizeof(TEST)
のように、どのように最適化されてもいいような使い方をしたほうが
いいと思います。



▲このページのトップへ戻る

異なる型の値を返す関数
ひとつの関数に複数の型の戻り値を持たせるサンプルです。
サンプルは、CalcAdd()という足し算関数です。
第三引数に0を指定すれば整数、1を指定すれば文字列が戻り値となります。
戻り値はvoid*となっており、キャストして値を取得します。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void *CalcAdd(int iData1, int iData2, int iWhich);
    void DeleteCalcData(void *pData);
    
    void main()
    {
        int a = 10;
        int b = 100;
        int *piData = NULL;
        char *psData = NULL;
    
        //足し算の結果を整数値で取得
        piData = (int*)CalcAdd(a, b, 0);
    
        //足し算の結果を文字列で取得
        psData = (char*)CalcAdd(a, b, 1);
    
        //結果を出力
        printf("int型で結果を取得\n"); 
        printf("%ld + %ld = %ld\n", a, b, *piData);
    
        printf("\nchar型で結果を取得\n"); 
        printf("%ld + %ld = %s\n", a, b, psData);
    
        //メモリ解放
        if(piData != NULL){
            DeleteCalcData(piData);
        }
    
        if(psData != NULL){
            DeleteCalcData(psData);
        }
    }
    
    //
    //足し算処理
    //
    //引数:int iData1:足される数
    //    :int iData1:足す数
    //    :int iWhich:結果を整数値と文字列のどちらにするかのフラグ
    //                  0:整数値、1:文字列
    //
    //戻り値:計算結果(int型 or char型)のポインタ
    //
    void *CalcAdd(int iData1, int iData2, int iWhich)
    {
        int iResult = iData1 + iData2;
        int *piData;
        char sData[64];
        char *psData;
    
        if(iWhich == 0){
            piData = (int*)malloc(sizeof(int));
            *piData = iResult;
    
            return piData;
        }
        else if(iWhich == 1){
            itoa(iResult, sData, 10);
            psData = (char*)malloc(strlen(sData) + 1);
            strcpy(psData, sData);
    
            return psData;
        }
    
        return NULL;
    }
    
    //
    //CalcAdd()関数内部で生成されたデータ領域を解放する
    //
    //引数:void *pData:解放するデータ領域へのポインタ
    //
    void DeleteCalcData(void *pData)
    {
        free(pData);
    }
    
結果:

int型で結果を取得
10 + 100 = 110

char型で結果を取得
10 + 100 = 110


でもこのやり方はバグが入りやすいので、お勧めはできません。



▲このページのトップへ戻る

関数のポインタを利用する
関数のポインタを使って処理を実行します。
DLLを明示的にロードするときなんかに使います。

    #include <stdio.h>
    
    //足し算関数
    int CalcAdd(int a, int b){ return a + b; };
    
    //CalcAdd関数へのポインタ
    int (*pCalcAdd)(int, int) = CalcAdd;
    
    //CalcAdd関数へのポインタのポインタ
    int (*ppCalcAdd)(int, int) = pCalcAdd;
    
    //printf関数のポインタ(標準関数でもOK)
    int (*pPrintf)(const char*, ...) = printf;
    
    void main()
    {
        int a = 10;
        int b = 100;
        int result1;
        int result2;
    
        //CalcAdd関数へのポインタを使って計算
        result1 = (*pCalcAdd)(a, b);
    
        //CalcAdd関数へのポインタのポインタを使って計算
        result2 = (**ppCalcAdd)(a, b);
    
        //printf関数のポインタを使って出力
        (*pPrintf)("%ld + %ld = %ld\n", a, b, result1);
        (*pPrintf)("%ld + %ld = %ld\n", a, b, result2);
    }
    
結果:

10 + 100 = 110
10 + 100 = 110

でも、あんまり使う時ってないですね。



▲このページのトップへ戻る

可変引数関数を作ってみる
printf関数の様に可変数の引数が可能な関数を作ってみます。

    #include <stdio.h>
    #include <stdarg.h>
    
    int CalcAdd(int first, ...)
    {
        int sum = 0;
        int next = 0;
        va_list marker;
    
        va_start(marker, first);          //可変個の引数の初期化
    
        sum += first;
    
        //va_arg()は、リスト内の次の引数を指すように
        //markerをインクリメントし、その位置の値を返す
        //0となったらループを抜ける
        while((next = va_arg(marker, int)) != 0){
            sum += next;
        }
    
        va_end(marker);                   //可変個の引数のリセット
    
        return sum;
    }
    
    void main()
    {
        //加算結果を出力する(最後に0を付ける)
        printf("加算結果 = %ld\n", CalcAdd(1, 2, 3, 4, 5, 0));
        printf("加算結果 = %ld\n", CalcAdd(1, -2, 3, -4, 5, 0));
    
        //↓途中に0が入るとだめ
        printf("加算結果 = %ld\n", CalcAdd(1, 2, 0, 4, 5, 0));
        //↓intの最大値を越えるとだめ
        printf("加算結果 = %ld\n", CalcAdd(2147483647, 1, 0));
    }
    
結果:

加算結果 = 15
加算結果 = 3
加算結果 = 3
加算結果 = -2147483648

これを使うとコンパイラによる型チェックができないので
なるべく使わないほうがいいでしょう。



▲このページのトップへ戻る

オフセットマクロを使ってみる
オフセットマクロを使って構造体に値をセットします。

    #include <stdio.h>
    #include <stddef.h>
    
    struct OffsetTest
    {
        int value1;
        short value2;
        char value3[8];
        int value4;           //この値を設定します
    };
    
    void main()
    {
        struct OffsetTest tOffset;
    
        tOffset.value4 = 0;
    
        //offsetof マクロを使い value4 に値を代入する
        *(int *)((char *)&tOffset
            + offsetof(struct OffsetTest, value4)) = 100;
    
        printf("value4 = %ld\n", tOffset.value4);
    }
    
結果:

value4 = 100

オフセット値(構造体の先頭ポインタから離れているバイト数)を使い
値をセットします。
オフセットは、Windows APIでたまに使われているのを見かけますね。



▲このページのトップへ戻る

#includeの機能
#includeは、”指定されたファイルの内容をその位置に展開する”
という機能です。
なので下記のようなこともできちゃいます。

「test.txt」の内容。

    #include <stdio.h>
    
    void main()
    {
        printf("Hello! World\n");
    }
    

実際のプログラム「test.c」の内容。

    
    #include "test.txt"
    
    

結果:

Hello! World

変な感じですけどね。





■トップへ戻る
▲このページのトップへ戻る