una suma programacion matriz matrices llenar lenguaje float ejercicios como codigo bidimensionales ats arreglos c arrays initialization language-lawyer

suma - programacion ats matrices



ConfusiĆ³n sobre la inicializaciĆ³n de matriz en C (7)

En lenguaje C, si inicializa una matriz como esta:

int a[5] = {1,2};

entonces todos los elementos de la matriz que no se inicializan explícitamente se inicializarán implícitamente con ceros.

Pero, si inicializo una matriz como esta:

int a[5]={a[2]=1}; printf("%d %d %d %d %d/n", a[0], a[1],a[2], a[3], a[4]);

salida:

1 0 1 0 0

No entiendo, ¿por qué a[0] imprime 1 lugar de 0 ? ¿Es un comportamiento indefinido?

Nota: Esta pregunta fue hecha en una entrevista.


No entiendo, ¿por qué a[0] imprime 1 lugar de 0 ?

Presumiblemente, a[2]=1 inicializa a[2] primero, y el resultado de la expresión se usa para inicializar a[0] .

A partir de N2176 (borrador C17):

6.7.9 Inicialización

  1. Las evaluaciones de las expresiones de la lista de inicialización se secuencian de forma indeterminada entre sí y, por lo tanto, no se especifica el orden en el que aparecen los efectos secundarios. 154)

Así que parece que la salida 1 0 0 0 0 también hubiera sido posible.

Conclusión: no escriba inicializadores que modifiquen la variable inicializada sobre la marcha.


Creo que int a[5]={ a[2]=1 }; es un buen ejemplo para un programador que se dispara a sí mismo en su propio pie.

Podría sentir la tentación de pensar que lo que quisiste decir fue int a[5]={ [2]=1 }; que sería un elemento de configuración de inicializador designado C99 2 a 1 y el resto a cero.

En el raro caso de que realmente quisieras decir int a[5]={ 1 }; a[2]=1; int a[5]={ 1 }; a[2]=1; , entonces esa sería una forma divertida de escribirlo. De todos modos, esto es a lo que se refiere su código, aunque algunos de ellos señalaron que no está bien definido cuando la escritura a a[2] se ejecuta realmente. El problema aquí es que a[2]=1 no es un inicializador designado sino una asignación simple que a su vez tiene el valor 1.


Creo que el estándar C11 cubre este comportamiento y dice que el resultado no está especificado , y no creo que C18 haya realizado cambios relevantes en esta área.

El lenguaje estándar no es fácil de analizar. La sección relevante de la norma es §6.7.9 Inicialización . La sintaxis se documenta como:

initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designation opt initializer
initializer-list , designation opt initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier

Tenga en cuenta que uno de los términos es asignación-expresión , y como a[2] = 1 es indudablemente una expresión de asignación, se permite dentro de los inicializadores para matrices con una duración no estática:

§4 Todas las expresiones en un inicializador para un objeto que tenga una duración de almacenamiento de hilo o estática serán expresiones constantes o cadenas literales.

Uno de los párrafos clave es:

§19 La inicialización se producirá en el orden de la lista de inicializadores, cada inicializador proporcionado para un subobjeto en particular anulará cualquier inicializador previamente listado para el mismo subobjeto; 151) todos los subobjetos que no se inicializan explícitamente se inicializarán implícitamente de la misma manera que los objetos que tienen una duración de almacenamiento estático.

151) Cualquier inicializador para el subobjeto que se invalida y, por lo tanto, no se utiliza para inicializar ese subobjeto puede no ser evaluado en absoluto.

Y otro párrafo clave es:

§23 Las evaluaciones de las expresiones de la lista de inicialización se secuencian de manera indeterminada entre sí y, por lo tanto, no se especifica el orden en el que aparecen los efectos secundarios. 152)

152) En particular, el orden de evaluación no tiene que ser el mismo que el orden de inicialización del subobjeto.

Estoy bastante seguro de que el párrafo 23 indica que la notación en la pregunta:

int a[5] = { a[2] = 1 };

conduce a un comportamiento no especificado. La asignación a a[2] es un efecto secundario, y el orden de evaluación de las expresiones está secuenciado de forma indeterminada entre sí. En consecuencia, no creo que haya una manera de apelar a la norma y afirmar que un compilador en particular está manejando esto correctamente o incorrectamente.


Intento dar una respuesta corta y simple para el rompecabezas: int a[5] = { a[2] = 1 };

  1. Primero se establece a[2] = 1 . Eso significa que la matriz dice: 0 0 1 0 0
  2. Pero mira, dado que lo hiciste en los corchetes { } , que se usan para inicializar la matriz en orden, toma el primer valor (que es 1 ) y lo establece en a[0] . Es como si int a[5] = { a[2] }; Quedaría, donde ya tenemos a[2] = 1 . La matriz resultante es ahora: 1 0 1 0 0

Otro ejemplo: int a[6] = { a[3] = 1, a[4] = 2, a[5] = 3 }; - A pesar de que la orden es algo arbitraria, asumiendo que va de izquierda a derecha, iría en estos 6 pasos:

0 0 0 1 0 0 1 0 0 1 0 0 1 0 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 3 1 2 3 1 2 3


La asignación a[2]= 1 es una expresión que tiene el valor 1 , y esencialmente escribiste int a[5]= { 1 }; (con el efecto secundario de que a[2] se le asigna 1 ).


Mi Entendimiento es a[2]=1 devuelve el valor 1 para que el código se convierta en

int a[5]={a[2]=1} --> int a[5]={1}

int a[5]={1} asignar valor para a [0] = 1

De ahí que se imprima 1 para un [0]

Por ejemplo

char str[10]={‘H’,‘a’,‘i’}; char str[0] = ‘H’; char str[1] = ‘a’; char str[2] = ‘i;


TL; DR: No creo que el comportamiento de int a[5]={a[2]=1}; Está bien definido, al menos en C99.

Lo gracioso es que lo único que tiene sentido para mí es la parte sobre la que me pregunta: a[0] se establece en 1 porque el operador de asignación devuelve el valor asignado. Es todo lo demás que no está claro.

Si el código hubiera sido int a[5] = { [2] = 1 } , todo hubiera sido fácil: es un inicializador designado que establece a[2] a 1 y todo lo demás a 0 . Pero con { a[2] = 1 } tenemos un inicializador no designado que contiene una expresión de asignación, y caemos en un agujero de conejo.

Esto es lo que he encontrado hasta ahora:

  • a debe ser una variable local.

    6.7.8 Inicialización

    1. Todas las expresiones en un inicializador para un objeto que tiene una duración de almacenamiento estática serán expresiones constantes o literales de cadena.

    a[2] = 1 no es una expresión constante, por lo tanto, debe tener un almacenamiento automático.

  • a está en alcance en su propia inicialización.

    6.2.1 Alcances de los identificadores.

    1. Las etiquetas de estructura, unión y enumeración tienen un alcance que comienza justo después de la aparición de la etiqueta en un especificador de tipo que declara la etiqueta. Cada constante de enumeración tiene un alcance que comienza justo después de la aparición de su enumerador de definición en una lista de enumeradores. Cualquier otro identificador tiene un alcance que comienza justo después de completar su declarador.

    El declarador es a[5] , por lo que las variables están dentro del alcance de su propia inicialización.

  • a está vivo en su propia inicialización.

    6.2.4 Duraciones de almacenamiento de objetos.

    1. Un objeto cuyo identificador se declara sin vinculación y sin el static especificador de clase de almacenamiento tiene una duración de almacenamiento automática .

    2. Para un objeto de este tipo que no tiene un tipo de matriz de longitud variable, su tiempo de vida se extiende desde la entrada al bloque con el que está asociado hasta que la ejecución de ese bloque finaliza de alguna manera. (Entrar en un bloque cerrado o llamar a una función suspende, pero no finaliza, la ejecución del bloque actual). Si el bloque se ingresa recursivamente, se crea una nueva instancia del objeto cada vez. El valor inicial del objeto es indeterminado. Si se especifica una inicialización para el objeto, se realiza cada vez que se alcanza la declaración en la ejecución del bloque; de lo contrario, el valor se vuelve indeterminado cada vez que se alcanza la declaración.

  • Hay un punto de secuencia después de a[2]=1 .

    6.8 Declaraciones y bloques.

    1. Una expresión completa es una expresión que no forma parte de otra expresión o de un declarador. Cada uno de los siguientes es una expresión completa: un inicializador ; la expresión en una declaración de expresión; la expresión de control de una declaración de selección ( if o switch ); la expresión controladora de una sentencia while o do ; cada una de las expresiones (opcionales) de una declaración for ; La expresión (opcional) en una declaración de return . El final de una expresión completa es un punto de secuencia.

    Tenga en cuenta que, por ejemplo, en int foo[] = { 1, 2, 3 } la parte { 1, 2, 3 } es una lista de inicializadores con corchetes, cada uno de los cuales tiene un punto de secuencia después.

  • La inicialización se realiza en orden de lista de inicialización.

    6.7.8 Inicialización

    1. Cada lista de inicialización incluida en corchetes tiene un objeto actual asociado. Cuando no hay designaciones presentes, los subobjetos del objeto actual se inicializan en orden de acuerdo con el tipo del objeto actual: elementos de matriz en orden de subíndice creciente, miembros de estructura en orden de declaración y el primer miembro nombrado de una unión. [...]

    1. La inicialización se producirá en el orden de la lista de inicializadores; cada inicializador proporcionado para un subobjeto en particular anulará cualquier inicializador previamente listado para el mismo subobjeto; todos los subobjetos que no se inicializan explícitamente se inicializarán implícitamente de la misma manera que los objetos que tienen una duración de almacenamiento estático.
  • Sin embargo, las expresiones de inicialización no necesariamente se evalúan en orden.

    6.7.8 Inicialización

    1. El orden en que aparecen los efectos secundarios entre las expresiones de la lista de inicialización no se especifica.

Sin embargo, eso todavía deja algunas preguntas sin respuesta:

  • ¿Son los puntos de secuencia incluso relevantes? La regla básica es:

    6.5 Expresiones

    1. Entre el punto de secuencia anterior y el siguiente, un objeto tendrá su valor almacenado modificado a lo sumo una vez por la evaluación de una expresión . Además, el valor anterior se leerá solo para determinar el valor que se almacenará.

    a[2] = 1 es una expresión, pero la inicialización no lo es.

    Esto se contradice ligeramente en el Anexo J:

    J.2 Comportamiento indefinido

    • Entre dos puntos de secuencia, un objeto se modifica más de una vez, o se modifica y el valor anterior se lee para determinar el valor a almacenar (6.5).

    El Anexo J dice que cualquier modificación cuenta, no solo modificaciones por expresiones. Pero dado que los anexos no son normativos, probablemente podamos ignorarlo.

  • ¿Cómo se secuencian las inicializaciones de subobjetos con respecto a las expresiones de inicialización? ¿Se evalúan primero todos los inicializadores (en algún orden), luego los subobjetos se inicializan con los resultados (en el orden de la lista de inicializadores)? ¿O pueden intercalarse?

Creo que int a[5] = { a[2] = 1 } se ejecuta de la siguiente manera:

  1. El almacenamiento para a se asigna cuando se introduce su bloque que contiene. Los contenidos son indeterminados en este punto.
  2. Se ejecuta (solo) el inicializador ( a[2] = 1 ), seguido de un punto de secuencia. Esto almacena 1 en a[2] y devuelve 1 .
  3. Ese 1 se usa para inicializar a[0] (el primer inicializador inicializa el primer subobjeto).

Pero aquí las cosas se ponen borrosas porque se supone que los elementos restantes ( a[1] , a[2] , a[3] , a[4] ) se inicializan a 0 , pero no está claro cuándo: ¿Ocurre antes de a[2] = 1 es evaluado? Si es así, a[2] = 1 "ganaría" y sobrescribiría a[2] , pero ¿esa asignación tendría un comportamiento indefinido porque no hay un punto de secuencia entre la inicialización de cero y la expresión de asignación? ¿Son los puntos de secuencia incluso relevantes (ver arriba)? ¿O ocurre una inicialización cero después de que todos los inicializadores son evaluados? Si es así, a[2] debería terminar siendo 0 .

Debido a que el estándar de C no define claramente lo que sucede aquí, creo que el comportamiento no está definido (por omisión).