構造体
関連記事:構造体定義によるメモリ配置への影響
構造体を使った配列へのアクセス
構造体:「情報」をまとめて一つで使いたい扱いたいときに使用する。
複数の情報から構成されるデータをパッケージ化し、独自の型として定義する。
複数の情報 例:
https://gyazo.com/74316c6ad31b8529a8b2a1cb66faf184
参考: 構造体の型定義
構造体の型定義(構造体定義):
基本的な書き方:
stract 構造体タグ名{
構造体メンバ;
};
main関数の中
構造体の型名 構造体変数名;
構造体変数名.構造体メンバ= xxxx;
例:
code:c
struct Square{
int vertical;
int beside;
};
int main(void){
struct Square squ; //構造体の変数定義
squ.vertical = 10;
squ.beside = 20;
printf("縦の長さ:%d、横の長さ:%d",squ.vertical,squ.beside);
return 0;
}
実行結果:https://paiza.io/projects/b3Rv_Ujb8fbs5C79wnJG7A
typedefを使った構造体の型定義:
typedefを使用することにより、変数定義でstructを記述する必要がなくなり省略できる。
書き方:
typedef struct{
構造体メンバ
}構造体のデータ型名;
main関数の中
構造体の型名 構造体変数名;
構造体変数名.構造体メンバ= xxxx;
例:
code:c
typedef struct{
double latitude;
double longtude;
}Coodinate;
int main(){
Coodinate pos; //構造体の変数定義
pos.latitude = 34.1;
pos.longtude = 134.2;
printf("経度:%lf、緯度:%lf",pos.latitude,pos.longtude);
return 0;
}
ブラウザで、確認。
実行結果:https://paiza.io/projects/apNaW_PNvS4-4NaW0SgBGg?language=c
構造体変数の初期化方法:
下記書き方で初期化が可能
code:c
typedef struct{
double latitude;
double longtude;
}Coordinate;
int main(){
Coordinate potision; //構造体の変数定義
// 全メンバを初期化
Coordinate pos1 = {34.6461, 134.9991};
// 最初のメンバのみ初期化
Coordinate pos2 = {34.6461}; //他のメンバは0になる。
//メンバのコピー
Coordinate pos3 = po1;
printf("経度pos1:%lf、緯度pos1:%lf\n",pos1.latitude,pos1.longtude);
printf("経度pos2:%lf、緯度pos2:%lf\n",pos2.latitude,pos2.longtude);
printf("経度pos3:%lf、緯度pos3:%lf\n",pos3.latitude,pos3.longtude);
return 0;
}
https://gyazo.com/e60de4a80ea561e908bf5bcadbea70b6
構造体を使った関数:
code:c
#include <stdio.h>
// 座標構造体の型定義
typedef struct
{
double latitude; // 緯度
double longitude; // 経度
} Coordinate;
// 座標情報を画面に出力する
void printCoordinate(Coordinate pos)
{
printf("lat:%lf\n", pos.latitude);
printf("lon:%lf\n", pos.longitude);
}
int main(void)
{
Coordinate pos = {34.6461867, 134.9991424};
// 座標の表示
printCoordinate(pos);
return 0;
}
ブラウザで確認
https://gyazo.com/7aac48705e67253f307d9867704761b4
アラインメント(境界調整):
参考:https://hirokuma.blog/?p=1691
メモリアライメント:メモリアドレス番地の位置のこと
以下を守って実装することがアライメントを守るという意味になる。
CPUビット数単位のアドレスにCPUビット数単位のデータアクセス(ワードアクセス)
CPUビット数半分単位のアドレスにCPUビット数半分単位のデータアクセス(ハーフワードアクセス)
1byte単位のアドレスアラインに1byte単位のデータアクセス(バイトアクセス)
これらが守れていない場合、アライメントに違反するという
守るためにアドレス番地を調整することをアライメントを調整するという。
構造体定義によるメモリ配置への影響:
例:
code:c
typedef struct {
unsigned long a;
unsigned short b;
unsigned short c;
unsigned long d;
} hoge1;
typedef struct {
unsigned long a;
unsigned short b;
} hoge2;
typedef struct {
unsigned long a;
unsigned short b;
unsigned short c;
unsigned short d;
} hoge3;
typedef struct {
unsigned long a;
unsigned short b;
unsigned long d;
unsigned short c;
} hoge4;
int main(){
printf("long:%d\n",sizeof(long));
printf("short:%d\n",sizeof(short));
printf("hoge1_サイズ:%d\n",sizeof(hoge1));
printf("hoge2_サイズ:%d\n",sizeof(hoge2));
printf("hoge3_サイズ:%d\n",sizeof(hoge3));
printf("hoge4_サイズ:%d\n",sizeof(hoge4));
}
long:8byteのメンバ (64bit)
https://www.google.com/search?q=long+バイト数&oq=long+バイト数&aqs=chrome..69i57.4931j0j1&sourceid=chrome&ie=UTF-8
shory:2byteのメンバ
それぞれ2個ずつ持っている。
hoge1とhoge2で、違うのはメンバの並びだけ。
https://gyazo.com/3107a912baca8651948fbc8c41c455bf
https://gyazo.com/5a8e57819f789af18327e45ebccefb4f
出力結果が異なる。
https://gyazo.com/4f4987a085e527ec09e52cc9da963147
ブラウザ確認:https://paiza.io/projects/GSF_WP3rGgNEEklP84cezQ
参考:sizeof:https://monozukuri-c.com/langc-funclist-sizeof/
構造体確認
code:c
typedef struct {
unsigned char ver;
}Square;
int main(void){
Square squ; //構造体の変数定義
int test = 1;
squ.ver = test;
if(squ.ver == test){
printf("長さ");
}else{
printf("none");
}
return 0;
}
結果:https://paiza.io/projects/eFGiu4y1AzTTwscImdG94Q?language=c
参考:
https://qiita.com/trgkpc/items/61ba1a8084c8cc41de47
構造体の確認:
code:c
///PGV波形モード
typedef enum {PGVMODE_AC=0 ///<AC
,PGVMODE_DC ///<DC
}PGVMODE;
/** DOP設定情報 構造体の定義 ************************************* */
typedef struct {
/** 4バイト目 ************************************************ */
int eDopUnit :3; ///<波形の単位(KHz/cms)
int eAtPos :4; ///<オートポジション設定値
int scAtScl :5; ///<オートスケール設定値
int eSmooth :1; ///<スムージングフィルタ周波数(10Hz/5Hz)
} FRZ_DOP_SET_INF;
#include <stdio.h>
int main(void){
// Your code here!
FRZ_DOP_SET_INF FrzDop;
int num = 20;
int anum;
int bnum;
int cnum;
for(int i=0;i<num;i++){
int w = i;
FrzDop.eDopUnit = w;
FrzDop.eAtPos = w;
FrzDop.scAtScl = w;
ai = FrzDop.eDopUnit;
bi = FrzDop.eAtPos;
ci = FrzDop.scAtScl;
printf("i=%d,a=%d,b=%d,c=%d\n",i,ai,bi,ci);
//printf("size(a)=%d\n",sizeof(a));
}
}
結果:https://paiza.io/projects/-UEtbjLobvxm3ITj6xmZLw?language=c
int eDopUnit :3; の意味
2ビット目までは整数、それを超えるとマイナスになる。
https://hogehoge.tk/tool/number.html
つまり、2ビット目(11まで)は、整数で、それを超えるとマイナスになる。
構造体の確認:
構造体のサイズ確認
code:c
typedef struct { //サイズ:int(4)×4=16
int mode1;
int mode2;
int mode3;
int mode4;
}MODE;
typedef struct {
int N1;
char N2;
}N;
typedef struct{ //サイズ:int(4) + MODE(16) =20
int NUM1;
MODE NUM2;
}NUM;
typedef enum{ //サイズ:4
t1,
t2,
t3
}Test;
typedef struct{ //サイズ:Test(4) + int(4) = 8
Test conf1;
}Cnf;
typedef struct{
Test *point;
//Test *point2;
}POINTA;
#include <stdio.h>
int main(void){
MODE mode;
NUM num;
printf("MODE:%lu\n",sizeof(MODE));
printf("N:%lu\n",sizeof(N));
printf("NUM:%lu\n",sizeof(NUM));
printf("Test:%lu\n",sizeof(Test));
printf("Cnf:%lu\n",sizeof(Cnf));
printf("POINTA:%lu\n",sizeof(POINTA));
printf("mode.mode1=%lu\n",sizeof(mode.mode1));
printf("num.NUM2=%lu\n",sizeof(num.NUM2));
return 0;
}
実行結果:https://paiza.io/projects/-IktY0YlY0BtEKVUiSPFlA?language=c
構造体バッファ:
別ファイルの構造体を使用する場合の注意点
https://paiza.io/projects/ObGKBJLVy9RBiE48smn0xQ?language=c
複数ファイルで使用確認
https://paiza.io/projects/nKCqijQyCblJ3SCPLyjEvw?language=c
https://paiza.io/projects/V3AWV3KlaCDVcHINDlyWBA?language=c
構造体の配列
https://paiza.io/projects/74rtaGtz07h7WDAnYtU28A?language=c
構造体をポインタで渡す。
https://paiza.io/projects/F_3JWsoWwke7SO1ENAx9NQ?language=c
https://paiza.io/projects/Eh2FLoKHAbiVESKkA8C4Vw?language=c
構造体ポインタの取得
https://paiza.io/projects/sIwPxrOOP_J1E9lsMT7T4A?language=c
https://paiza.io/projects/zTbJvmzwGQwVbZXLinZjpQ
https://paiza.io/projects/OxcmRHRxvI3CfNP3vr_akA?language=c
https://paiza.io/projects/sIwPxrOOP_J1E9lsMT7T4A?language=c
https://paiza.io/projects/CkHMZnSsBQlhg4h5pzAzbA?language=c
table:構造体ポインタ
ポインターを介してアクセスする場合 直接メンバーにアクセスする場合
メリット ・動的なデータ構造を扱う場合、ポインターを介してアクセスする必要がある ・コードが簡潔で、可読性が高い
・構造体の複数のメンバーにアクセスする場合、ポインターを使用するとコードが簡潔になる場合がある ・単一のメンバーにアクセスする場合、直接アクセスする方が使いやすい場合がある
デメリット ・ポインターの型を考慮する必要があるため、コードが複雑になる場合がある ・動的なデータ構造を扱う場合には、ポインターを介してアクセスする方が扱いやすい場合がある
・ポインターがNULLである場合には、クラッシュする可能性がある ・構造体の複数のメンバーにアクセスする場合、コードが長くなる場合がある
code:c
#include <stdio.h>
typedef struct{
int test1;
int test2;
}TTest;
typedef struct{
TTest samp1;
TTest samp2;
int samp3;
}TSamp;
volatile TSamp gsamp;
void funcA();
int main(void){
// Your code here!
//関数 funcAの実行
funcA();
//構造体の中を出力
printf("test1=%d\n",gsamp.samp1.test1);
printf("test1=%d\n",gsamp.samp1.test2);
}
void funcA(){
//ポインタ情報を取得
TTest *ptest;
ptest = (TTest *)&gsamp.samp1;
ptest -> test1 = 2; ///ポインタを使って構造体に格納
gsamp.samp1.test2 = 2; ///変数を指定して、構造体に格納
}
結果:https://paiza.io/projects/sIwPxrOOP_J1E9lsMT7T4A?language=c
このプログラムの ptest = (TTest *)&gsamp.samp1; は、gsamp.samp1と同じメモリアドレスを持つTTest型のポインタptestを宣言しています。このポインタptestは、gsamp.samp1の実際の型であるTSamp型ではなく、TTest型であるかのように振る舞います。&gsamp.samp1は、gsamp変数のsamp1メンバーのアドレスを意味します。そして、(TTest *)は、ポインタをTTest型にキャストすることを意味します。
つまり、この行は、gsamp.samp1の先頭アドレスをTTest型のポインタに変換して、ptestがそれを指すようにすることで、gsamp.samp1構造体のメモリをTTest型のポインタでアクセスできるようにするためのものです。これにより、ptestを通じて、TTest型の変数test1とtest2にアクセスできます。
その後、ptest -> test1 = 2; は、ptestが指すTTest型のオブジェクトのtest1フィールドに2を代入することを意味します。これにより、gsamp.samp1.test1に2が格納されます。同様に、gsamp.samp1.test2 = 2;は、gsamp.samp1のtest2フィールドに2を直接代入することを意味します。
構造体と配列
https://paiza.io/projects/HkttO5ahiiiCj87zQVCQow?language=c
構造体について
基本的な動きは、通常の変数と同じである。
構造体を使う場所、理由としては下記がある。
変数の名前の一貫性
複数の変数を関数に渡す時に、構造体で渡せる。
などがある。
変数の名前の一貫性とは
プログラムが大規模になるにつれて、変数は多くなっていく。
その為、変数の名前はその変数が何のための、何をする変数なのかが分かる形で書く必要がある。
構造体は、xxxx.xxxという形で、を使用することにより、その変数の表す意味が分かりやすくなる。
https://tcs.c.titech.ac.jp/csbook/c_lang/chap11.html
https://paiza.io/projects/2dsGUT3MpyOMZNfMHcR0_g?language=c
ポインタと構造体
code:c
#include <stdio.h>
// TTest構造体の作成
typedef struct {
int test1;
int test2;
} TTest;
// TSamp構造体の作成
typedef struct {
char tsamp1;
TTest samp1; // 構造体TTestで宣言
TTest samp2; // 構造体TTestで宣言
} TSamp;
typedef struct {
TSamp *psamp;
} TSset;
typedef struct {
void
TSamp *psamp2;
} TSset2;
TSamp gsamp;
TSset gsset;
TSset2 gsset2;
// プロトタイプ宣言
void funcA(TSset *psset);
int main(void) {
// 実体化
TSamp *psamp;
// gsampのアドレスをgsset.psampに設定
gsset.psamp = &gsamp;
gsset2.psamp5 = &gsamp; // gsset2.psamp5を初期化
psamp = gsset.psamp;
// 関数 funcAの実行
// 引数として、TSset構造体のポインタを渡す。
funcA(&gsset);
// 構造体の中を出力
printf("test1=%d\n", gsamp.samp1.test1);
printf("test2=%d\n", gsamp.samp1.test2);
printf("test1=%d\n", gsset.psamp->samp1.test1);
printf("test3=%d\n", gsset2.psamp5->samp1.test1);
return 0;
}
// TSsetの中のpsampを使ってsamp1に値を代入する関数
void funcA(TSset *psset) {
// psset->psampを介してsamp1のtest1とtest2に値を代入
TSamp *psamp2;
psamp2 = gsset2.psamp5;
gsset2.psamp5 ->samp1.test1 = 3;
//psamp2->samp1.test1 = 3;
psset->psamp->samp1.test1 = 1;
psset->psamp->samp1.test2 = 2;
psset->psamp->samp1.test1 = 4;
}
https://paiza.io/projects/y0MJZPfSkaUI4DBrepsEIA?language=c
構造体の初期化/宣言方法
https://paiza.io/projects/b0c5XYKIyGl31RBEk0ee0g?language=c
構造体の初期化/宣言方法2
ポインタの取得
https://paiza.io/projects/k5csAYzoSKsL_eHnkOnF_Q?language=c
https://paiza.io/projects/Ph77uyd7S9mqu3ZG7YxYIg?language=c
初期化子を使用
https://paiza.io/projects/HtPz9_R646c4qcCQWXTZzQ?language=c
関数ポインタ?と構造体
https://paiza.io/projects/UxDUklDQlRyKx5XpGopbKQ
https://paiza.io/projects/5kh5D3Hr7ED_81X-BVRDVg
エラー:https://paiza.io/projects/oVRRIoA3Bt-o43VAd9zsYQ
///構造体で関数ポインタを使用する
https://paiza.io/projects/C-sLeGuTBlqWegqpnEjNJQ
///get.cファイルの関数をget.hをmain.cでインクルードしないで実行する1
https://paiza.io/projects/xgZ-8x8HgTRaBeT5tQq2YA
///get.cファイルの関数をget.hをmain.cでインクルードしないで実行する2
https://paiza.io/projects/8D6hYluXZczkgoDGwkqZBw
https://paiza.io/projects/UJXT1zoQ1mnnUlztqQTd5A