segmentation - "Int*nums={5, 2, 1, 4}" provoca un error de segmentación
segmentacion psicografica ejemplos de productos (5)
int *nums = {5, 2, 1, 4};
printf("%d/n", nums[0]);
causa un defecto de seguridad, mientras que
int nums[] = {5, 2, 1, 4};
printf("%d/n", nums[0]);
no lo hace Ahora:
int *nums = {5, 2, 1, 4};
printf("%d/n", nums);
impresiones 5.
En base a esto, he conjeturado que la notación de inicialización de matriz, {}, carga ciegamente estos datos en cualquier variable que esté a la izquierda. Cuando es int [], la matriz se llena como se desee. Cuando es int *, el puntero se llena con 5, y las ubicaciones de memoria después de donde se almacena el puntero se llenan con 2, 1 y 4. Por lo tanto, nums [0] intenta eliminar 5, causando una falla por defecto.
Si me equivoco, corrígeme. Y si estoy en lo correcto, explique, porque no entiendo por qué los inicializadores de matriz funcionan de la manera en que lo hacen.
Al asignar
{5, 2, 1, 4}
int *nums = {5, 2, 1, 4};
está asignando 5 a
nums
(después de una conversión implícita de int a puntero a int).
Si lo desmarca, realiza una llamada de acceso a la ubicación de memoria en
0x5
.
Es posible que su programa no pueda acceder.
Tratar
printf("%p", (void *)nums);
Hay una regla (estúpida) en C que dice que cualquier variable simple puede inicializarse con una lista de inicializadores encerrada entre llaves, como si fuera una matriz.
Por ejemplo, puede escribir
int x = {0};
, que es completamente equivalente a
int x = 0;
.
Entonces, cuando escribes
int *nums = {5, 2, 1, 4};
En realidad, está dando una lista de inicializador a una sola variable de puntero.
Sin embargo, es solo una variable única, por lo que solo se le asignará el primer valor 5, el resto de la lista se ignora (en realidad no creo que el código con un exceso de inicializadores incluso se compile con un compilador estricto), no escribir en la memoria en absoluto.
El código es equivalente a
int *nums = 5;
.
Lo que significa que los
nums
deben apuntar a la
dirección
5
.
En este punto, ya debería haber recibido dos advertencias / errores del compilador:
- Asignación de un entero al puntero sin un reparto.
- Elementos en exceso en la lista de inicializadores.
Y luego, por supuesto, el código se bloqueará y quemará, ya que
5
probablemente no sea una dirección válida a la que se le permite desreferenciar con
nums[0]
.
Como nota al margen, debe
printf
direcciones de puntero
printf
con el especificador
%p
o, de lo contrario, está invocando un comportamiento indefinido.
No estoy muy seguro de lo que está intentando hacer aquí, pero si desea establecer un puntero para apuntar a una matriz, debe hacer lo siguiente:
int nums[] = {5, 2, 1, 4};
int* ptr = nums;
// or equivalent:
int* ptr = (int[]){5, 2, 1, 4};
O si desea crear una matriz de punteros:
int* ptr[] = { /* whatever makes sense here */ };
EDITAR
Después de algunas investigaciones, puedo decir que la "lista de inicializadores de elementos excedentes" no es válida C, es una extensión GCC .
La inicialización estándar 6.7.9 dice (el énfasis es mío):
2 Ningún inicializador intentará proporcionar un valor para un objeto no contenido dentro de la entidad que se está inicializando.
/ - /
11 El inicializador para un escalar debe ser una sola expresión, opcionalmente encerrada entre llaves. El valor inicial del objeto es el de la expresión (después de la conversión); se aplican las mismas restricciones de tipo y conversiones que para la asignación simple, tomando el tipo del escalar como la versión no calificada de su tipo declarado.
"Tipo escalar" es un término estándar que se refiere a variables individuales que no son del tipo matriz, estructura o unión (se denominan "tipo agregado").
Entonces, en inglés simple, el estándar dice: "cuando inicializa una variable, siéntase libre de agregar algunos corchetes adicionales alrededor de la expresión del inicializador, solo porque puede".
int *nums = {5, 2, 1, 4};
Es un código mal formado.
Hay una extensión GCC que trata este código de la misma manera que:
int *nums = (int *)5;
intentando formar un puntero a la dirección de memoria 5. (Esto no me parece una extensión útil, pero supongo que la base de desarrolladores lo quiere).
Para evitar este comportamiento (o al menos obtener una advertencia), puede compilar en modo estándar, por ejemplo,
-std=c11 -pedantic
.
Una forma alternativa de código válido sería:
int *nums = (int[]){5, 2, 1, 4};
que apunta a un literal mutable de la misma duración de almacenamiento que
nums
.
Sin embargo, la versión
int nums[]
es generalmente mejor ya que usa menos almacenamiento y puede usar
sizeof
para detectar cuánto dura la matriz.
ESCENARIO 1
int *nums = {5, 2, 1, 4}; // <-- assign multiple values to a pointer variable printf("%d/n", nums[0]); // segfault
¿Por qué este segfault?
Usted declaró
nums
como un puntero a int, es decir, se supone que
nums
contiene la dirección de
un
entero en la memoria.
Luego trató de inicializar
nums
en una matriz de
múltiples
valores.
Entonces, sin profundizar en muchos detalles, esto es
conceptualmente incorrecto
: no tiene sentido asignar múltiples valores a una variable que se supone que contiene un valor.
En este sentido, verías exactamente el mismo efecto si haces esto:
int nums = {5, 2, 1, 4}; // <-- assign multiple values to an int variable
printf("%d/n", nums); // also print 5
En cualquier caso (asigne múltiples valores a un puntero o una variable int), lo que sucede entonces es que la variable obtendrá el primer valor que es
5
, mientras que los valores restantes se ignoran.
Este código cumple, pero recibirá advertencias para cada valor adicional que no se supone que está en la asignación:
warning: excess elements in scalar initializer
.
Para el caso de asignar múltiples valores a la variable de puntero, el programa falla cuando accede a
nums[0]
, lo que significa que está defendiendo lo que está almacenado en la
dirección 5
literalmente.
No asignó ninguna memoria válida para
nums
puntero en este caso.
Vale la pena señalar que no hay segfault para el caso de asignar múltiples valores a la variable int (no está haciendo referencia a ningún puntero no válido aquí).
ESCENARIO 2
int nums[] = {5, 2, 1, 4};
Este no es predeterminado, porque está asignando legalmente una matriz de 4 entradas en la pila.
ESCENARIO 3
int *nums = {5, 2, 1, 4};
printf("%d/n", nums); // print 5
Este no se divide por defecto como se esperaba, porque está imprimiendo el valor del puntero en sí , NO lo que está desreferenciando (que es el acceso no válido a la memoria).
Otros
Casi siempre está condenado a segfault cada vez que
codifica el valor de un puntero
como este (porque es la tarea del sistema operativo determinar qué proceso puede acceder a qué ubicación de memoria).
int *nums = 5; // <-- segfault
Por lo tanto, una regla general es siempre inicializar un puntero a la dirección de alguna variable asignada , como:
int a;
int *nums = &a;
o,
int a[] = {5, 2, 1, 4};
int *nums = a;
int *nums = {5, 2, 1, 4};
nums
es un puntero de tipo
int
.
Por lo tanto, debe hacer este punto a alguna ubicación de memoria válida.
num[0]
intenta desreferenciar alguna ubicación de memoria aleatoria y, por lo tanto, la falla de segmentación.
Sí, el puntero tiene el valor 5 e intenta desreferenciarlo, lo cual es un comportamiento indefinido en su sistema.
(Parece que
5
no es una ubicación de memoria válida en su sistema)
Mientras
int nums[] = {1,2,3,4};
es una declaración válida donde dice que
nums
es una matriz de tipo
int
y la memoria se asigna en función del número de elementos pasados durante la inicialización.