誤ってポインタとメモリアドレスの整列の穴を踏んだ

組み込みプログラミングのより実践的なスキルを学ぶには、上の青いフォントをクリックしてください。
この記事が役に立ったと思われる場合は、「いいね」と「フォロー」をお願いします

序文

ポインタはC言語における重要な概念であり、その特徴であり、C言語を習得する上で難しい部分でもあります。ポインタはメモリアドレスであり、ポインタ変数はメモリアドレスを格納するために使用される変数です。

本質は依然として変数であり、ポインターは記憶域の場所への動的アクセスの手段を提供します (通常の変数と比較して、通常の変数はそれらが占める記憶域の場所にのみアクセスできます)

メモリ アドレス アライメントは、コンピュータがメモリ内のデータを配置してアクセスする方法であり、基本データ アライメントと構造データ アライメントという 2 つの独立した相互に関連する部分が含まれます。

現代のコンピュータはメモリ内のデータをバイト ブロック単位で読み書きします. 理論的には, あらゆるタイプの変数アクセスはあらゆるアドレスから開始できます. しかし, コンピュータ システムではメモリ内のあらゆるデータ タイプの格納場所が制限されており, これらの最初のアドレスが必要になります.アドレスの値は、K (4 ビットまたは 8 ビット) の整数倍です。

どうやってピットに足を踏み入れたのですか?

非常に優れたコードでは、ポインターの使用率は非常に高くなります。ポインターを使用すると、コードの実装がより自由になり、効率的で便利になり、他の多くの利点が得られるからです。立ち上がったとき(通常、プログラムは暴走します)

したがって、ポインターの使用率は、おそらくその人のプログラミング能力のレベルを判断することができます

以下のコードを見てください。操作の結果は何ですか?

// 假设数组首地址为 0x00004000,符合内存对齐:4的倍数
static unsigned char sg_arrBuf[100]; 

int main()
{
    memset(sg_arrBuf, 0, sizeof(sg_arrBuf));

    // 地址为 0x00004000
    uint8_t *pucVal = (uint8_t *)&sg_arrBuf[0];

    // 地址为 0x00004001
    uint16_t *puiVal = (uint16_t *)&sg_arrBuf[1];

    *pucVal = 20;   // HEX: 0x14
    *puiVal = 2000; // HEX: 0x07d0

    printf("HEX: ");

    for (int i = 0; i < 3; i++)
    {
        printf("%02x ", sg_arrBuf[i]);
    }

    printf("\n");

    return 0;
}

多くの友人の予想される結果は次のとおりです (リトル エンディアン モード):

HEX: 14 d0 07

本当にそうしなければならないのでしょうか?

不確かです!

友達の中には自分のコンピューターで VS を開き、コピーして貼り付けて実行し、コンパイルして実行すると、結果は上記とまったく同じになります! ! ! 人をだましていませんか!! !

MCUで動かしてみた?プログラムが暴走した?

なぜ?

コンピューターがメモリ アドレスに対して読み取りまたは書き込みを行う場合、それはワード サイズのチャンクに格納されます。データ アライメントとは、ワード長の倍数に等しいメモリ オフセットにデータを配置することを意味し、CPU がこのようにメモリを処理することで、システム パフォーマンスが向上します。ほとんどの CPU は、メモリ アラインされたアドレスにのみアクセスできます。

これは、アラインされていないアドレスにアクセスすると、一部のシステム アーキテクチャが異常になることを意味し、アラインされていないメモリ変数にアクセスしようとすると、バス エラーが発生します。アラインされたアクセスのみをサポートします。
たとえば、4 バイト (ダブルワード) の変数にアクセスする場合、変数の開始アドレスが 4 で割り切れる場合、このアクセスはダブルワード アラインされていると言います。2 バイト (Word) の変数にアクセスした場合、開始アドレスが 2 で割り切れる場合にアラインされます。バイト ( Byte ) 型変数へのアクセスは常に整列されます。

これにより、問題の原因、つまり、プログラムがこの位置まで実行され、バス エラーが発生して暴走したという問題の原因を迅速に特定できます。

// 地址为 0x00004001,未对齐
*puiVal = 2000; // HEX: 0x07d0

予防と解決

上記の書き込み方法について、一部の友人がコンピューター側で何らかの機能を実装している可能性があり、コンピューターのテストでは問題はありませんが、MCUに移植されると動作しなくなりますので、急いで確認してくださいこれが問題かどうか。
したがって、コードの高い移植性と安定性を確保するには、バイト ( Byte ) 型の変数にアクセスするか、ある種の問題を使用することで、memcpyこのような状況を回避する必要があります。

// 假设数组首地址为 0x00004000,符合内存对齐: 4的倍数
static unsigned char sg_arrBuf[100]; 

int main()
{
    memset(sg_arrBuf, 0, sizeof(sg_arrBuf));

    uint8_t ucVal = 20;
    uint16_t uiVal = 2000;

    memcpy(&sg_arrBuf[0], &ucVal, 1);
    memcpy(&sg_arrBuf[1], &uiVal, 2);

    printf("HEX: ");

    for (int i = 0; i < 3; i++)
    {
        printf("%02x ", sg_arrBuf[i]);
    }

    printf("\n");

    return 0;
}

c28b4238ebc84ad080f8bc94fb76ed50.jpeg


56378bd93524c80dc7c2d33552a3fdd4.jpeg

b9341682e3e7280e2794368412a3a4f3.jpeg

おすすめ

転載: blog.csdn.net/weiqifa0/article/details/129891233