실수로 포인터 및 메모리 주소 정렬 구덩이를 밟았습니다.

임베디드 프로그래밍의 더 실용적인 기술을 배우려면 위의 파란색 글꼴을 클릭하십시오.
이 글이 도움이 되셨다면 좋아요+팔로우 부탁드립니다.

머리말

포인터는 C언어에서 중요한 개념이자 특징이며 C언어를 마스터하기 어려운 부분이기도 합니다. 포인터는 메모리 주소이고 포인터 변수는 메모리 주소를 저장하는 데 사용되는 변수입니다.

본질은 여전히 ​​변수이며 포인터는 저장 위치에 대한 동적 액세스 수단을 제공합니다(일반 변수와 비교하여 일반 변수는 자신이 차지하는 저장 위치에만 액세스할 수 있음).

메모리 주소 정렬은 컴퓨터가 메모리의 데이터를 정렬하고 액세스하는 방식으로, 기본 데이터 정렬과 구조 데이터 정렬이라는 두 개의 독립적이고 상호 관련된 부분을 포함합니다.

최신 컴퓨터는 메모리에서 바이트 블록 단위로 데이터를 읽고 씁니다.이론적으로 모든 유형의 가변 액세스는 모든 주소에서 시작할 수 있지만 컴퓨터 시스템은 메모리의 모든 데이터 유형에 대한 저장 위치가 제한되어 있으며 이러한 첫 번째 주소가 필요합니다. 주소 값은 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