Ahora que tenemos std :: array, ¿qué usos quedan para las matrices de estilo C?

89

std::arrayes muy superior a las matrices C. E incluso si quiero interoperar con código heredado, puedo usar std::array::data(). ¿Hay alguna razón por la que alguna vez quisiera una matriz de la vieja escuela?

R. Martinho Fernandes
fuente

Respuestas:

60

A menos que me haya perdido algo (no he seguido demasiado de cerca los cambios más recientes en el estándar), la mayoría de los usos de las matrices de estilo C aún permanecen. std::arraypermite la inicialización estática, pero aún así no contará los inicializadores por usted. Y dado que el único uso real de las matrices de estilo C antes std::arrayera para tablas inicializadas estáticamente en la línea de:

MyStruct const table[] =
{
    { something1, otherthing1 },
    //  ...
};

utilizando las funciones habituales beginy de endplantilla (adoptadas en C ++ 11) para iterar sobre ellas. Sin mencionar el tamaño, que el compilador determina a partir del número de inicializadores.

EDITAR: Otra cosa que olvidé: los literales de cadena siguen siendo matrices de estilo C; es decir, con tipo char[]. No creo que nadie excluya el uso de cadenas literales solo porque lo hemos hecho std::array.

James Kanze
fuente
7
Puede escribir una plantilla de función variada que construya matrices sin tener que especificar la longitud.
derecha
2
Con C ++ 17 Class Template Deduction, se admite la deducción automática del número de inicializadores. Por ejemplo, "auto a = std :: array {1, 2, 3};"
Ricky65
Nitpick: el tipo de cadenas literales esconst char[]
Bulletmagnet
31

No. Para, eh, decirlo sin rodeos. Y en 30 caracteres.

Por supuesto, necesita arreglos C para implementar std::array, pero esa no es realmente una razón por la que un usuario alguna vez querría arreglos C. Además, no, std::arrayno es menos eficaz que una matriz C y tiene una opción para un acceso con límites comprobados. Y finalmente, es completamente razonable que cualquier programa de C ++ dependa de la biblioteca estándar, ese es el punto de que sea estándar, y si no tiene acceso a una biblioteca estándar, entonces su compilador no es conforme y el La pregunta está etiquetada como "C ++", no "C ++ y esas cosas que no son C ++ que pierden la mitad de la especificación porque lo consideran inapropiado".

Perrito
fuente
1
Hm. ¿Qué sucede si está escribiendo código C ++ que se llama desde otro idioma y necesita pasar algo como parámetro?
asveikau
3
Se permite que las implementaciones independientes omitan casi toda la biblioteca estándar y aún así sean compatibles. Sin embargo, tendría serias dudas sobre un compilador que no podría implementar std::arrayen una implementación independiente de C ++ 11.
Dennis Zickefoose
11
Borrador final de C ++ 0x (documento N3092) § 1.4.7 "Se definen dos tipos de implementaciones: alojada y autónoma. Para una implementación alojada, esta Norma Internacional define el conjunto de bibliotecas disponibles. Una implementación independiente es aquella en la que la ejecución puede tiene lugar sin el beneficio de un sistema operativo y tiene un conjunto de bibliotecas definido por la implementación que incluye ciertas bibliotecas de soporte de lenguaje ". El STL no se incluye como una biblioteca de" soporte de lenguaje "en un compilador independiente
Earlz
24

Parece que usar matrices multidimensionales es más fácil con matrices C que std::array. Por ejemplo,

char c_arr[5][6][7];

Opuesto a

std::array<std::array<std::array<char, 7>, 6>, 5> cpp_arr;

También debido a la propiedad de decaimiento automático de las matrices de C, c_arr[i]en el ejemplo anterior decaerá a un puntero y solo necesita pasar las dimensiones restantes como dos parámetros más. Mi punto es que c_arrno es caro copiar. Sin embargo, cpp_arr[i]será muy costoso copiarlo.

Sumant
fuente
1
Sin embargo, puede pasar un multidimensional arraya una función sin perder dimensiones. Y si lo pasa a una plantilla de función, esa función podría deducir tanto la dimensión como el tamaño de cada dimensión, o solo una de las dos. Esto podría ser interesante para las bibliotecas de plantillas científicas que trabajan principalmente en dimensiones arbitrarias.
Sebastian Mach
30
un simple template <typename T, int M, int N> using array2d = std::array<std::array<T, N>, M>;debería resolver cualquiera de esos problemas.
Miles Rout
6
¡Tu ejemplo c_arres muy caro de copiar! Debe proporcionar el código para hacerlo usted mismo. El puntero al que decaerá es un equivalente más cercano a una referencia que a una copia y puede usar std::arraypara pasar una referencia si eso es lo que desea.
ChetS
1
@MilesRout técnicamente , ¿no debería ser en std::size_tlugar de int? perdón por ser quisquilloso, pero esto lo haría universal.
robbie
1
@ robbie0630 Sí, puedes hacerlo size_tsi quieres, aunque no puedo imaginar que haya muchos escenarios en los que se necesiten matrices con más de 4 mil millones de filas o columnas.
Miles Rout
14

Como dijo Sumant, las matrices multidimensionales son mucho más fáciles de usar con matrices C integradas que con std::array.

Cuando está anidado, std::arraypuede resultar muy difícil de leer e innecesariamente detallado.

Por ejemplo:

std::array<std::array<int, 3>, 3> arr1; 

comparado con

char c_arr[3][3]; 

Además, tenga en cuenta que begin(), end()y size()todos devuelven valores sin sentido cuando anida std::array.

Por estas razones, he creado mis propios contenedores de matrices multidimensionales de tamaño fijo array_2dy array_3d. Son análogos a std::arraypero para matrices multidimensionales de 2 y 3 dimensiones. Son más seguras y no tienen peor rendimiento que las matrices multidimensionales integradas. No incluí un contenedor para matrices multidimensionales con dimensiones superiores a 3, ya que son poco comunes. En C ++ 0x se podría crear una versión de plantilla variada que admita un número arbitrario de dimensiones.

Un ejemplo de la variante bidimensional:

//Create an array 3 x 5 (Notice the extra pair of braces) 

fsma::array_2d <double, 3, 5> my2darr = {{
    { 32.19, 47.29, 31.99, 19.11, 11.19},
    { 11.29, 22.49, 33.47, 17.29, 5.01 },
    { 41.97, 22.09, 9.76, 22.55, 6.22 }
}};

La documentación completa está disponible aquí:

http://fsma.googlecode.com/files/fsma.html

Puede descargar la biblioteca aquí:

http://fsma.googlecode.com/files/fsma.zip

Ricky65
fuente
4
Las matrices de estilo C de tamaño fijo son fáciles, pero si desea variar las dimensiones, las cosas se complican. Por ejemplo, dado arr[x][y], no se puede saber si arres una matriz de matrices, una matriz de punteros, un puntero a una matriz o un puntero a un puntero; todas las implementaciones son legítimas, dependiendo de sus necesidades. Y probablemente la mayoría de los casos de uso del mundo real para matrices multidimensionales requieren que el tamaño se determine en tiempo de ejecución.
Keith Thompson
¡Me encantaría ver esa implementación de plantilla variada de matrices n-dimensionales! ¡Metaprogramación en su máxima expresión!
steffen
3
@steffen Hice un intento hace unos años. Puede verlo aquí: code.google.com/p/fsma/source/browse/trunk/… . Caso de prueba aquí: code.google.com/p/fsma/source/browse/trunk/… . Sin embargo, estoy seguro de que se puede hacer mucho mejor.
Ricky65
5

Las matrices de estilo C que están disponibles en C ++ son en realidad mucho menos versátiles que las matrices C reales. La diferencia es que en C, los tipos de matriz pueden tener tamaños de tiempo de ejecución . El siguiente es código C válido, pero no se puede expresar con matrices de estilo C ++ ni con los array<>tipos C ++ :

void foo(int bar) {
    double tempArray[bar];
    //Do something with the bar elements in tempArray.
}

En C ++, tendría que asignar la matriz temporal en el montón:

void foo(int bar) {
    double* tempArray = new double[bar];
    //Do something with the bar elements behind tempArray.
    delete[] tempArray;
}

Esto no se puede lograr con std::array<>, porque barno se conoce en tiempo de compilación, requiere el uso de matrices de estilo C en C ++ o de std::vector<>.


Si bien el primer ejemplo podría expresarse con relativa facilidad en C ++ (aunque requiera new[]y delete[]), lo siguiente no se puede lograr en C ++ sin std::vector<>:

void smoothImage(int width, int height, int (*pixels)[width]) {
    int (*copy)[width] = malloc(height*sizeof(*copy));
    memcpy(copy, pixels, height*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y][x] = //compute smoothed value based on data around copy[y][x]
        }
    }
    free(copy);
}

El punto es que los punteros a los arreglos lineales int (*)[width]no pueden usar un ancho de tiempo de ejecución en C ++, lo que hace que cualquier código de manipulación de imágenes sea mucho más complicado en C ++ que en C.Una implementación típica de C ++ del ejemplo de manipulación de imágenes se vería así:

void smoothImage(int width, int height, int* pixels) {
    int* copy = new int[height*width];
    memcpy(copy, pixels, height*width*sizeof(*copy));
    for(y = height; y--; ) {
        for(x = width; x--; ) {
            pixels[y*width + x] = //compute smoothed value based on data around copy[y*width + x]
        }
    }
    delete[] copy;
}

Este código hace precisamente los mismos cálculos que el código C anterior, pero necesita realizar el cálculo del índice a mano dondequiera que se utilicen los índices . Para el caso 2D, esto sigue siendo factible (aunque ofrece muchas oportunidades para hacer un cálculo incorrecto del índice). Sin embargo, se vuelve realmente desagradable en el caso de 3D.

Me gusta escribir código en C ++. Pero siempre que necesito manipular datos multidimensionales, realmente me pregunto si debería mover esa parte del código a C.

cmaster - reinstalar monica
fuente
7
Cabe señalar que al menos Clang y GCC admiten VLA en C ++.
Janus Troelsen
@JanusTroelsen y también que están terriblemente limitados en los tipos de elementos que admiten.
derecha
¿C11 no hace que VLA sea opcional? Si es así, creo que su respuesta es engañosa. Sería correcto cuando C99 fuera el estándar pero no C11.
Bosón Z
1
@Zboson C99 es un estándar de C, y hay compiladores que implementan sus características de VLA ( gccpor ejemplo). C11 ha hecho que algunas cosas interesantes sean opcionales, y no creo que sea porque quieran prohibir la función. Tiendo a verlo como una señal de que querían bajar el nivel para escribir un compilador totalmente compatible con el estándar: los VLA son una bestia bastante difícil de implementar, y mucho código puede prescindir, por lo que tiene sentido para un nuevo compilador en algunos nuevos plataforma para no tener que implementar VLA de inmediato.
cmaster - reinstalar a monica
-1

Puede ser std::arrayque no sea lento. Pero hice algunas evaluaciones comparativas usando simple store y leí desde std :: array; Vea los resultados de referencia a continuación (en W8.1, VS2013 Update 4):

ARR_SIZE: 100 * 1000
Avrg = Tick / ARR_SIZE;

test_arr_without_init
==>VMem: 5.15Mb
==>PMem: 8.94Mb
==>Tick: 3132
==>Avrg: 0.03132
test_arr_with_init_array_at
==>VMem: 5.16Mb
==>PMem: 8.98Mb
==>Tick: 925
==>Avrg: 0.00925
test_arr_with_array_at
==>VMem: 5.16Mb
==>PMem: 8.97Mb
==>Tick: 769
==>Avrg: 0.00769
test_c_arr_without_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 358
==>Avrg: 0.00358
test_c_arr_with_init
==>VMem: 5.16Mb
==>PMem: 8.94Mb
==>Tick: 305
==>Avrg: 0.00305

Según las marcas negativas, el código que utilicé está en el pastebin ( enlace )

El código de la clase de referencia está aquí ;

No sé mucho sobre evaluaciones comparativas ... Mi código puede tener fallas

K'Prime
fuente
6
¿Resultados de referencia sin código de referencia o indicadores de compilación? Vamos, puedes hacerlo mejor.
R. Martinho Fernandes
FWIW, solo ese pequeño fragmento de código ya muestra que el punto de referencia tiene fallas graves. Un compilador lo suficientemente inteligente simplemente convertirá todo enlong test_arr_without_init() { return ARR_SIZE; }
R. Martinho Fernandes
Eso fue solo un ejemplo. Pensé que no era gran cosa. Cambié el código para devolver vacío, usé la versión de lanzamiento en VS 2013, con / O2 / Ot / Gl.
K'Prime
Eliminar el valor de retorno significa que el compilador puede convertir todo en void test_arr_without_init() {}ahora. Realmente necesita saltar a través de aros para asegurarse de que el código que está midiendo es el código que desea medir.
R. Martinho Fernandes
-5
  1. para implementar algo como std::array
  2. si no quiere usar el STL o no puede
  3. Para el rendimiento
Lou Franco
fuente
27
Dime cómo std::arrayserá menos eficiente que una matriz C.
Xeo
2
De wikipedia : "La implementación de la matriz no es necesaria para realizar una verificación vinculada. Sin embargo, la implementación en boost lo hace para el operador [], pero no para los iteradores". - entonces el operador [] es más lento. No he mirado las implementaciones, pero cualquier código en la implementación podría interferir con el optimizador.
Lou Franco
19
@Aaron McDaid: Eso es solo dentro at(), no está dentro operator[], como std::vector. No hay disminución del rendimiento ni exceso de código std::array, el compilador está diseñado para optimizar este tipo de cosas. Y, por supuesto, la adición de la función comprobada es una excelente herramienta de depuración y una gran ventaja. @Lou Franco: Todo el código C ++ puede depender de la biblioteca estándar, para eso es. @Earlz: Si no tiene STL disponible, entonces no es C ++, y eso es todo.
Puppy
6
@Earlz: El estándar C ++ contiene la biblioteca estándar. Si no puede usar la biblioteca, no es conforme. Y en segundo lugar, debe tener un compilador de mierda para que el uso de std::arraysea ​​más grande que el uso de matriz C equivalente.
Puppy
5
@Earlz: Hay una gran diferencia entre "no muy conforme" y "características faltantes que son cientos de páginas en la especificación".
Puppy