Accidentalmente pisó el foso del puntero y la alineación de direcciones de memoria

Haga clic en la fuente azul de arriba para aprender más habilidades prácticas de programación integrada.
Si crees que este artículo es útil para ti, dale me gusta + seguir

prefacio

El puntero es un concepto importante y sus características en el lenguaje C, y también es una parte difícil de dominar el lenguaje C. Un puntero es una dirección de memoria y una variable de puntero es una variable utilizada para almacenar una dirección de memoria.

La esencia sigue siendo una variable, y el puntero proporciona un medio de acceso dinámico a la ubicación de almacenamiento (en comparación con las variables ordinarias, las variables ordinarias solo pueden acceder a la ubicación de almacenamiento que ocupan)

La alineación de direcciones de memoria es la forma en que la computadora organiza y accede a los datos en la memoria e incluye dos partes independientes e interrelacionadas: alineación de datos básicos y alineación de datos de estructura.

Las computadoras modernas leen y escriben datos en la memoria por bloques de bytes.En teoría, cualquier tipo de acceso variable puede comenzar desde cualquier dirección, pero el sistema informático tiene ubicaciones de almacenamiento limitadas para cualquier tipo de datos en la memoria, y requerirá la primera dirección de estos datos El valor de la dirección es un múltiplo entero de K (4 bits u 8 bits).

¿Cómo pisaste el pozo?

En un código muy bueno, la tasa de uso de los punteros es muy alta, porque los punteros pueden hacer que la implementación del código sea más gratuita, eficiente y conveniente, y muchas otras ventajas, pero para los amigos que no están muy familiarizados con los punteros, puede ser un desastre. cuando sube (comúnmente, el programa se escapa)

Por lo tanto, la tasa de uso de punteros probablemente puede juzgar el nivel de capacidad de programación de una persona.

Mire el código a continuación, ¿cuál es el resultado de la operación?

// 假设数组首地址为 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;
}

Los resultados esperados de muchos amigos son los siguientes (modo little endian):

HEX: 14 d0 07

¿Realmente tiene que ser así?

¡incierto!

Tal vez algunos amigos abren VS en sus computadoras, copian y pegan y ejecutan, compilan y ejecutan, ¡el resultado es realmente el mismo que el anterior! ! ! ¡No estás engañando a la gente! ! !

¿Has probado a ejecutar en MCU? ¿Se escapó el programa?

¿Por qué?

Cuando la computadora lee o escribe en una dirección de memoria, la almacena en fragmentos del tamaño de una palabra. La alineación de datos significa colocar datos en un desplazamiento de memoria igual a un múltiplo de la longitud de la palabra, y es de esta manera que la CPU maneja la memoria que mejora el rendimiento del sistema. La mayoría de las CPU solo pueden acceder a direcciones alineadas con la memoria.

Significa que algunas arquitecturas del sistema serán anormales al acceder a direcciones no alineadas.Si intenta acceder a las variables no alineadas de la memoria, provocará un error de bus. Solo admite acceso alineado.
Por ejemplo, cuando accedemos a una variable de 4 bytes (Double Word), si la dirección de inicio de la variable es divisible por 4, decimos que este acceso es de doble palabra alineado. Si se accede a una variable de 2 bytes (Word), se alinea cuando la dirección inicial es divisible por 2. El acceso a las variables de tipo byte ( Byte ) siempre está alineado.

De acuerdo con esto, la causa del problema se puede identificar rápidamente, es decir, el programa se ejecuta en esta posición y provoca un error de bus, por lo que se escapa.

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

Prevención y Solución

Con respecto al método de escritura mencionado anteriormente, algunos amigos pueden haber implementado una determinada función en el lado de la computadora, y no hay ningún problema en la prueba de la computadora, pero una vez que se trasplanta a la MCU, no funcionará, así que date prisa y verifica si este es el problema. Por lo tanto, para garantizar que nuestro código tenga una alta portabilidad y estabilidad, debemos prevenir esta situación, que se puede evitar
accediendo a variables de tipo byte ( Byte ), o utilizandomemcpy

// 假设数组首地址为 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

Supongo que te gusta

Origin blog.csdn.net/weiqifa0/article/details/129891233
Recomendado
Clasificación