Representación en memoria de datos estructurados (variables, punteros, matrices, cadenas, estructuras y uniones) en C

representación de datos estructurados

1. Dirección de memoria:

Todos los datos se almacenan en la memoria.La
memoria es una secuencia numerada de bytes, comenzando con 0x. Ese número calcula la dirección de memoria.
Por lo general, después de la compilación, el nombre de la variable y el tipo de variable desaparecen y son reemplazados por la dirección de memoria.

inserte la descripción de la imagen aquí

En segundo lugar, el diseño de la memoria de variables globales y variables locales.

A través de un ejemplo, observe sus direcciones de memoria

#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);
}

resultado de la operación

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

Mirando los valores en los resultados de la ejecución, encontramos:

Las variables globales no inicializadas parecen inicializarse en 0 de alguna manera, pero las variables locales no se inicializan.

A continuación observamos el valor de la dirección y encontramos que:
para variables locales:

Todas las variables locales se asignan de forma contigua y, cuando se agregan, las
variables crecen hacia direcciones más bajas.

Para variables globales:

Las variables que se inicializan están en un grupo y las que no están inicializadas están en otro.
Las variables no inicializadas crecen hacia direcciones más bajas y las variables inicializadas crecen hacia direcciones más altas.

Explicación: Aumente de 4 en 4, porque el tipo int ocupa 4 bytes.
inserte la descripción de la imagen aquí

3. Representación de datos en memoria

Primero debemos darnos cuenta de una cosa, ¿qué es un puntero?

El puntero almacena la dirección de memoria, que también es la flexibilidad del lenguaje C. Para obtener una explicación detallada de los punteros, puede ver los punteros en lenguaje C

Entonces pensemos en una pregunta, ¿podemos usar un tipo char para leer datos int?

La respuesta es sí, porque ambos están representados en bits sin formato en el hardware.

Esto está relacionado con el almacenamiento de datos en memoria, veamos un ejemplo:

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

¿Cuál será su resultado?

//运行结果
0x41

¿Qué está sucediendo?
La variable i se ve así en la memoria:
inserte la descripción de la imagen aquí

El tipo de carácter char solo ocupa un byte, por lo que solo se leen 41;

Pruébelo si cambia el valor a 0x12345678;

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

El resultado de correr es

0x78

codificación

Especifica la asignación de valores a bits, es decir, qué secuencia de bits representa qué número entero y qué secuencia de bits representa qué carácter
. Especifica cuántos bits se requieren para un tipo particular, digamos int, 4 bytes, 32 bits se requieren
dependiendo de la computadora y el compilador de codificación
que usamos hace el trabajo y maneja estos detalles por nosotros.

Cuarto, la representación de la matriz en la memoria.

definición de matriz

una secuencia de elementos de un tipo uniforme

asignación de memoria

Organizar los elementos de la matriz en la memoria en orden creciente

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

Representación de la memoria:
inserte la descripción de la imagen aquí

Punteros vs matrices

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

Matrices de tipos multibyte
Vea un ejemplo:

#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);
}

resultado de la operación:

0x300

¿Qué está sucediendo?
inserte la descripción de la imagen aquí
Escribe una matriz bidimensional cuando tengas tiempo.

5. Representación de cadenas en memoria

En c, no hay ningún tipo
de cadena Una cadena es una matriz de caracteres que termina en el carácter '\0' (también conocido como carácter nulo)

Las bibliotecas de cadenas estándar comunes, como char *strcat(char *dest,char *src), la primera dirección de la matriz de cadenas se pasa en la función, entonces, ¿por qué no pasar la longitud de la matriz? Personalmente, esta es una de las razones por las que las cadenas terminan con el carácter '\0' (también conocido como carácter nulo), que se puede utilizar para atravesar.

ejemplo:

#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);
}

resultado de la operación

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

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

¿Qué está sucediendo?
inserte la descripción de la imagen aquí

6. Representación de estructuras y uniones en memoria

Los tipos de datos agregados son capaces de almacenar más de una pieza de datos separada a la vez. C proporciona dos tipos de tipos de datos agregados, matrices y estructuras.
Una matriz es una colección de elementos del mismo tipo, cada uno de los cuales se selecciona por referencia de subíndice o puntero indirecto.
Una estructura también es una colección de valores llamados sus miembros, pero los miembros de una estructura pueden tener diferentes tipos.
Se puede acceder a los subíndices de los elementos de la matriz mediante subíndices porque los elementos de la matriz tienen la misma longitud.
Sin embargo, este no es el caso en las estructuras. Dado que los miembros de una estructura tienen diferentes longitudes, no se puede acceder a ellos mediante subíndices, sino que cada miembro de la estructura tiene su propio nombre y se accede a ellos por nombre.

Antes de discutir la memoria de una estructura, veamos la declaración de la estructura:

Ejemplo 1: esta declaración crea una variable llamada x con tres miembros: un número entero, un carácter y un número de punto flotante.

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

Ejemplo 2: Esta declaración crea y y z. y es una matriz que contiene 20 estructuras. z es un puntero a una estructura de este tipo.

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

Las variables miembro de estas dos estructuras son las mismas, ¿son del mismo tipo?

La respuesta es no, el compilador trata estas dos declaraciones como dos tipos distintos. Es decir z=&x, es ilegal.

Entonces, la pregunta es, si quiero continuar usando el tipo declarado antes, ¿tengo que crearlo en una declaración separada?

La respuesta es no, puedes usar etiquetas. Las etiquetas permiten múltiples declaraciones para usar la misma lista de miembros. Como sigue:

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

Sin embargo, una mejor práctica para declarar estructuras es typedefcrear un nuevo tipo usando lo siguiente:

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

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

Ingrese al tema: Echemos un vistazo al almacenamiento real de la estructura en la memoria
. Veamos un ejemplo primero.

#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));
}

resultado de la operación:

0x61fef0
0x61fef1
0x61fef4
0x61fef8
16

inserte la descripción de la imagen aquí
¿Por qué esto es tan? Luego, debe averiguar las reglas de alineación del almacenamiento de memoria en la estructura:

1. El compilador asigna espacio para cada miembro uno por uno en el orden de la lista de miembros
2. La alineación de una estructura es la misma que la alineación máxima de sus elementos
3. El tamaño de una estructura es un múltiplo de su alineación
4 .K bytes de tamaño Los datos deben almacenarse en una dirección que sea un múltiplo entero de K

Cambia un poco este ejemplo:

#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

inserte la descripción de la imagen aquí

Entonces, ¿por qué alinearlo de esta manera? ¿No sería una pérdida de espacio?

Está relacionado con la forma en que la CPU maneja la memoria, lo que puede mejorar el rendimiento del sistema y acelerar la velocidad de acceso a los datos.

Entonces, ¿qué es la unión?

A diferencia de una estructura, cada miembro de una unión comparte una parte de la memoria, y solo uno de ellos se selecciona cuando se usa.

Almacenamiento en memoria de estructuras

El espacio ocupado depende del elemento más grande de la unión.

ver un ejemplo

#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));
}

Estructura de carrera:

0x61fef8
0x61fef8
0x61fef8
0x61fef8
8

Supongo que te gusta

Origin blog.csdn.net/weixin_47138646/article/details/122017208
Recomendado
Clasificación