Cでの構造化データ(変数、ポインター、配列、文字列、構造体、および共用体)のメモリ表現

構造化データ表現

1.メモリアドレス:

すべてのデータはメモリに保存されます。
メモリは、0xから始まる番号付きのバイトシーケンスです。その数はメモリアドレスを計算します。
通常、コンパイル後、変数名と変数タイプはなくなり、メモリアドレスに置き換えられます。

ここに画像の説明を挿入

次に、グローバル変数とローカル変数のメモリレイアウト

例を通して、彼らのメモリアドレスを観察します

#include<stdio.h>
/*全局未初始化变量*/
int global_int1;
int global_int2;
/*全局初始化变量*/
int global_int3=1;
int global_int4=1;
int main(){
    
    
    /*局部未初始化变量*/
    int local_int1;
    int local_int2;
    /*局部初始化变量*/
    int local_int3=1;
    int local_int4=1;

    printf("全局变量的值:%d   %d   %d    %d\n",global_int1,global_int2,global_int3,global_int4);
    printf("局部变量的值:%d   %d   %d    %d\n",local_int1,local_int2,local_int3,local_int4);
    printf("初始化的全局变量地址:%#x   %#x\n",&global_int3,&global_int4);
    printf("未初始化的全局变量地址:%#x   %#x\n",&global_int1,&global_int2);
    printf("局部变量地址:%#x    %#x    %#x    %#x\n",&local_int1,&local_int2,&local_int3,&local_int4);
}

運転結果

全局变量的值:0   0   1    1
局部变量的值:35   8   1    1
初始化的全局变量地址:0x402000   0x402004
未初始化的全局变量地址:0x40506c   0x405068
局部变量地址:0x61fefc    0x61fef8    0x61fef4    0x61fef0

実行結果の値を見ると、次のことがわかります:

初期化されていないグローバル変数はどういうわけか0に初期化されているようですが、ローカル変数は初期化されていません。

次に、アドレスの値を観察し、次のことを見つけます
。ローカル変数の場合:

すべてのローカル変数は連続して割り当てられ、集約されると、
変数はより低いアドレスに向かって大きくなります。

グローバル変数の場合:

初期化された変数は1つのクラスターにあり、初期化されていない変数は別のクラスターにあります。
初期化されていない変数は低いアドレスに向かって成長し、初期化された変数は高いアドレスに向かって成長します。

説明:int型は4バイトを占めるため、一度に4ずつ増やします。
ここに画像の説明を挿入

3.メモリ内のデータの表現

まず、1つのことを理解する必要があります。それは、ポインターとは何ですか。

ポインタはメモリアドレスを格納します。これはC言語の柔軟性でもあります。ポインターの詳細な説明については、C言語でポインターを表示できます。

次に、質問について考えてみましょう。char型を使用してintデータを読み取ることはできますか?

どちらもハードウェアの生のビットで表されるため、答えは「はい」です。

これは、メモリへのデータの保存に関連しています。例を見てみましょう。

#include<stdio.h>
int main(){
    
    
    int i=0x8041;
    char *p;
    p=(char*)&i;
    printf("%#x\n",*p);
}

その結果はどうなりますか?

//运行结果
0x41

どうしたの?
メモリ内の変数iは次のようになります。
ここに画像の説明を挿入

char文字タイプは1バイトしか占有しないため、41のみが読み取られます。

値を0x12345678に変更する場合は、テストしてください。

#include<stdio.h>
int main(){
    
    
    int i=0x12345678;
    char *p;
    p=(char*)&i;
    printf("%#x\n",*p);
}

実行の結果は

0x78

コーディング

値のビットへのマッピングを指定します。つまり、どのビットシーケンスがどの整数を表し、どのビットシーケンスがどの文字を表す
を指定します。特定のタイプに必要なビット数を指定します。たとえば、int、4バイト、32ビットが必要です。
コンピューターと
使用するエンコードコンパイラーがその役割を果たし、これらの詳細を処理してくれます。

第四に、メモリ内の配列の表現

配列定義

均一タイプの要素のシーケンス

メモリ割り当て

メモリ内の配列要素を昇順で配置します

#include<stdio.h>
int main(){
    
    
    int a=0;
    int intArray[5]={
    
    1,2,3,4,5};
    int b=6;
}

記憶表現:
ここに画像の説明を挿入

ポインタと配列

intArray+N=&(intArray[N])
intArray[N]=*(intArray+N)

マルチバイト型の配列
例を参照してください。

#include<stdio.h>
int main(){
    
    
    int intArray[10]={
    
    1,2,3,4,5,6,7,8,9,10};
    int *p=(int*)((char*)intArray+7);
    printf("%#x\n",*p);
}

演算結果:

0x300

どうしたの?
ここに画像の説明を挿入
時間があるときに2次元配列を書く

5.メモリ内の文字列の表現

cには、文字列タイプはありません。文字
列は、文字「\ 0」(ヌル文字とも呼ばれます)で終了する文字の配列です。

文字列配列の最初のアドレスなどの一般的な標準文字列ライブラリchar *strcat(char *dest,char *src)が関数に渡されるので、配列の長さを渡してみませんか?個人的には、これが文字列が文字「\ 0」(ヌル文字とも呼ばれます)で終了する理由の1つであり、これを使用してトラバースできます。

例:

#include<stdio.h>
int main(){
    
    
    int i;
    char a[4]="hi?";
    char b[3]="hi?";//错误写法,很危险
    for(i=0;i<4;i++){
    
    
        printf("%#x:%c(%d)\n",&a[i],a[i],a[i]);
    }
    printf("\n");
    for(i=0;i<3;i++){
    
    
        printf("%#x:%c\n",&b[i],b[i]);
    }
    puts(b);
}

運転結果

0x61fef8:h(104)
0x61fef9:i(105)
0x61fefa:?(63)
0x61fefb: (0)

0x61fef5:h
0x61fef6:i
0x61fef7:?
hi?hi?

どうしたの?
ここに画像の説明を挿入

6.メモリ内の構造と結合の表現

集約データ型は、一度に複数の個別のデータを格納できます。Cは、配列と構造の2種類の集計データ型を提供します。
配列は、同じタイプの要素のコレクションであり、各要素は、添え字参照またはポインターの間接参照によって選択されます。
構造体はそのメンバーと呼ばれる値のコレクションでもありますが、構造体のメンバーは異なるタイプを持つ場合があります。
配列要素の長さは同じであるため、配列要素の添え字には添え字を付けることでアクセスできます。
ただし、これは構造体には当てはまりません。構造体のメンバーは長さが異なるため、添え字を使用してアクセスすることはできません。代わりに、各構造体メンバーには独自の名前があり、名前でアクセスされます。

構造体のメモリについて説明する前に、構造体の宣言を見てみましょう。

例1:この宣言は、整数、文字、浮動小数点数の3つのメンバーを持つxという名前の変数を作成します。

struct{
    
    
    int a;
    char b;
    float c;
}x;

例2:このステートメントはyとzを作成します。yは、20個の構造を含む配列です。zは、このタイプの構造体へのポインターです。

struct{
    
    
    int a;
    char b;
    float c;
}y[20],*z;

これら2つの構造体のメンバー変数は同じですが、同じタイプですか?

答えはノーです。これらの2つの宣言は、コンパイラーによって2つの異なるタイプとして扱われます。つまりz=&x、違法です。

したがって、問題は、以前に宣言されたタイプを引き続き使用する場合、別の宣言で作成する必要があるかどうかです。

答えはノーです。タグを使用できます。タグを使用すると、複数の宣言で同じメンバーリストを使用できます。次のように:

struct SIMPLE{
    
    
    int a;
    char b;
    float c;
};
struct SIMPLE x;
struct SIMPLE y[20],*z;
z=&x;//这时候是合法的;

ただし、構造体を宣言するためのより良い方法はtypedef、以下を使用して新しい型を作成することです。

typedef struct{
    
    
    int a;
    char b;
    float c;
}SIMPLE;//此时SIMPLE是个类型名而不是结构标签

SIMPLE x;
SIMPLE y[20],*z;

トピックを入力してください:メモリ内の構造の実際のストレージを見てみ
ましょう最初に例を見てみましょう

#include<stdio.h>
int main(){
    
    
    typedef struct {
    
    
        char a;
        char b;
        int c;
        double d;
    }test;
    test x;
    printf("%#x\n%#x\n%#x\n%#x\n",&x.a,&x.b,&x.c,&x.d);
    printf("%d",sizeof(x));
}

演算結果:

0x61fef0
0x61fef1
0x61fef4
0x61fef8
16

ここに画像の説明を挿入
なんでそうなの?次に、構造内のメモリストレージの配置ルールを理解する必要があります。

1.コンパイラーは、メンバー・リストの順序で各メンバーに1つずつスペースを割り当てます
。2。構造体の配置は、その要素の最大配置と同じです。3
.構造体のサイズは、その配置の倍数です
4 。サイズがKバイトデータは、Kの整数倍のアドレスに格納する必要があります

この例を少し変更します。

#include<stdio.h>
int main(){
    
    
    typedef struct {
    
    
        char a;
        char b[4];
        int c;
        double d;
    }test;
    test x;
    printf("%#x\n%#x\n%#x\n%#x\n",&x.a,&x.b,&x.c,&x.d);
    printf("%d",sizeof(x));
}
0x62fe00
0x62fe01
0x62fe08
0x62fe10
24

ここに画像の説明を挿入

では、なぜこのように調整するのでしょうか。スペースの無駄ではないでしょうか?

これは、CPUがメモリを処理する方法に関連しており、システムのパフォーマンスを向上させ、データへのアクセス速度を上げることができます。

では、ユニオンとは何ですか?

構造体とは異なり、ユニオンの各メンバーは1つのメモリを共有し、使用時に1つだけが選択されます。

構造体のメモリ内ストレージ

占有されるスペースは、ユニオン内の最大の要素によって異なります

例を見る

#include<stdio.h>
int main(){
    
    
    typedef union{
    
    
        char a;
        char b[4];
        int c;
        double d;
    }test;
    test x;
    printf("%#x\n%#x\n%#x\n%#x\n",&x.a,&x.b,&x.c,&x.d);
    printf("%d",sizeof(x));
}

実行構造:

0x61fef8
0x61fef8
0x61fef8
0x61fef8
8

おすすめ

転載: blog.csdn.net/weixin_47138646/article/details/122017208