はじめてでないC

C言語ポインタと構造体を理解する

◆ はじめに

とりあえず、燈明日記での『C言語ネタ』をまとめてみました。

とりあえず、C言語で一番わかり難いと評判のポインタと構造体です。

他にもCSVデータの扱いや、printfの使いこなし、C言語での正規表現、popen等のネタはありますが、徐々にバージョンアップしていきたいと思います。

と云う事で、とりあえず公開します。なにかお気づきの点がありましたら、お知らせ下さい。

◆ インデックス

◆ コンテンツ

ポインタその1

突然ですが、C言語習得で一番つまずく、ポインタについて、思いつくまま解説をしていきます。

名付けて『はじめてでないC』です。まぁ、C言語をリベンジする人のためのC言語基礎講座です。

まず、ポインタとは、何かというと、『指すもの』です。 では、何を指すかというと、アドレスを指します。

つまり、C言語でのポインタとは、アドレスを指すものになります。 また、値が入るアドレスの場所を、プログラム的には、変数といいます。 特にポインタの値が入る変数をポインタ変数といいます。

サンプル1でそのイメージを示します。

・サンプル1

char abc[]="ABC"; /* 文字列変数    */
char *p;          /* ポインタ変数  */
char wk;          /* 変数(ワーク)*/

    p = &abc[0]; /* 文字列変数abcのアドレスをポインタ変数pへ格納 */

文字列変数abcのエリアのアドレスを、&演算子でポインタ変数pへ格納しています。 上記を実行すると、以下のメモリ上のイメージになります。

アドレス  メモリ
         +-----+
    100  |     |
         +-----+
    101  | A   |  ここが、文字列変数abcのエリア
         +-----+
    102  | B   |
         +-----+
    103  | C   |
         +-----+
    104  | \0  |
         +-----+
         |     |
         +-----+
    106  |101  | ここが、ポインタ変数pのエリア(実際は4バイト)
         +-----+

ここでBの値を得ることを考えてみましょう。

普通は、

 wk = abc[1];   /* (0) */

で、変数wkに値Bを得ることができます。 しかし、実は、これ以外に5通りの方法があるのです。

 wk = 1[abc];   /* (1) */
 wk = *(abc+1); /* (2) */
 wk = *(p+1);   /* (3) */
 wk = p[1];     /* (4) */
 wk = 1[p];     /* (5) */

(1)は、[ ]が、変数名を囲んでもでも、インデックス値を囲んでも、どちらでもかまわないのです。 これは、C言語では『[ ]』が演算子なので、演算子『+』場合のa+bとb+aが同じように、そうなるわけです。

(2)は、*演算子で修飾されたアドレスの内容値を得ることができます。 つまり、abc+1のアドレスにはBが入っていて、それを得ることができるわけです。

尚、*(abc+1)を*abc+1と書くと、全く別の計算になります。 *(abc+1)は、abcのアドレスに+1したアドレスの内容です。 一方、*abc+1は、abcのアドレスの内容に+1したものです。

(3)は、ポインタ変数pには、変数abcのアドレスが入っていますので、プラス1したアドレスには、 Bが入っていて、それを得ることがきるわけです。

(4)は、C言語では、ポインタを配列と同じように表現することが出来き、(0)と同じようになります。

(5)は、C言語では、ポインタを配列と同じように表現することが出来き、(1)と同じようになります。

ここで、ポインタと配列をまとめてみると

ポインタと配列は、データを得る時の表現は、全く同じですが、中身は、全く異なるものです。 中身は、配列には、値が入っていて、ポインタには、配列のアドレスが入っています。

そして、同じデータを得る時の表現が、ポインタと配列で6通りもあるのです。 どれを使うかは、プログラマのセンスによります。

最後に、C言語では、なんでポインタなんか必要かというと、私も良くわかりません。 ただ、C言語は関数の集まりで、その引数は、値渡しのみで、VBのように参照渡しがありません。 そこで、ポインタを使って、あたかも参照渡しもどきを実現するためなのかなとか。 あと、動的メモリを得た時のアドレスを、ポインタに設定ができることとかです。たぶん。

どうですか、ポインタの感じがつかめましたか?

当分、C言語です。よろしくです。

ポインタ その2

前回は、以下のように、値について文字列変数(普通の変数)とポインタ変数は、全く同様に表現ができることをご紹介致しまし た。

char abc[]="ABC"; /* 文字列変数宣言 */
char *p;          /* ポインタ変数宣言 */
char wk;

    p = &abc[0];

    /* 文字列変数からBをwkにセット */
    wk = abc[1];   
    wk = 1[abc]; 
    wk = *(abc+1);  
    /* ポインタ変数に従ってBをwkにセット */ 
    wk = p[1];
    wk = 1[p];
    wk = *(p+1);

実は、アドレスについても文字列変数とポインタ変数は全く同様に表現ができるのです。

char abc[]="ADC";/* 文字列変数宣言 */
char *p;         /* ポインタ変数宣言 */

     p = &abc[0];

    /* 文字列変数を表示 */
     printf("%s\n", abc);
     printf("%s\n", &abc[0]);
     printf("%s\n", abc+1);
     printf("%s\n", &abc[1]);

    /* ポインタ変数で指し示された文字列変数を表示 */
     printf("%s\n", p);
     printf("%s\n", &p[0]);
     printf("%s\n", p+1);
     printf("%s\n", &p[1]);

整理

まず、文字列変数abcとポインタ変数pが以下のようになっているとします。

アドレス  メモリ
         +-----+
    100  |     |
         +-----+
    101  | A   |  ここが、文字列変数abcのエリア
         +-----+
    102  | B   |
         +-----+
    103  | C   |
         +-----+
    104  | \0  |
         +-----+
         |     |
         +-----+
    106  |101  | ここが、ポインタ変数pのエリア(実際は4バイト)
         +-----+

値をもとめる表現方法は、文字列変数、ポインタ変数ともに同じ形式です。

例えば、Bの値を求めるには、おのおの3通りで、全部で6通りあります。

    abc[1]   
    1[abc] 
    *(abc+1) 
    
    p[1]
    1[p]
    *(p+1)

そして、アドレスをもとめる表現方法も、文字列変数とポインタ変数ともに同じ形式です。

例えば、Bのアドレスを求めるには、おのおの2通りで全部で4通りあります。

    abc+1
    &abc[1]

    p+1
    &p[1]

これまで、ご説明した通り、ポインタと配列は、全く別物にもかかわらず、値とアドレスの表現方法が、全く同じなのです。

つまり、ポインタをあたかも配列のように処理したり、逆もしかりなのです。

ですので、ポインタも配列も、 私は、ポインタのないコボルから入ったので、なるべく配列風に処理をしています。 逆にバリバリにポインタ風に処理する人もいます。

配列風の場合

値が、abc[1] で、アドレスが、&abc[1]で表現します。

値abc[1]のアドレスは、&(アンドれす)のabc[1]と覚え。

ポインタ風の場合

値が、*(abc+1)で、アドレスが、abc+1です。

アドレスabc+1の値は、*(アスターたい)の(abc+1)と覚える。

で、次回は、『関数引渡しについて』です。お楽しみに!

ポインタ その3

今回は、関数引数渡しでのポインタの役割を解説してみます。

普通、変数を関数の引数として引渡しをするには、2つの方法があります。

『値渡し(Call by value)』と『参照渡し(Call by reference)』です。

『値渡し』では、親関数から値渡しされた変数を子関数が変更しても、親関数の変数は変更されません。 つまり、親関数の変数には、影響が全くありません。

一方、『参照渡し』は、子関数で変数を変更した場合、親関数の変数も変更されます。 つまり、親関数の変数には、もろ影響するわけです。

そして、C言語では、言語仕様的に、『参照渡し』はありません。『値渡し』のみなのです。 また、C言語では、『値渡し』のみなのに、実は、配列や文字列の値を渡せません。 そう、C言語では、『参照渡し』でも『値渡し』でも配列や文字列の値を渡せないのです。

では、どうやって、渡すかというと、ここにポインタが登場するわけです。 つまり、ポインタを『値渡し』にして、あたかも、『参照渡し』の様に見せかけて、配列や文字列を渡すのです。

以下にそのサンプルを示します。

サンプル:ポインタの値渡し(ポインタ渡し)
#include <stdio.h>

main()
{
char abc[]="ABC";

     printf("%s\n", abc);
     sub(abc);      /* 文字列abcのアドレスを値渡ししている */
     printf("%s\n", abc);


}
sub(char *abc)      /* 文字列abcのアドレスをポインタとして値受け取り */
{
     abc[1] = 'X';  /* 引数の値を変更しているのではなく、*/
                    /* 引数の値が指し示すところを変更している */
                    /* 尚、*(abc+1) = 'X'としても同様 */
}

次回は、2次元配列の関数引渡しとポインタについてです。では、また。

ポインタ その4

今回『2次元配列の関数引渡しについて』を解説致します。

たとえば、

char abc[][4]={"ABC", "DEF", "GHI", "JKL"};

のような2次元配列(2次元の文字列)の変数を関数の引数にするには、以下のようになります。

main()
{
char abc[][4]={"ABC", "DEF", "GHI", "JKL"};

     printf("%s\n", &abc[2]);
     sub(abc);
     printf("%s\n", &abc[2]);
     sub2(&abc[0]);
     printf("%s\n", &abc[2]);
}
sub2(char abc[][4])
{
       abc[2][1] = 'Y';
}
sub(char (*abc)[4])
{
       abc[2][0] = 'X';
}

呼び出し側は、abcかまたは、&abc[0]で2次元配列のアドレスを設定します。 呼ばれる側は、char abc[][4]かまたは、char (*abc)[4]で受けます。

そして、char abc[][4]とchar (*abc)[4]は、何なのかというと、『char型で大きさ4の配列へのポインタ』になります。 これらの表現の仕方は、もう覚えるしかないです。 とにかく、右側から[4]の角括弧の中が配列の大きさで、char abc[]とchar (*abc)は、それへのchar型のポインタです。

どうですか? ポインタは理解できたでしょうか……。 で、そろそろポインタは、終わりに致します。

しかし、終わる前に、どんでん返しを2つ用意してみました。

第1のどんでん返しは、上記は、2次元配列といいましたが、実は、C言語には、1次元配列しかないのです。 上記の2次元配列は、厳密には、2次元配列もどきなのです。

普通、2次元配列というのは、エクセルのように行と列で表現するものです。 しかし、C言語では、あくまでも、ある大きさをもった要素の1次元配列で表現するのです。

イメージ的には以下の感じ。

普通の2次元配列のイメージ

  +-----+-----+
  | 1,1 | 1,2 |
  +-----------+
  | 2,1 | 2,2 |
  +-----+-----+
C言語での2次元配列のイメージ
  +-----+
  | 1,1 | 実は、2次元配列でなく、1,1と1,2の2つの要素をもった1元配列なのです。
  | 1,2 |
  +-----+
  | 2,1 |
  | 2,2 |
  +-----+

第2のどんでん返しは、前回、配列と文字列は『値渡し』が出来ないと申し上げました。 しかし、構造体でこれらをラッピングすれば、実は、『値渡し』が簡単に出来るのです。

・サンプル:文字列を構造体でつつんで値渡し
include <stdio.h>

struct abc_struct{
    char abc[2][4];
};

main()
{
struct abc_struct abc_st;

     strcpy(&abc_st.abc[0], "ABC");
     strcpy(&abc_st.abc[1], "XYZ");

     sub(abc_st); /* 値渡し */

     /* subで値をかえてもmainでは、影響を受けない */
     printf("acb_st.abc[0]=%s\n", &abc_st.abc[0]);
     printf("acb_st.abc[1]=%s\n", &abc_st.abc[1]);
}
sub(struct abc_struct hiki_abc_st)
{
    printf("hiki_acb_st.abc[0]=%s\n", &hiki_abc_st.abc[0]);
    printf("hiki_acb_st.abc[1]=%s\n", &hiki_abc_st.abc[1]);
    
    /* 値をかえてみる */
    strcpy(&hiki_abc_st.abc[0], "123");
    strcpy(&hiki_abc_st.abc[1], "987");

    printf("hiki_acb_st.abc[0]=%s\n", &hiki_abc_st.abc[0]);
    printf("hiki_acb_st.abc[1]=%s\n", &hiki_abc_st.abc[1]);
}

ちなみに、上記をポインタでの値渡し(ポインタ渡し)にすると以下の様になります。

・サンプル:構造体のポインタ渡し
#include <stdio.h>

struct abc_struct{
    char abc[2][4];
};

main()
{
struct abc_struct abc_st;

     strcpy(&abc_st.abc[0], "ABC");
     strcpy(&abc_st.abc[1], "XYZ");

     sub(&abc_st);/* &をつけてアドレスを渡す */

     /* subで値をかえるとこちらも変わる */
     printf("acb_st.abc[0]=%s\n", &abc_st.abc[0]);
     printf("acb_st.abc[1]=%s\n", &abc_st.abc[1]);
}
sub(struct abc_struct *hiki_abc_st) /* 構造体のポインタで受ける */
{
    printf("hiki_acb_st->abc[0]=%s\n", &hiki_abc_st->abc[0]);
    printf("hiki_acb_st->abc[1]=%s\n", &hiki_abc_st->abc[1]);

    /* 値をかえてみる */
    strcpy(&hiki_abc_st->abc[0], "123");
    strcpy(&hiki_abc_st->abc[1], "987");

    printf("hiki_acb_st->abc[0]=%s\n", &hiki_abc_st->abc[0]);
    printf("hiki_acb_st->abc[1]=%s\n", &hiki_abc_st->abc[1]);
}

尚、構造体の『.』と『->』は、メンバ参照演算子といって、 『.』は、構造体変数に、『->』は、構造体ポインタ変数に使用します。

ポインタ その5

前回をポインタ最終回としましたが、まだ、語り残したことが2つありました。

それは、ポインタ配列多重ポインタです。それでは、参ります。

ポインタ配列とは、まぁポインタの配列です。文字列の2次元配列とすごく似ているのですが、全く別物です。

以下のサンプルで確認してください。

#include <stdio.h>

main()
{
char abc[][4]={"ABC", "DEF"}; /* char型で大きさ4の文字列の2次元配列 */
char *xyz[2]={"ABC", "DEF"};  /* char型で文字列のポインタの配列       */

     sub(abc);     /* 文字列(配列)のアドレスを引き渡す */
     printf("%s\n", &abc[1]);
     sub2(xyz);    /* ポインタ配列のアドレスを引き渡す */
     printf("%s\n", xyz[1]);
}
sub(char (*p_abc)[4]) /* char型で大きさ4の配列へのポインタで受ける */
{
       p_abc[1][1] = 'X';     /* abc の DEF を DXF に */
}
sub2(char **pp_xyz)  /* ポインタのポインタで受ける */
{
       *(pp_xyz[1]+1) = 'Y';  /* xyz の DEF を DYF に */
}

多重ポインタとは、関数:sub2のように、文字列のアドレスが入っているポインタ配列のアドレスを受ける場合のポインタをいいます。 別の言い方だと、まず、値(文字列)があって、それを指すポインタがあって、また、そのポインタを指すポインタ(ポインタ配列)のアドレスをsub2関数の引数に渡しています。このような時の引数は、ポインタのポインタで受けます。

文字列2次元配列とポインタ配列のイメージを以下に示します。

・文字列2次元配列のメモリイメージ
アドレス  メモリ
         +-----+
    100  |     |
         +-----+
    101  | A   |  ここが、文字列変数abcのエリア
         +-----+
    102  | B   |
         +-----+
    103  | C   |
         +-----+
    104  | \0  |
         +-----+
    105  | D   |
         +-----+
    106  | E   |
         +-----+
    107  | F   |
         +-----+
    108  | \0  |
         +-----+
         |     |
         +-----+
    110  |101  | ここが、ポインタ変数p_abcのエリア(実際は4バイト)
         +-----+ 101番地の文字列変数abcのエリアを指す。
・ポインタ配列のメモリイメージ
アドレス  メモリ
         +-----+
    100  |     |
         +-----+
    101  | A   | ここが、文字列ABCのエリア
         +-----+
    102  | B   |
         +-----+
    103  | C   |
         +-----+
    104  | \0  |
         +-----+
         |     |
         +-----+
    106  | D   |ここが、文字列DEFのエリア
         +-----+
    107  | E   |
         +-----+
    108  | F   |
         +-----+
    109  | \0  |
         +-----+
         |     |
         +-----+
    111  |101  |ここが、各文字列のアドレスが入るポインタ配列:xyzのエリア。
         +-----+
    112  |106  |
         +-----+
         |     |
         +-----+
    114  |111  |ここが、ポインタ配列エリア:xyzのアドレスが入るポインタ:pp_xyzのエリア。
         +-----+

どうですか、わかりましたか?

とにかく、普通に宣言すると、普通の値を表現する変数になり、 『*』付きで宣言すると、ポインタ(アドレス)であることを表現する変数になり、 『**』付きで宣言すると、ポインタが入っているポインタであることを表現する変数になります。

構造体 その1

今回から、構造体について、解説いたします。

C言語の基本的なデータ型は、intやchar,float等ですが、それらを任意に組み合わせて、 一つのデータ型にすることが出来ます。そのようなデータ型を構造体といいます。

以下の例は、charのみですが、5つのchar型の文字列を組み合わせて、 任意のdb_loginというデータ型を作成しています。

つぎに、db_login型の変数shisha_svを宣言しています。(構造体なのでキーワードstructが必要)

そして、7支社分必要なので、構造体を配列にしてそれを表現します。

サンプル
#include <stdio.h>

#define SHISHA_CNT 7

struct db_login {
       char svnm[7];
       char svnm2[9];
       char loginid[31];
       char password[31];
       char db_string[45];
};

struct db_login shisha_sv[SHISHA_CNT]={
       "svr001","A支社","id","passwd","svr001s",
       "svr002","B支社","id","passwd","svr002s",
       "svr003","C支社","id","passwd","svr003s",
       "svr004","D支社","id","passwd","svr004s",
       "svr005","E支社","id","passwd","svr005s",
       "svr006","F支社","id","passwd","svr006s",
       "svr007","G支社","id","passwd","svr007s",
};

int main()
{
int  i;

    for(i=0; i<SHISHA_CNT; i++){
        printf("-----------------------\n");
        printf("hostname=%s\n", &shisha_sv[i].svnm[0]);
        printf("hostname2=%s\n", &shisha_sv[i].svnm2[0]);
        printf("loginid=%s\n", &shisha_sv[i].loginid[0]);
        printf("password=%s\n", &shisha_sv[i].password[0]);
        printf("db_string=%s\n", &shisha_sv[i].db_string[0]);
    }
}

構造体 その2

前回は、構造体を勉強致しました。今回は、ついでに構造体のポインタについても勉強をしてしまいましょう。

前回のプログラムを、構造体のポインタを使ったものに作り変えてみました。

関数の呼出し側;sub(&shisha_sv[0]);は、構造体の先頭アドレスを構造体ポインタで引数渡しにしています。 尚、『&』は、アドレス取得演算子と云い、今回、shisha_svの先頭アドレスを取得しています。

関数の呼ばれ側:sub(struct db_login *p_shisha_sv)は、構造体のポインタが引数であることを 『struct db_login *』 で宣言(明示)しています。

尚、『for(;p_shisha_sv->svnm[0]; p_shisha_sv++)』は、 初期値なしのfor文で、『->』は、ポインタのメンバーへの参照演算子です。 『p_shisha_sv++』は、今回の場合、forループで構造体のサイズ分のポインタがずれることを意味しています。

以下のロジックを昨日ロジックと見比べて見てください。

サンプル
#include <stdio.h>

#define SHISHA_CNT 7

struct db_login {
       char svnm[7];
       char svnm2[9];
       char loginid[31];
       char password[31];
       char db_string[45];
};

int main()
{
struct db_login shisha_sv[SHISHA_CNT+1]={
       "svr001","A支社","id","passwd","svr001s",
       "svr002","B支社","id","passwd","svr002s",
       "svr003","C支社","id","passwd","svr003s",
       "svr004","D支社","id","passwd","svr004s",
       "svr005","E支社","id","passwd","svr005s",
       "svr006","F支社","id","passwd","svr006s",
       "svr007","G支社","id","passwd","svr007s",
       "\0"
};

    sub(&shisha_sv[0]);
}
int sub(struct db_login *p_shisha_sv)
{

    for(;p_shisha_sv->svnm[0]; p_shisha_sv++){
        printf("-----------------------\n");
        printf("hostname=%s\n", &p_shisha_sv->svnm[0]);
        printf("hostname2=%s\n", &p_shisha_sv->svnm2[0]);
        printf("loginid=%s\n", &p_shisha_sv->loginid[0]);
        printf("password=%s\n", &p_shisha_sv->password[0]);
        printf("db_string=%s\n", &p_shisha_sv->db_string[0]);
    }
}

構造体 その3

前回、前々回と構造体をお勉強しまたので、引きつづき構造体繋がりです。 まず、おさらいです。構造体は型宣言をしてから、実体宣言をいたします。以下の感じです。

struct db_login {           /* 型宣言 */
       char svnm[7];
       char svnm2[13];
};

struct db_login shisha_sv;  /* 実体宣言 */

しかし、実体宣言をする前に、『struct db_login』でなく、構造体をあたかも新しい一つの型として、別名で定義することが出来ます。これを、タイプデフといいます。以下の感じです。

struct db_login {
       char svnm[7];
       char svnm2[13];
};

typedef struct db_login DB_LOGIN; /* 構造体を別名で定義 */
DB_LOGIN shisha_sv;    /* 実体宣言 */

typedefで、struct db_loginを、DB_LOGINとして別名を定義しています。 つまり、タイプデフすれば、基本データ型と同じような扱いができるわけです。

ちなみに、typedefと同じようなものに、『#define』があります。 『#define』でも、同じことが可能です。

#define DB_LOGIN struct db_login
DB_LOGIN {
       char svnm[7];
       char svnm2[13];
};
DB_LOGIN shisha_sv;

両者の違いは、#defineは、コンパイル前に文字列置換され、『struct db_login』として処理され、 typedefは、基本データ型と同じような扱いで、コンパイルされます。

以下に、前回のサインプルをtypedefバージョンと#defineバージョンで示します。

・typedefバージョン
#include <stdio.h>

#define SHISHA_CNT 7

struct db_login {
       char svnm[7];
       char svnm2[9];
       char loginid[31];
       char password[31];
       char db_string[45];
};
typedef struct db_login DB_LOGIN;


int main()
{
DB_LOGIN shisha_sv[SHISHA_CNT+1]={
       "svr001","A支社","id","passwd","svr001s",
       "svr002","B支社","id","passwd","svr002s",
       "svr003","C支社","id","passwd","svr003s",
       "svr004","D支社","id","passwd","svr004s",
       "svr005","E支社","id","passwd","svr005s",
       "svr006","F支社","id","passwd","svr006s",
       "svr007","G支社","id","passwd","svr007s",
       "\0"
};

    sub(&shisha_sv[0]);
}

int sub(DB_LOGIN *p_shisha_sv)
{

    for(;p_shisha_sv->svnm[0]; p_shisha_sv++){
        printf("-----------------------\n");
        printf("hostname=%s\n", &p_shisha_sv->svnm[0]);
        printf("hostname2=%s\n", &p_shisha_sv->svnm2[0]);
        printf("loginid=%s\n", &p_shisha_sv->loginid[0]);
        printf("password=%s\n", &p_shisha_sv->password[0]);
        printf("db_string=%s\n", &p_shisha_sv->db_string[0]);
    }
}
・#defineバージョン
#include <stdio.h>

#define SHISHA_CNT 7
#define DB_LOGIN struct db_login

struct db_login {
       char svnm[7];
       char svnm2[9];
       char loginid[31];
       char password[31];
       char db_string[45];
};


int main()
{
DB_LOGIN shisha_sv[SHISHA_CNT+1]={
       "svr001","A支社","id","passwd","svr001s",
       "svr002","B支社","id","passwd","svr002s",
       "svr003","C支社","id","passwd","svr003s",
       "svr004","D支社","id","passwd","svr004s",
       "svr005","E支社","id","passwd","svr005s",
       "svr006","F支社","id","passwd","svr006s",
       "svr007","G支社","id","passwd","svr007s",
       "\0"
};

    sub(&shisha_sv[0]);
}
int sub(DB_LOGIN *p_shisha_sv)
{

    for(;p_shisha_sv->svnm[0]; p_shisha_sv++){
        printf("-----------------------\n");
        printf("hostname=%s\n", &p_shisha_sv->svnm[0]);
        printf("hostname2=%s\n", &p_shisha_sv->svnm2[0]);
        printf("loginid=%s\n", &p_shisha_sv->loginid[0]);
        printf("password=%s\n", &p_shisha_sv->password[0]);
        printf("db_string=%s\n", &p_shisha_sv->db_string[0]);
    }
}

しかし、実際は、#defineバージョンは、使わないです、あくまでも参考に……。

構造体 その4

今回で構造体は最終回にしたいと思います。

構造体を使用する時の基本は、型宣言をしてから、実体宣言をいたします。以下の感じです。

struct db_login {           /* 型宣言 */
       char svnm[7];
       char svnm2[13];
};
struct db_login shisha_sv;  /* 実体宣言 */

しかし、2つも宣言をするのは、面倒だと思ったあなた、そう、実は、1つで型宣言と実体宣言が一緒に出来ます。

struct db_login {
       char svnm[7];
       char svnm2[13];
} shisha_sv;  

使い分けは、同じ構造体を沢山使う時は、型宣言を一つしておいて、実体宣言を沢山する。 一つの時は、型宣言と実体宣言の一体型を使う。

サンプル:型宣言と実体宣言の構造体一体型バージョン
#include <stdio.h>

#define SHISHA_CNT 7

struct db_login {
       char svnm[7];
       char svnm2[9];
       char loginid[31];
       char password[31];
       char db_string[45];
} shisha_sv[SHISHA_CNT]={
       "svr001","A支社","id","passwd","svr001s",
       "svr002","B支社","id","passwd","svr002s",
       "svr003","C支社","id","passwd","svr003s",
       "svr004","D支社","id","passwd","svr004s",
       "svr005","E支社","id","passwd","svr005s",
       "svr006","F支社","id","passwd","svr006s",
       "svr007","G支社","id","passwd","svr007s",
};

int main()
{
int  i;

    for(i=0; i<SHISHA_CNT; i++){
        printf("-----------------------\n");
        printf("hostname=%s\n", &shisha_sv[i].svnm[0]);
        printf("hostname2=%s\n", &shisha_sv[i].svnm2[0]);
        printf("loginid=%s\n", &shisha_sv[i].loginid[0]);
        printf("password=%s\n", &shisha_sv[i].password[0]);
        printf("db_string=%s\n", &shisha_sv[i].db_string[0]);
    }
}

また、typedefを使用する時の基本は、型宣言をしてから、typedef宣言をいたします。以下の感じです。

struct db_login {
       char svnm[7];
       char svnm2[13];
       char loginid[31];
       char password[31];
       char db_string[45];
};
typedef struct ora_login ORA_LOGIN;

しかし、2つも宣言するのは、面倒だと思ったあなた、そう、実はこちらも、1つで型宣言とtypedef宣言が一緒に出来ます。

typedef struct db_login {
       char svnm[7];
       char svnm2[13];
       char loginid[31];
       char password[31];
       char db_string[45];
} DB_LOGIN;

使い分けは、個人の趣味の範囲になるかと思います。たぶん。

サンプル:typedefの構造体一体型バージョン
#include <stdio.h>

#define SHISHA_CNT 7

typedef struct db_login {
       char svnm[7];
       char svnm2[9];
       char loginid[31];
       char password[31];
       char db_string[45];
} DB_LOGIN;


int main()
{
DB_LOGIN shisha_sv[SHISHA_CNT+1]={
       "svr001","A支社","id","passwd","svr001s",
       "svr002","B支社","id","passwd","svr002s",
       "svr003","C支社","id","passwd","svr003s",
       "svr004","D支社","id","passwd","svr004s",
       "svr005","E支社","id","passwd","svr005s",
       "svr006","F支社","id","passwd","svr006s",
       "svr007","G支社","id","passwd","svr007s",
       "\0"
};

    sub(&shisha_sv[0]);
}
int sub(DB_LOGIN *p_shisha_sv)
{

    for(;p_shisha_sv->svnm[0]; p_shisha_sv++){
        printf("-----------------------\n");
        printf("hostname=%s\n", &p_shisha_sv->svnm[0]);
        printf("hostname2=%s\n", &p_shisha_sv->svnm2[0]);
        printf("loginid=%s\n", &p_shisha_sv->loginid[0]);
        printf("password=%s\n", &p_shisha_sv->password[0]);
        printf("db_string=%s\n", &p_shisha_sv->db_string[0]);
    }
}

人のよいところをどんどん見つけよう