2010-01-10 18 views
14

Consideremos el siguiente código:validez del código

void populate(int *arr) 
{ 
    for(int j=0;j<4;++j) 
     arr[j]=0; 
} 

int main() 
{ 
    int array[2][2]; 
    populate(&array[0][0]); 
} 

Hubo una discusión con respecto a esto en una comunidad local si el código es válido o no (¿Se supone que mencionar su nombre?). Un tipo estaba diciendo que invoca UB porque viola

C++ estándar ($ 5.7/5 [expr.add])

"Si tanto el operando puntero y el punto resultado a los elementos de la misma array, o uno pasado el último elemento del objeto de matriz, la evaluación no producirá un desbordamiento; ; de lo contrario, el comportamiento no está definido. "

Pero no veo nada de malo en el código, el código está perfectamente bien para mí.

Entonces, solo quiero saber si este código es válido o no? ¿Me estoy perdiendo de algo?

+0

+1 buena pregunta –

Respuesta

15

Su array es dos matrices de int[2], mientras que su función populate() lo trata como una única matriz de int[4]. Dependiendo de cómo exactamente el compilador decida alinear los elementos de array, esto puede no ser una suposición válida.

Específicamente, cuando j es 2 y se intenta acceder a arr[2], esto es fuera de los límites de main 's array[0] y es, por tanto, inválida.

+0

Pero cuando accede a 'arr [2]', 'arr' no es una" matriz [2] de nada ", es un puntero. No estoy seguro de si este argumento se aplica. –

+3

@Alok: en C++, las matrices multidimensionales son diferentes a las matrices de punteros. Java solo tiene el último, pero C y C++ tienen ambas formas. –

+0

@Greg, OK. En C, no hay nada como "matrices multidimensionales", solo hay arreglos (que a su vez pueden estar compuestos por matrices, etc.). No estoy seguro si estaba implicando que C++ tiene matrices multidimensionales verdaderas. –

3

Eso no siempre va a funcionar. C tiene matrices de matrices, no matrices 2D. Las sub-matrices no siempre se especifican para que sean contiguas en la memoria (las matrices estáticas pueden ser, verifique el estándar C/C++) En este ejemplo en particular, sospecho que funciona correctamente. Sin embargo, si hubiera asignado dinámicamente la memoria que se está transfiriendo, posiblemente fallaría, ya que malloc (o nuevo) podría haber separado los subarrays.

Si, sin embargo, desea caminar linealmente hacia abajo en la memoria '2d', puede construir un acceso 2D en una matriz 1D y funcionará bien y cosas como memset funcionarán contra la matriz 1D.

+1

Las matrices deben tener memoria contigua (sección 6.2.5, viñeta 10 en el estándar ISO C99). Y si las matrices multidimensionales están definidas como matrices de matrices, ¿no implica eso que también son contiguas (la n + 1ra matriz debe seguir inmediatamente la n-ésima matriz en la memoria)? – jamesdlin

+0

Vaya, me refiero a la viñeta 20. – jamesdlin

+0

Deben ser contiguos, pero puede haber un relleno definido por la implementación entre ellos. Sin embargo, no estoy al tanto de que esto sea un problema para 'int' en ningún compilador que haya usado. –

9

Tiene una matriz de matrices. array[1] sigue array[0] en memoria, porque las matrices son contiguas. Si p == array[0], entonces p[1] sigue p[0], porque las matrices son contiguas. Entonces, tienes razón: toda la memoria para array es contigua.

En las imágenes, array se parece así.

+-----------------+-----------------+ 
|  [0]  |  [1]  | 
+-----------------+-----------------+ 

Ahora, vamos a romper array[0] y array[1], se ven de forma individual como esto:

+--------+--------+ 
| [0] | [1] |   
+--------+--------+ 

Por lo tanto, la imagen final es:

+--------+--------+--------+--------+ 
| [0][0] | [0][1] | [1][0] | [1][1] | 
+--------+--------+--------+--------+ 

Ahora, la pregunta es, ¿puede accedes a esta memoria contigua tal como eres. La respuesta es que no está garantizada por el estándar.Las matrices son contiguas, pero el estándar no permite la indexación de la forma en que lo ha hecho. En otras palabras:

&array[0][0]+2 == &array[1][0], pero (&a[0][0] + 2) + 1 no está definido, mientras que &a[1][0] + 1 es válido. Si esto parece extraño, lo es, pero según la cita que publicaste del estándar, solo puedes calcular un puntero que se encuentre dentro de una matriz o como mucho después de la matriz (sin desreferenciar ese puntero "anterior") .

En la práctica, dudo que esto fallaría en cualquier lugar, pero al menos de acuerdo con la norma, su código no es válido debido a un comportamiento indefinido.

Consulte this post on comp.lang.c también.

+0

¿No debería ser el último elemento de la tercera imagen "[1] [1]"? – vobject

+0

La pregunta no es si la memoria es contigua (lo es claramente), sino si el comportamiento está bien definido. No veo cómo esto puede ser "medio correcto". Él pregunta si es UB, la respuesta es sí. El hecho de que generalmente funcione no lo hace menos indefinido. – jalf

+1

jalf, tienes razón. Cuando dije "medio a la derecha", quise decir que algunas personas pensaron que la memoria no necesita ser contigua, por lo tanto, el comportamiento no está definido. Por otro lado, muchas otras personas pensaban que la memoria debe ser continua, por lo que el comportamiento está bien definido. –

0

En C todo se almacena en segmentos de memoria lineal. Está pasando la dirección de a[0][0] que sería igual que la dirección de a[0] así que a[i][j] es lo mismo que a[i*ColSize+j] porque todo se almacena linealmente. Pero si asigna dinámicamente la memoria, fallaría porque, en ese momento, es posible que no se almacenen todas las filas en una ubicación contigua. entonces a[i][j] sería *(&a[i]+j).

+0

No, NO se requiere que las matrices estén almacenadas linealmente por el estándar. Si se declara como una matriz bidimensional, se trata de una matriz bidimensional, no una matriz única. lo suficientemente grande como para hol d todas las sub-matrices. –

+1

BillyONeal, una matriz de matrices sigue siendo una matriz y sus elementos se almacenan contiguamente. – avakar

Cuestiones relacionadas