パソコン活用研究シリコンバレー(C、C++、の活用研究)



構造体(ポインタ変数)のコンパイラ間の差
―更新準備中―

構造体のポインタ変数の扱いについては、コンパイラによって結構差があるところなので、
あのコンパイラでは通ったソースコードが別のコンパイラではエラーが出るというようなことが
あります。

今回、コンパイラ間の差を試してみました。

各プログラムの説明は「構造体(ポインタ変数)」をご参照下さい。
このページは各コンパイラ間の差についてのみ説明しています。

 T 構造体のポインタ変数とメンバーの参照


構造体のポインタ変数への値の入力と参照のプログラムです。
構造体の各メンバー、name,adress,telに値の入力をした後に、3パターンの方法で各メンバーの
値を参照して表示しています。

structa.c for Borland C++用
(LSIC-86でコンパイルもOK)

#include <stdio.h>


struct personal {
char name[20];
char adress[40];
char tel[15];
};

int main(void){
struct personal *ps, ps_data[2];   /* (1) */
int i;

/* 下の ps=ps_data; がないと、コンパイル時に「PSの値が未定義」というエラーになる*/  
ps=ps_data;    /* (2) */
for(i=0;i<2;i++,ps++){      /* (3) */       
printf("name?  -"); gets(ps->name);
printf("adress?  -"); gets(ps->adress);
printf("tel?  -"); gets(ps->tel); printf("\n");
}

ps=ps_data;    /* (4) */
for(i=0;i<2;i++){
printf("%s : %s : %s\n",(*(ps+i)).name,(*(ps+i)).adress,(*(ps+i)).tel);}

ps=ps_data;
for(i=0;i<2;i++){
printf("%s : %s : %s\n",(ps+i)->name,(ps+i)->adress,(ps+i)->tel);}

ps=ps_data;
for(i=0;i<2;i++,ps++){
printf("%s : %s : %s\n",ps->name,ps->adress,ps->tel);}
return 0; }

実行例
C:\C>structa
name? -田中
adress? -東京都世田谷区
tel? -999-666-333

name? -Ron Bon
adress? -NY, US
tel? -777-999-999

田中 : 東京都世田谷区 : 999-666-333    
Ron Bon : NY, US : 777-999-999
田中 : 東京都世田谷区 : 999-666-333
Ron Bon : NY, US : 777-999-999
田中 : 東京都世田谷区 : 999-666-333
Ron Bon : NY, US : 777-999-999

C:\C>

personal構造体の各メンバ変数を配列からポインタ変数に変更してみた。
LSIC-86でコンパイルしたものは動作するが、
Borand C++, Visual C++ でこのソースコードをコンパイルしたプログラムはデータの入力の途中で動作を停止し
プログラムが終了してしまう。

#include <stdio.h>


struct personal {
char *name;
char *adress;
char *tel;
};

int main(void){
struct personal *ps, ps_data[2];
int i;

/* 下の ps=ps_data; がないと、コンパイル時に「PSの値が未定義」というエラーになる*/
ps=ps_data;
for(i=0;i<2;i++,ps++){     /* Borland C++では2周目に動作停止 */
printf("name? -");  gets(ps->name);
printf("adress? -");  gets(ps->adress);
printf("tel? -"); gets(ps->tel); printf("\n");
}

ps=ps_data;
for(i=0;i<2;i++){
printf("%s  :  %s  : %s\n",(*(ps+i)).name,(*(ps+i)).adress,(*(ps+i)).tel);
}

ps=ps_data;
for(i=0;i<2;i++){
printf("%s  :  %s  : %s\n",(ps+i)->name,(ps+i)->adress,(ps+i)->tel);
}

ps=ps_data;
for(i=0;i<2;i++,ps++){
printf("%s  :  %s  : %s\n",ps->name,ps->adress,ps->tel);
}
return 0;}

U 構造体のコピー(代入)

int型のメンバー変数x,yをもつstarという型の構造体を作って、3つのやり方で構造体変数のコピー(代入)を
してみます。

struct3a.c
Borland C++, Visual C++でコンパイル可。

#include <stdio.h>

struct star{
int x;
int y;
};

int main(void)  {
struct star st, *p,q;  /* @  */

st.x=100; st.y=200;
q=st;                 /* A  */
  printf("q:%d   st:%d \n",&q,&st);
  printf("q.x:%d, q.y:%d\n",q.x,q.y);
        
st.x=1; st.y=2;
*p=st;                /* B */
  printf("p:%d   st:%d \n",p,&st);
  printf("p->x:%d, p->y:%d\n",p->x,p->y);
st.x=-5; st.y=0;      /* C */
  printf("st.x:%d, st.y:%d, p->x:%d, p->y:%d\n",st.x,st.y,p->x,p->y);
         
st.x=10; st.y=15;
p=&st;                /* D */
  printf("p:%d  &st:%d \n",p,&st);
  printf("p->x:%d, p->y:%d\n",p->x,p->y);
st.x=0; st.y=50;      /* E */
  printf("st.x:%d, st.y:%d, p->x:%d, p->y:%d\n",st.x,st.y,p->x,p->y);
return 0;
}





LSIC-86でコンパイルすると、Bの個所で、pのアドレスが定まっていないという警告がでる。
そのままコンパイルはできてしまうので、コンパイルして実行した結果



V 構造体を関数で受け渡し

構造体の変数を関数の引数として渡すプログラムです。


@の関数input()では、呼び出し側では、構造体配列 ps_data[ ]の先頭アドレス(ポインタ) である ps_data を
引数として関数に渡しています。関数input()側では、構造体のポインタ変数 *ps で構造体のデータを受け取っています。
(つまり、参照渡しをしている)、
関数input()において、メンバ変数 ps->name, ps->adress, ps->tel に 値を代入すると、ps_data[]も同じ値を
指すことになります。

A、Bは表示のための関数ですが、Aは構造体のポインタ変数を使って、Bは構造体変数を使って引数を
受け渡ししています。

struct2b.c Borland C++用

#include <stdio.h>

void input(struct personal *);
void display(struct personal *);
void display2(struct personal);

struct personal {
char name[20];
char adress[40];
char tel[15];
};

int main(void){
struct personal ps_data[2];

input(ps_data);
display(ps_data);
display2(ps_data[0]);
return 0;
}

void input(ps)    /* @ */
struct personal * ps
{int i;
for(i=0;i<2;i++,ps++){
  printf("name? -");  gets(ps->name);
  printf("%s\n",ps->name);
  printf("adress? -");  gets(ps->adress);
  printf("%s\n",ps->adress);
  printf("tel? -"); gets(ps->tel);  printf("%s\n",ps->tel);}
}

void display(ps)   /* A */
struct personal *ps;
{int i;
for(i=0;i<2;i++,ps++){
  printf("%s  :  %s  : %s\n",ps->name,ps->adress,ps->tel);}
}


void display2(ps)  /* B */
struct personal ps;
 { printf("%s  :  %s  : %s\n",ps.name,ps.adress,ps.tel);}

実行例
c:\bcc55\Bin>struct2b
name? -Yamada Taro
Yamada Taro
adress? -Nerima-Ku, Tokyo, Japan
Nerima-Ku, Tokyo, Japan
tel? -999-333-1234
999-333-1234
name? -John Bonn
John Bonn
adress? -NY, US
NY, US
tel? -000-000-0000
000-000-0000
Yamada Taro  :  Nerima-Ku, Tokyo, Japan  : 999-333-1234
John Bonn  :  NY, US  : 000-000-0000
Yamada Taro  :  Nerima-Ku, Tokyo, Japan  : 999-333-1234

c:\bcc55\Bin>


struct2b.c Visual C++2022用
最近のVisual C++はセキュリティ上の問題からgetsが使用禁止になっているので、
代わりにgets_sを使っている。

#include <stdio.h>

void input(struct personal*);
void display(struct personal*);
void display2(struct personal);

struct personal {
        char name[20];
        char adress[40];
        char tel[15];
};

int main(void) {
        struct personal ps, ps_data[2];

        input(ps_data);
        display(ps_data);
        display2(ps_data[0]);
        return 0;
}

void input(struct personal* ps)
{
        int i;
        for (i = 0; i < 2; i++, ps++) {
                printf("name? -");  gets_s(ps->name,20);
                printf("%s\n", ps->name);
                printf("adress? -");  gets_s(ps->adress,40);
                printf("%s\n", ps->adress);
                printf("tel? -"); gets_s(ps->tel,15); printf("%s\n",ps->tel);
        }
}

void display(ps)
struct personal* ps;
{int i;
for (i = 0; i < 2; i++, ps++) {
        printf("%s  :  %s  : %s\n", ps->name, ps->adress, ps->tel);
}}


void display2(ps)
struct personal ps;
{
        printf("%s  :  %s  : %s\n", ps.name, ps.adress, ps.tel);
}



LSIC-86

personal型の構造体の定義で、メンバー変数を配列ではなくポインタ変数にしてみた。
Borland C++やVisual C++でコンパイルすると、input関数で値を入力するところでプログラムが
停止してしまうが、LSIC-86でコンパイルするとちゃんと動作する。

ただし、LSIC-86では関数display2はpersonalが未定義というエラーが出る。
関数の引数として構造体変数を引き渡すときには、構造体のポインタ変数しか引き渡せない
(つまり参照渡しで引き渡すしかない)


struct2.c LSIC-86用

#include <stdio.h>

void input(struct personal *);
void display(struct personal *);
/* void display2(struct personal); これはpersonalが未定義というエラーがでる*/


struct personal {
char *name;
char *adress;
char *tel;
};

int main(void){
struct personal ps_data[2];

input(ps_data);
display(ps_data);
/* display2(ps_data[0]); */
return 0;
}

void input(ps)
struct personal *ps;
{int i;
for(i=0;i<2;i++,ps++){
printf("name? -");  gets(ps->name);
printf("adress? -");  gets(ps->adress);
printf("tel? -"); gets(ps->tel); printf("\n");
}}

void display(ps)
struct personal *ps;
{int i;
for(i=0;i<2;i++,ps++){
printf("%s  :  %s  : %s\n",ps->name,ps->adress,ps->tel);
}}

/*
void display2(ps)
struct personal ps;
        {printf("%s  : %s  :%s\n",ps.name,ps.adress,ps.tel);}
        
*/

mallocでメモリを動的に確保
personal型の構造体の定義で、メンバー変数を配列ではなくポインタ変数にしてみた場合、
mallocで動的にメモリを確保すれば、Borland C++でコンパイルしてもちゃんと動作する。
bufに入力された文字列は、strcpyを使って、各メンバ変数 ps->name, ps->adress, ps->tel
にコピーする。

struct2a.c Borland C++用

#include <stdio.h>

void input(struct personal *);
void display(struct personal *);
/* void display2(struct personal); */


struct personal {
char *name;
char *adress;
char *tel;
};

int main(void){
struct personal ps_data[2];

input(ps_data);
display(ps_data);
/* display2(ps_data[0]); */
return 0;
}

void input(struct personal * ps)
{int i;
char *buf;
for(i=0;i<2;i++,ps++){
printf("name? -");  gets(buf);
        ps->name=malloc(strlen(buf)+1); 
        strcpy(ps->name,buf);
        printf("%s\n",ps->name);
printf("adress? -");  gets(buf); 
        ps->adress=malloc(strlen(buf)+1);
        strcpy(ps->adress,buf);
        printf("%s\n",ps->adress);
printf("tel? -"); gets(buf); 
        ps->tel=malloc(strlen(buf)+1);
        strcpy(ps->tel,buf);
        printf("%s\n",ps->tel);
}}


void display(ps)
struct personal *ps;
{int i;
for(i=0;i<2;i++,ps++){
printf("%s  :  %s  : %s\n",ps->name,ps->adress,ps->tel);
}}

/*
void display2(ps)
struct personal ps;
        {printf("%s  : %s  :%s\n",ps.name,ps.adress,ps.tel);}
        
*/



TopPage