vectores una punteros programacion matriz matrices llenar ejemplos como c++ arrays c++11 pointers multidimensional-array

c++ - punteros - ¿Cuál es el tipo de un puntero a una matriz 2D?



punteros en c (4)

¿Cuál es el tipo de [...]

¿Ya trataste de pedirle al compilador que te dijera el tipo de expresión?

int main() { int arr[2][3] = {{0,1,2}, {3,4,5}}; // <-- direct complete initialized here auto ptr = arr; // <-- address assignment only cout << "arr: " << typeid(arr).name() << endl; cout << "ptr: " << typeid(ptr).name() << endl; return 0; }

Debo confesar que la salida

arr: A2_A3_i ptr: PA3_i

parece no ser muy legible a primera vista (en comparación con algunos otros idiomas), pero en caso de duda puede ser útil. Es muy compacto, pero uno puede acostumbrarse pronto. La codificación depende del compilador, en caso de que esté utilizando gcc, puede leer el Chapter 29. Demangling para comprender cómo.

Editar:

algunos experimentos con alguna función simple_cpp_name como este truco rudimentario

#include <typeinfo> #include <cxxabi.h> #include <stdlib.h> #include <string> std::string simple_cpp_name(const std::type_info& ti) { /// simplified code extracted from "Chapter 29. Demangling" /// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_demangling.html char* realname = abi::__cxa_demangle(ti.name(), 0, 0, 0); std::string name = realname; free(realname); return name; }

le mostrará que auto &rfa = arr; hace que rfa tenga el mismo tipo que arr que es int [2][3] .

Sé que lo siguiente no es correcto:

int arr[2][3] = {}; //some array initialization here int** ptr; ptr = arr;

Pero estoy bastante sorprendido de que las siguientes líneas realmente funcionen

int arr[2][3] = {}; //some array initialization here auto ptr = arr; int another_arr[2][3] = {}; //some array initialization here ptr = another_arr;

¿Alguien puede explicar cuál es el tipo asignado a ptr en el segundo bloque de código, y qué sucedió debajo?


Bueno, las matrices decaen a punteros cuando se usan prácticamente en todas partes. Por lo tanto, naturalmente, también hay deterioro en el fragmento de código.

Pero es solo la dimensión de matriz "exterior" la que se desintegra a un puntero. Dado que las matrices tienen una fila principal, usted termina con int (*)[3] como el tipo de puntero, que es un puntero a una matriz unidimensional, no a una matriz bidimensional. Apunta a la primera "fila".

Si desea que la deducción de ptr sea ​​un puntero a la matriz en su lugar, utilice el operador de dirección de:

auto ptr = &arr;

Ahora ptr es int(*)[2][3] .


En

auto ptr = arr;

arr descompone en un puntero a su primer elemento de la manera normal; es equivalente a

auto ptr = &arr[0];

Como arr[0] es una matriz de tres int s, eso hace que ptr a int (*)[3] - un puntero a int[3] .

another_arr desintegra exactamente de la misma manera, entonces en

ptr = another_arr;

ambos lados de la asignación tienen el tipo int (*)[3] , y puede asignar un T* a un T* para cualquier tipo T

Un puntero a arr tiene el tipo int(*)[2][3] .

Si desea un puntero a la matriz en lugar de un puntero al primer elemento de la matriz, debe usar & :

auto ptr = &arr;


Primero, veamos por qué no puede asignar int arr[2][3] a int ** . Para que sea más fácil de visualizar, inicializaremos su matriz con una secuencia y consideraremos cómo se ve en la memoria:

int arr[2][3] = {{1,2,3},{4,5,6}};

En la memoria, los datos de la matriz se almacenan como un solo bloque, al igual que una matriz 1D regular:

arr: [ 1, 2, 3, 4, 5, 6 ]

La variable arr contiene la dirección del inicio de este bloque, y de su tipo ( int[2][3] ) el compilador sabe interpretar un índice como arr[1][0] como "toma el valor que está en posición (1 * 2 + 0) en la matriz ".

Sin embargo, para un puntero a puntero ( int** ), se espera que el puntero a puntero contenga una sola dirección de memoria o una matriz de direcciones de memoria, y esta / estas dirección (es) apuntan a (an) otro valor int único o una matriz de ints. Digamos que int **ptrptr la matriz arr en int **ptrptr . En memoria, se vería así:

ptrptr: [0x203F0B20, 0x203F17D4] 0x203F0B20: [ 1, 2, 3 ] 0x203F17D4: [ 4, 5, 6 ]

Por lo tanto, además de los datos int reales, se debe almacenar un puntero adicional para cada fila de la matriz. En lugar de convertir los dos índices en una única búsqueda de matriz, el acceso se debe realizar haciendo una primera búsqueda de matriz ("tomar el segundo valor en ptrptr para obtener una int *"), luego otra búsqueda de matriz ("tomar el primer valor en el array en la dirección mantenida por el int * "obtenido previamente.

Aquí hay un programa que ilustra esto:

#include <iostream> int main() { int arr[2][3] = {{1,2,3},{4,5,6}}; std::cout << "Memory addresses for int arr[2][3]:" << std::endl; for (int i=0; i<2; i++) { for (int j=0; j<3; j++) { std::cout << reinterpret_cast<void*>(&arr[i][j]) << ": " << arr[i][j] << std::endl; } } std::cout << std::endl << "Memory addresses for int **ptrptr:" << std::endl; int **ptrptr = new int*[2]; for (int i=0; i<2; i++) { ptrptr[i] = new int[3]; for (int j=0; j<3; j++) { ptrptr[i][j] = arr[i][j]; std::cout << reinterpret_cast<void*>(&ptrptr[i][j]) << ": " << ptrptr[i][j] << std::endl; } } // Cleanup for (int i=0; i<2; i++) { delete[] ptrptr[i]; ptrptr[i] = nullptr; } delete[] ptrptr; ptrptr = nullptr; return 0; }

Salida:

Memory addresses for int arr[2][3]: 0x7ecd3ccc0260: 1 0x7ecd3ccc0264: 2 0x7ecd3ccc0268: 3 0x7ecd3ccc026c: 4 0x7ecd3ccc0270: 5 0x7ecd3ccc0274: 6 Memory addresses for int **ptrptr: 0x38a1a70: 1 0x38a1a74: 2 0x38a1a78: 3 0x38a1a90: 4 0x38a1a94: 5 0x38a1a98: 6

Observe cómo las direcciones de memoria siempre aumentan en 4 bytes para arr , pero para ptrptr hay un salto de 24 bytes entre los valores 3 y 4.

Una asignación simple no puede crear la estructura de puntero a puntero necesaria para el tipo int ** , por lo que los ciclos fueron necesarios en el programa anterior. Lo mejor que puede hacer es degradar el tipo int[2][3] en un puntero a una fila de esa matriz, es decir, int (*)[3] . Eso es lo que su auto ptr = arr; termina como.