una referencia recibir recibe que por pasar parametro matriz llenar funciones funcion enviar con como bidimensionales arreglos c pointers language-lawyer undefined-behavior

recibir - pasar una matriz por referencia en c



¿Es legal llamar a memcpy con longitud cero en un puntero justo después del final de una matriz? (3)

3.15 objeto

  1. región de objeto del almacenamiento de datos en el entorno de ejecución, cuyos contenidos pueden representar valores

La memoria, el puntero a uno más allá de los puntos del último elemento, de un objeto de matriz o un objeto no puede representar valores, ya que no se puede eliminar la referencia (6.5.6 Operadores aditivos, párrafo 8).

7.24.2.1 La función memcpy

  1. La función memcpy copia n caracteres del objeto al que apunta s2 en el objeto al que apunta s1. Si la copia tiene lugar entre objetos que se superponen, el comportamiento no está definido.

Los punteros pasados ​​a memcpy deben apuntar a un objeto.

6.5.3.4 Los operadores sizeof y _Alignof

  1. Cuando se aplica sizeof a un operando que tiene el tipo char, unsigned char, o signed char, (o una versión calificada del mismo) el resultado es 1. Cuando se aplica a un operando que tiene un tipo de matriz, el resultado es el número total de bytes en la matriz . Cuando se aplica a un operando que tiene estructura o tipo de unión, el resultado es el número total de bytes en dicho objeto , incluido el relleno interno y posterior.

sizeof operator no cuenta el elemento de un pasado como el objeto, ya que no cuenta para el tamaño del objeto. Sin embargo, claramente da el tamaño de todo el objeto.

6.3.2.1 Valores, matrices y designadores de funciones

  1. Un lvalue es una expresión (con un tipo de objeto distinto de vacío) que potencialmente designa un objeto; 64) si un lvalue no designa un objeto cuando se evalúa, el comportamiento no está definido.

Yo sostengo que el puntero pasado a un objeto de matriz o un objeto, a los cuales se les permite señalar, no representa un objeto.

int a ; int* p = a+1 ;

p está definido, pero no apunta a un objeto ya que no se puede anular la referencia, la memoria a la que apunta no puede representar un valor y sizeof no cuenta esa memoria como parte del objeto. Memcpy requiere un puntero a un objeto.

Por lo tanto, pasar un puntero pasado a memcpy provoca un comportamiento indefinido.

Actualizar:

Esta parte también apoya la conclusión:

6.5.9 Operadores de igualdad

  1. Dos punteros se comparan igual si y solo si ambos son punteros nulos, ambos son punteros al mismo objeto (incluido un puntero a un objeto y un subobjeto al principio) o función, ambos son punteros al pasado del último elemento de la misma matriz objeto, o uno es un puntero a uno más allá del final de un objeto de matriz y el otro es un puntero al inicio de un objeto de matriz diferente que sucede a seguir inmediatamente el primer objeto de matriz en el espacio de direcciones.

Esto implica que el puntero a un objeto si se incrementa a uno más allá de un objeto, puede apuntar a un objeto diferente. En ese caso, ciertamente no puede apuntar al objeto al que apuntó originalmente, mostrando que el puntero que pasa por un objeto no apunta a un objeto.

Como se responde en otra parte , las funciones de llamada como memcpy con punteros nulos o no válidos son un comportamiento indefinido, incluso si el argumento de longitud es cero. En el contexto de dicha función, especialmente memcpy y memmove , ¿es un puntero justo después del final de la matriz un puntero válido?

Estoy haciendo esta pregunta porque es legal obtener un puntero justo después del final de una matriz (a diferencia de, por ejemplo, un puntero dos elementos más allá del final de una matriz) pero no se le permite desreferenciarla, sin embargo, la nota 106 de ISO 9899: 2011 indica que dicho puntero apunta al espacio de direcciones del programa, un criterio requerido para que un puntero sea válido de acuerdo con §7.1.4.

Tal uso ocurre en el código donde quiero insertar un elemento en el medio de una matriz, requiriéndome mover todos los elementos después del punto de inserción:

void make_space(type *array, size_t old_length, size_t index) { memmove(array + index + 1, array + index, (old_length - index) * sizeof *array); }

Si queremos insertar al final de la matriz, el index es igual a la length y la array + index + 1 puntos justo después del final de la matriz, pero el número de elementos copiados es cero.


Pasar el puntero del final al primer argumento de memmove tiene varios escollos, lo que probablemente resulte en un ataque de un demonio nasal. Estrictamente hablando, no existe una garantía impermeable para que eso esté bien definido.

(Desafortunadamente, no hay mucha información sobre el concepto "más allá del último elemento" en el estándar).

Nota: Lamento tener la otra dirección ahora ...

La pregunta básica es si el "uno más allá del puntero final" es un primer argumento de función válido para memmove si se memmove 0 bytes:

T array[length]; memmove(array + length, array + length - 1u, 0u);

El requisito en cuestión es la validez del primer argumento.

N1570, 7.1.4, 1

Si un argumento de función se describe como una matriz, el puntero que se pasa realmente a la función tendrá un valor tal que todos los cálculos de direcciones y accesos a objetos (eso sería válido si el puntero apuntara al primer elemento de tal matriz) de hecho son válidos.

Si un argumento de una función tiene un valor no válido (como un valor fuera del dominio de la función, o un puntero fuera del espacio de direcciones del programa, o un puntero nulo, o un puntero al almacenamiento no modificable cuando el parámetro correspondiente no está calificado por completo) o un tipo (después de la promoción) no esperado por una función con un número variable de argumentos, el comportamiento no está definido.

Haciendo el argumento válido si el puntero

  1. no está fuera del espacio de direcciones,
  2. no es un puntero nulo,
  3. no es un puntero a la memoria const

y si el tipo de argumento

  1. no es de tipo array.

1. Espacio de direcciones

N1570, 6.5.6, 8

Además, si la expresión P apunta al último elemento de un objeto de matriz, la expresión (P) +1 apunta uno más allá del último elemento del objeto de matriz, y si la expresión Q apunta uno más allá del último elemento de un objeto de matriz, la expresión (Q) -1 apunta al último elemento del objeto de matriz.

N1570, 6.5.6, 9

Además, si la expresión P apunta a un elemento de un objeto de matriz o una más allá del último elemento de un objeto de matriz, y la expresión Q apunta al último elemento del mismo objeto de matriz, la expresión ((Q) +1) - (P) tiene el mismo valor que ((Q) - (P)) + 1 y como - ((P) - ((Q) +1)), y tiene el valor cero si la expresión P apunta una más allá de último elemento del objeto de matriz, aunque la expresión (Q) +1 no apunta a un elemento del objeto de matriz. 106

106 Otra forma de aproximarse a la aritmética de punteros es convertir primero el puntero (s) en puntero (s) de carácter: En este esquema, la expresión de entero agregada o sustraída del puntero convertido se multiplica primero por el tamaño del objeto originalmente apuntado, y el puntero resultante se convierte de nuevo al tipo original. Para la sustracción de punteros, el resultado de la diferencia entre los punteros de caracteres se divide de manera similar por el tamaño del objeto originalmente apuntado.

Cuando se ve de esta manera, una implementación solo necesita proporcionar un byte adicional (que puede superponerse a otro objeto en el programa) justo después del final del objeto para satisfacer los requisitos de "pasado el último elemento".

Aunque la nota a pie de página no es normativa, como señala Lundin, aquí tenemos una explicación de que "una implementación solo necesita proporcionar un byte adicional". Aunque, no puedo demostrarlo, supongo que este es un indicio de que los medios estándar requieren que la implementación incluya memoria dentro del espacio de direcciones de los programas en la ubicación señalada por el puntero final.

2. Puntero nulo

El pasado el puntero final no es un puntero nulo.

3. Apuntando a la memoria constante.

El estándar no impone requisitos adicionales en el pasado del puntero final, aparte de proporcionar información sobre el resultado de varias operaciones y la nota al pie (de nuevo, no normal;) aclara que puede superponerse con otro objeto. Por lo tanto, no hay garantía de que la memoria pasada el punto al que apunta el puntero no sea constante. Dado que el primer argumento de memove es un puntero a la memoria no constante, no se garantiza que pasar el puntero final sea válido y un comportamiento potencialmente indefinido.

4. Validez de los argumentos de matriz.

El Capítulo 7.21.1 describe el encabezado de gestión de cadenas <string.h> y los estados de la primera cláusula:

El encabezado declara un tipo y varias funciones, y define una macro útil para manipular matrices de tipo de carácter y otros objetos tratados como matrices de tipo de carácter.

No creo que el estándar sea muy claro aquí si los "objetos tratados como matrices de tipo de carácter" se refieren a las funciones o solo a la macro. Si esta oración realmente implica que memove trata el primer argumento como una matriz de caracteres, el comportamiento de pasar el puntero final al memmove es un comportamiento indefinido según 7.1.4 (que requiere un puntero a un objeto válido).


Si nos fijamos en el estándar C99 , hay esto:

7.21.1.p2

Cuando un argumento declarado como size_t n especifica la longitud de la matriz para una función, n puede tener el valor cero en una llamada a esa función. A menos que se indique explícitamente lo contrario en la descripción de una función particular en esta subcláusula, los argumentos de puntero en dicha llamada seguirán teniendo valores válidos, como se describe en 7.1.4. En tal llamada, una función que localiza un carácter no encuentra ocurrencia, una función que compara dos secuencias de caracteres devuelve cero, y una función que copia caracteres copia cero caracteres. ...

No hay una declaración explícita en la descripción de memcpy en 7.21.2.1

7.1.4.p1

... Si un argumento de función se describe como una matriz, el puntero que se pasa realmente a la función tendrá un valor tal que todos los cálculos de direcciones y accesos a objetos (sería válido si el puntero apuntara al primer elemento de tales una matriz) son de hecho válidas .

Énfasis añadido. Parece que los punteros deben apuntar a ubicaciones válidas (en el sentido de desreferenciación), y los párrafos sobre aritmética de punteros que permiten apuntar al final + 1 no se aplican aquí.

Existe la pregunta de si los argumentos para memcpy son matrices o no. Por supuesto que no se declaran como matrices, pero

7.21.1.p1 dice

El encabezado string.h declara un tipo y varias funciones, y define una macro útil para manipular matrices de tipo de carácter y otros objetos tratados como matrices de tipo de carácter .

y memcpy está en string.h .
Así que supongo que memcpy trata los argumentos como matrices de caracteres. Debido a que la macro mencionada es NULL , la parte "útil para ..." de la oración se aplica claramente a las funciones.