unir unidimensionales trabajar suma programacion mayor matrices encontrar elemento datos con como ats arreglos arreglo almacenar c++ c standards language-lawyer

unidimensionales - encontrar el elemento mayor de un arreglo c++



Tome la dirección de un elemento de matriz de un extremo al otro a través del subíndice: ¿legal por el estándar de C++ o no? (13)

Lo he visto afirmado varias veces ahora que el C ++ Standard no permite el siguiente código:

int array[5]; int *array_begin = &array[0]; int *array_end = &array[5];

Is &array[5] código legal de C ++ en este contexto?

Me gustaría una respuesta con una referencia al Estándar si es posible.

También sería interesante saber si cumple con el estándar C. Y si no es C ++ estándar, ¿por qué se tomó la decisión de tratarlo de manera diferente de array + 5 o de &array[4] + 1 ?


Además de las respuestas anteriores, señalaré el operador y puedo anularlas para las clases. Por lo tanto, incluso si fuera válido para POD, probablemente no sea una buena idea hacerlo para un objeto que usted sabe que no es válido (al igual que el operador de anulación & () en primer lugar).


Borrador de trabajo ( n2798 ):

"El resultado del operador unario es un puntero a su operando. El operando debe ser un lvalue o un id calificado. En el primer caso, si el tipo de expresión es" T ", el tipo de resultado es" puntero a T. "" (p.103)

array [5] no es un id calificado como lo mejor que puedo decir (la lista está en la página 87); lo más cercano parecería ser identificador, pero mientras array es una matriz de identificador [5] no lo es. No es un valor l porque "Un lvalue se refiere a un objeto o función" (p.76). array [5] obviamente no es una función, y no se garantiza que haga referencia a un objeto válido (porque la matriz + 5 está después del último elemento de matriz asignado).

Obviamente, puede funcionar en ciertos casos, pero no es válido en C ++ ni es seguro.

Nota: es legal agregar para pasar el conjunto (página 113):

"si la expresión P [un puntero] apunta al último elemento de un objeto de la matriz, la expresión (P) +1 señala uno pasado el último elemento del objeto de la matriz, y si la expresión Q señala uno pasado el último elemento de una matriz array, la expresión (Q) -1 apunta al último elemento del objeto de matriz. Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto de matriz, o uno más allá del último elemento del objeto de matriz, la evaluación no producirá un sobre flujo "

Pero no es legal hacerlo usando &.


Creo que esto es legal, y depende de la conversión ''lvalue to rvalue'' que tenga lugar. La última línea Núcleo std.dkuug.dk/JTC1/SC22/WG21/docs/cwg_active.html#232 tiene lo siguiente:

Acordamos que el enfoque en la norma parece estar bien: p = 0; *pag; no es intrínsecamente un error. Una conversión de lvalue a rvalue le daría un comportamiento indefinido

Aunque este es un ejemplo ligeramente diferente, lo que sí muestra es que el ''*'' no da como resultado la conversión de valor a valor real y, dado que la expresión es el operando inmediato de ''&'' que espera un valor l entonces se define el comportamiento.


Debe ser un comportamiento indefinido, por las siguientes razones:

  1. Intentar acceder a elementos fuera de límites genera un comportamiento indefinido. Por lo tanto, el estándar no prohíbe una implementación que arroje una excepción en ese caso (es decir, una implementación que verifica los límites antes de acceder a un elemento). Si & (array[size]) se definió como begin (array) + size , una implementación que arroje una excepción en caso de acceso fuera de límite ya no se ajustaría al estándar.

  2. Es imposible hacer que este rendimiento end (array) si la matriz no es una matriz sino más bien un tipo de colección arbitraria.


Es legal

De acuerdo con la documentación de gcc para C ++ , &array[5] es legal. Tanto en C ++ como en C , puede abordar con seguridad el elemento uno más allá del final de una matriz; obtendrá un puntero válido. Entonces, &array[5] ya que una expresión es legal.

Sin embargo, sigue siendo un comportamiento indefinido intentar derivar los punteros a la memoria no asignada, incluso si el puntero apunta a una dirección válida. Por lo tanto, intentar desreferenciar el puntero generado por esa expresión sigue siendo un comportamiento indefinido (es decir, ilegal) aunque el puntero mismo sea válido.

En la práctica, imagino que, por lo general, no causaría un choque.

Editar: dicho sea de paso, generalmente es así como se implementa el iterador final () para los contenedores STL (como un puntero a un extremo), por lo que es un buen testimonio de que la práctica es legal.

Edit: Oh, ahora veo que realmente no estás preguntando si mantener un puntero a esa dirección es legal, pero si esa forma exacta de obtener el puntero es legal. Voy a ceder a los otros respondedores sobre eso.


Estándar C ++, 5.19, párrafo 4:

Una expresión constante de dirección es un puntero a un lvalue .... El puntero se creará explícitamente, usando el operador unario ... o usando una expresión de matriz (4.2) ... tipo. El operador de suscripción [] ... puede usarse en la creación de una expresión de constante de dirección, pero no se podrá acceder al valor de un objeto mediante el uso de estos operadores. Si se utiliza el operador de suscripción, uno de sus operandos será una expresión de constante integral.

Me parece que & array [5] es legal C ++, siendo una expresión constante de dirección.


Esto es legal:

int array[5]; int *array_begin = &array[0]; int *array_end = &array[5];

Sección 5.2.1 Suscripción La expresión E1 [E2] es idéntica (por definición) a * ((E1) + (E2))

Entonces, con esto podemos decir que array_end también es equivalente:

int *array_end = &(*((array) + 5)); // or &(*(array + 5))

Sección 5.3.1.1 Operador unario ''*'': el operador unario * realiza indirección: la expresión a la que se aplica debe ser un puntero a un tipo de objeto, o un puntero a un tipo de función y el resultado es un valor l referente al objeto o función a la que apunta la expresión. Si el tipo de expresión es "puntero a T", el tipo de resultado es "T". [Nota: un puntero a un tipo incompleto (que no sea cv void) puede desreferenciarse. El valor l así obtenido se puede usar de manera limitada (para inicializar una referencia, por ejemplo); este lvalue no se debe convertir a un valor r, ver 4.1. - nota final]

La parte importante de lo anterior:

''el resultado es un valor l referente al objeto o función''.

El operador unario ''*'' está devolviendo un valor l referente al int (no de-refeference). El operador unario ''&'' obtiene la dirección del lvalue.

Siempre que no haya desreferencia de un puntero fuera de límites, la operación queda totalmente cubierta por el estándar y se define todo el comportamiento. Entonces, según mi lectura, lo anterior es completamente legal.

El hecho de que muchos de los algoritmos de STL dependen de que el comportamiento esté bien definido es una especie de insinuación de que el comité de estándares ya ha pensado en esto y estoy seguro de que hay algo que lo cubre explícitamente.

La sección de comentarios a continuación presenta dos argumentos:

(Por favor, lea: pero es largo y los dos terminamos trollish)

Argumento 1

esto es ilegal debido a la sección 5.7 párrafo 5

Cuando una expresión que tiene un tipo integral se agrega o se resta de un puntero, el resultado tiene el tipo del operando del puntero. Si el operando puntero apunta a un elemento de un objeto de matriz, y la matriz es lo suficientemente grande, el resultado apunta a un desplazamiento de elemento desde el elemento original de modo que la diferencia de los subíndices de los elementos de matriz resultantes y originales es igual a la expresión integral. En otras palabras, si la expresión P apunta al elemento i-ésimo de un objeto de matriz, las expresiones (P) + N (equivalentemente, N + (P)) y (P) -N (donde N tiene el valor n) señalan a, respectivamente, los elementos i + n-ésimo e i-n-ésimo del objeto de matriz, siempre que existan. Además, si la expresión P apunta al último elemento de un objeto de matriz, la expresión (P) +1 señala uno pasado el último elemento del objeto de matriz, y si la expresión Q señala un último elemento de un objeto de matriz, la expresión (Q) -1 apunta al último elemento del objeto de la matriz. Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto del arreglo, o uno más allá del último elemento del objeto del arreglo, la evaluación no producirá un desbordamiento; de lo contrario, el comportamiento no está definido.

Y aunque la sección es relevante; no muestra un comportamiento indefinido Todos los elementos de la matriz de la que estamos hablando están dentro de la matriz o uno más allá del final (que está bien definido en el párrafo anterior).

Argumento 2:

El segundo argumento presentado a continuación es: * es el operador de referencia.
Y aunque este es un término común usado para describir el operador ''*''; este término se evita deliberadamente en el estándar ya que el término "desreferencia" no está bien definido en términos del lenguaje y lo que eso significa para el hardware subyacente.

Aunque acceder a la memoria uno más allá del final de la matriz definitivamente es un comportamiento indefinido. No estoy seguro de que el unary * operator acceda a la memoria (lee / escribe en la memoria) en este contexto (no de la manera que define el estándar). En este contexto (como se define en el estándar (ver 5.3.1.1)) el unary * operator devuelve un valor lvalue referring to the object . En mi comprensión del lenguaje, esto no es acceso a la memoria subyacente. El resultado de esta expresión es inmediatamente utilizado por el unary & operator que devuelve la dirección del objeto al que hace referencia el valor lvalue referring to the object .

Se presentan muchas otras referencias a Wikipedia y fuentes no canónicas. Todo lo cual me parece irrelevante. C ++ está definido por el estándar .

Conclusión:

Estoy dispuesto a admitir que hay muchas partes del estándar que no he considerado y que pueden probar que mis argumentos anteriores están equivocados. NON se proporcionan a continuación. Si me muestra una referencia estándar que muestra que esto es UB. voy a

  1. Deja la respuesta.
  2. Poner todo en mayúsculas es estúpido y estoy equivocado para que todos lo lean.

Esto no es un argumento:

No todo en el mundo entero está definido por el estándar C ++. Abre tu mente.


Incluso si es legal, ¿por qué apartarse de la convención? array + 5 es más corto de todos modos, y en mi opinión, más legible.

Editar: si lo quiere por simétrica, puede escribir

int* array_begin = array; int* array_end = array + 5;


No creo que sea ilegal, pero sí creo que el comportamiento de & array [5] no está definido.

  • 5.2.1 [expr.sub] E1 [E2] es idéntico (por definición) a * ((E1) + (E2))

  • 5.3.1 [expr.unary.op] unario * operador ... el resultado es un valor l que se refiere al objeto o función a la que apunta la expresión.

En este punto tiene un comportamiento indefinido porque la expresión ((E1) + (E2)) en realidad no apuntó a un objeto y el estándar dice cuál debería ser el resultado, a menos que lo haga.

  • 1.3.12 [defns.undefined] También se puede esperar un comportamiento indefinido cuando esta Norma Internacional omite la descripción de cualquier definición explícita de comportamiento.

Como se señaló en otra parte, array + 5 y &array[0] + 5 son formas válidas y bien definidas de obtener un puntero más allá del final de la matriz.


Si, es legal Del proyecto de norma C99 :

§6.5.2.1, párrafo 2:

Una expresión de postfijo seguida de una expresión entre corchetes [] es una designación con subíndice de un elemento de un objeto de matriz. La definición del operador de subíndice [] es que E1[E2] es idéntico a (*((E1)+(E2))) . Debido a las reglas de conversión que se aplican al operador binario + , si E1 es un objeto de matriz (de manera equivalente, un puntero al elemento inicial de un objeto de matriz) y E2 es un número entero, E1[E2] designa el elemento E2 -ésimo de E1 (contando desde cero).

§6.5.3.2, párrafo 3 (énfasis mío):

El operador unario arroja la dirección de su operando. Si el operando tiene el tipo '''' tipo '''', el resultado tiene el tipo '''' puntero para escribir ''''. Si el operando es el resultado de un operador unario * , ni ese operador ni el operador & se evalúan y el resultado es como si ambos se hubieran omitido, excepto que las restricciones sobre los operadores todavía se aplican y el resultado no es un valor l. De manera similar, si el operando es el resultado de un operador [] , ni el operador & ni el unario * implícito en [] se evalúa y el resultado es como si el operador & hubiera eliminado y el operador [] se hubiera cambiado a un operador + De lo contrario, el resultado es un puntero al objeto o función designado por su operando.

§6.5.6, párrafo 8:

Cuando una expresión que tiene un tipo de entero se agrega o se resta de un puntero, el resultado tiene el tipo del operando del puntero. Si el operando puntero apunta a un elemento de un objeto de matriz, y la matriz es lo suficientemente grande, el resultado apunta a un desplazamiento de elemento desde el elemento original tal que la diferencia de los subíndices de los elementos de matriz resultante y original es igual a la expresión entera. En otras palabras, si la expresión P apunta al i -ésimo elemento de un objeto de matriz, las expresiones (P)+N (equivalentemente, N+(P) ) y (P)-N (donde N tiene el valor n ) señalan a, respectivamente, los elementos i+n -ésimo e i−n ésimo del objeto de matriz, siempre que existan. Además, si la expresión P apunta al último elemento de un objeto de matriz, la expresión (P)+1 señala uno pasado el último elemento del objeto de matriz, y si la expresión Q señala un último elemento de un objeto de matriz, la expresión (Q)-1 apunta al último elemento del objeto de la matriz. Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto del arreglo, o uno más allá del último elemento del objeto del arreglo, la evaluación no producirá un desbordamiento; de lo contrario, el comportamiento no está definido. Si el resultado señala uno pasado el último elemento del objeto de la matriz, no se utilizará como el operando de un operador unario * que se evalúa.

Tenga en cuenta que la norma permite explícitamente que los punteros señalen un elemento más allá del final de la matriz, siempre que no se eliminen las referencias . Según 6.5.2.1 y 6.5.3.2, la expresión &array[5] es equivalente a &*(array + 5) , que es equivalente a (array+5) , que apunta una pasada al final de la matriz. Esto no da como resultado una desreferencia (según 6.5.3.2), por lo que es legal.


Su ejemplo es legal, pero solo porque no está usando un puntero fuera de límites.

Tratemos primero con los punteros fuera de límites (porque así es como originalmente interpreté su pregunta, antes de notar que el ejemplo usa un puntero de un solo extremo):

En general, ni siquiera está autorizado a crear un puntero fuera de los límites. Un puntero debe apuntar a un elemento dentro de la matriz, o uno más allá del final . En ningún otro lugar.

Ni siquiera se permite que el puntero exista, lo que significa que obviamente no se te permite desreferenciarlo tampoco.

Esto es lo que el estándar tiene que decir sobre el tema:

5.7: 5:

Cuando una expresión que tiene un tipo integral se agrega o se resta de un puntero, el resultado tiene el tipo del operando del puntero. Si el operando puntero apunta a un elemento de un objeto de matriz, y la matriz es lo suficientemente grande, el resultado apunta a un desplazamiento de elemento desde el elemento original de modo que la diferencia de los subíndices de los elementos de matriz resultantes y originales es igual a la expresión integral. En otras palabras, si la expresión P apunta al elemento i-ésimo de un objeto de matriz, las expresiones (P) + N (equivalentemente, N + (P)) y (P) -N (donde N tiene el valor n) señalan para, respectivamente, los elementos i + n-th e i-n-th del objeto de matriz, siempre que existan. Además, si la expresión P apunta al último elemento de un objeto de matriz, la expresión (P) +1 señala uno pasado el último elemento del objeto de matriz, y si la expresión Q señala un último elemento de un objeto de matriz, la expresión (Q) -1 apunta al último elemento del objeto de la matriz. Si tanto el operando del puntero como el resultado apuntan a elementos del mismo objeto de la matriz, o uno más allá del último elemento del objeto de la matriz, la evaluación no producirá un sobreflujo; de lo contrario, el comportamiento está indefinido .

(énfasis mío)

Por supuesto, esto es para el operador +. Entonces, para estar seguros, esto es lo que dice el estándar sobre la matriz de suscripción:

5.2.1: 1:

La expresión E1[E2] es idéntica (por definición) a *((E1)+(E2))

Por supuesto, hay una advertencia obvia: su ejemplo en realidad no muestra un puntero fuera de los límites. usa un puntero "pasado", que es diferente. Se permite que el puntero exista (como dice lo anterior), pero el estándar, por lo que puedo ver, no dice nada sobre desreferenciarlo. Lo más cerca que puedo encontrar es 3.9.2: 3:

[Nota: por ejemplo, se consideraría que la dirección anterior al final de una matriz (5.7) apunta a un objeto no relacionado del tipo de elemento de la matriz que podría estar ubicado en esa dirección. -finalizar nota]

Lo cual me parece que implica que sí, legalmente puede desreferenciarlo, pero el resultado de leer o escribir en la ubicación no está especificado.

Gracias a ilproxyil por corregir el último bit aquí, respondiendo la última parte de su pregunta:

  • array + 5 realidad no desreferencia nada, simplemente crea un puntero a uno pasado el final de la array .
  • &array[4] + 1 array+4 referencias &array[4] + 1 referencias array+4 (que es perfectamente segura), toma la dirección de ese valor l, y agrega una a esa dirección, lo que da como resultado un puntero pasado (pero ese puntero nunca se desreferencia .
  • &array[5] desreferencias array + 5 (que, por lo que puedo ver, es legal y da como resultado "un objeto no relacionado del tipo de elemento de la matriz", como se dijo anteriormente), y luego toma la dirección de ese elemento, que también parece lo suficientemente legal

Entonces no hacen lo mismo, aunque en este caso, el resultado final es el mismo.


If your example is NOT a general case but a specific one, then it is allowed. You can legally , AFAIK, move one past the allocated block of memory. It does not work for a generic case though ie where you are trying to access elements farther by 1 from the end of an array.

Just searched C-Faq : link text


It is perfectly legal.

The vector<> template class from the stl does exactly this when you call myVec.end(): it gets you a pointer (here as an iterator) which points one element past the end of the array.