sirven que punteros parametros para los funciones estructura ejemplos dobles datos como cadenas arreglos aritmetica apuntadores c arrays pointers syntax

que - Diferencias al usar** en C



punteros en c ejemplos (11)

¿Cómo puedo saber si:

arr es un puntero a un puntero de un entero

arr es un puntero a una matriz de punteros a enteros

arr es un puntero a una matriz de punteros a matrices de enteros

No puedes. Puede ser cualquiera de esos. Lo que termina siendo depende de cómo lo asigne / use.

Por lo tanto, si escribe código usando estos, documente lo que está haciendo con ellos, pase parámetros de tamaño a las funciones que los utilizan y, en general, asegúrese de lo que asignó antes de usarlo.

Empecé a aprender C recientemente, y estoy teniendo problemas para entender la sintaxis del puntero, por ejemplo, cuando escribo la siguiente línea:

int ** arr = NULL;

¿Cómo puedo saber si:

  • arr es un puntero a un puntero de un entero

  • arr es un puntero a una matriz de punteros a enteros

  • arr es un puntero a una matriz de punteros a matrices de enteros

¿No es todo lo mismo con int ** ?

Otra pregunta para el mismo problema:

Si tengo una función que recibe char ** s como parámetro, quiero referirme a ella como un pointer a una matriz de cadenas, lo que significa un puntero a una matriz de punteros a una matriz de chars , pero también es un puntero a un puntero a un char ?


¿Cómo puedo saber si:

  • arr es un puntero a un puntero de un entero

Siempre es un puntero a puntero a entero.

  • arr es un puntero a una matriz de punteros a enteros
  • arr es un puntero a una matriz de punteros a matrices de enteros

Nunca puede ser eso. Un puntero a una matriz de punteros a enteros se declararía así:

int* (*arr)[n]

Suena como si te hubieran engañado para usar int** por los profesores / libros / tutoriales pobres. Casi siempre es una práctica incorrecta, como se explica here y here y (con una explicación detallada sobre punteros de matriz) aquí .

EDITAR

Finalmente me puse a escribir una publicación detallada explicando qué matrices son, qué tablas de búsqueda son, por qué las últimas son malas y qué deberías usar en su lugar: asignar correctamente arreglos multidimensionales .


¿No es todo lo mismo con int ** ?

Acaba de descubrir lo que puede considerarse un defecto en el sistema de tipos. Cada opción que especificó puede ser verdadera. Se deriva esencialmente de una vista plana de una memoria de programas, donde una sola dirección puede utilizarse para hacer referencia a varios diseños de memoria lógica.

La forma en que los programadores de C han estado lidiando con esto desde el inicio de C, es poniendo una convención en su lugar. Tales como los parámetros de tamaño exigentes para las funciones que aceptan tales punteros, y la documentación de sus suposiciones sobre el diseño de la memoria. O exigir que las matrices terminen con un valor especial, permitiendo así buffers "irregulares" de punteros a los buffers.

Siento que una cierta cantidad de aclaración está en orden. Como verá al consultar las otras respuestas muy buenas aquí, las matrices definitivamente no son punteros . Sin embargo, se degradan en unos en contextos suficientes para garantizar un error de décadas en la enseñanza sobre ellos (pero estoy divagando).

Lo que originalmente escribí se refiere al código de la siguiente manera:

void func(int **p_buff) { } //... int a = 0, *pa = &a; func(&pa); //... int a[3][10]; int *a_pts[3] = { a[0], a[1], a[2] }; func(a_pts); //... int **a = malloc(10 * sizeof *a); for(int i = 0; i < 10; ++i) a[i] = malloc(i * sizeof *a[i]); func(a);

Supongamos func y cada fragmento de código se compila en una unidad de traducción separada. Cada ejemplo (salvo cualquier error tipográfico por mí) es válido C. Las matrices decaerán en un "puntero-a-un-puntero" cuando se pasen como argumentos. ¿Cómo es la definición de func para saber exactamente qué pasó del tipo de su parámetro solo? La respuesta es que no puede. El tipo estático de p_buff es int** , pero aún permite que func acceda indirectamente (partes de) objetos con tipos efectivos muy diferentes.


El tipo de

int ** arr;

solo tiene una interpretación válida. Es:

arr is a pointer to a pointer to an integer

Si no tiene más información que la declaración anterior, eso es todo lo que puede saber al respecto, es decir, si arr está probablemente inicializado, apunta a otro puntero que, si probablemente se inicializó, apunta a un número entero.

Suponiendo una inicialización adecuada, la única forma válida de usarlo garantizada es:

**arr = 42; int a = **arr;

Sin embargo, C le permite usarlo de múltiples maneras.

• arr puede usarse como un puntero a un puntero a un entero (es decir, el caso básico)

int a = **arr;

• arr puede usarse como un puntero a un puntero a una matriz de entero

int a = (*arr)[4];

• arr puede usarse como un puntero a una matriz de punteros a enteros

int a = *(arr[4]);

• arr puede usarse como un puntero a una matriz de punteros a matrices de enteros

int a = arr[4][4];

En los últimos tres casos, puede parecer como si tuviera una matriz. Sin embargo, el tipo no es una matriz. El tipo siempre es solo a pointer to a pointer to an integer ; la desreferenciación es la aritmética del puntero. No se parece en nada a una matriz 2D.

Para saber cuál es válido para el programa en cuestión, debe ver el código de inicialización arr .

Actualizar

Para la parte actualizada de la pregunta:

Si usted tiene:

void foo(char** x) { .... };

lo único que sabes con certeza es que **x dará un carácter y *x te dará un indicador de carácter (en ambos casos se supone una inicialización adecuada de x ).

Si desea usar x de otra manera, p. Ej. x[2] para obtener el tercer puntero de char, requiere que el interlocutor haya inicializado x para que apunte a un área de memoria que tenga al menos 3 punteros de char consecutivos. Esto se puede describir como un contrato para llamar a foo .


Hay un truco al usar punteros, léelo del lado derecho al lado izquierdo:

int** arr = NULL;

¿Qué obtienes: arr , * , * , int , por lo que array es un puntero a un puntero a un entero.

Y int **arr; es lo mismo que int** arr; .


La declaración int **arr dice: "declarar arr como un puntero a un puntero a un entero". (Si es válido) apunta a un único puntero que apunta (si es válido) a un único objeto entero. Como es posible usar la aritmética del puntero con cualquier nivel de direccionamiento indirecto (es decir, *arr es lo mismo que arr[0] y **arr es lo mismo que arr[0][0] ), el objeto se puede usar para acceder a cualquier de los 3 de su pregunta (es decir, por segundo, acceda a un conjunto de punteros a enteros, y para terceros, acceda a un conjunto de punteros a primeros elementos de matrices enteras), siempre que los punteros señalen los primeros elementos de los arreglos ...

Sin embargo, arr aún se declara como un puntero a un único puntero a un único objeto entero. También es posible declarar un puntero a una matriz de dimensiones definidas . Aquí, a se declara como un puntero a una matriz de punteros de 10 elementos para matrices de 10 enteros:

cdecl> declare a as pointer to array 10 of pointer to array 10 of int; int (*(*a)[10])[10]

En la práctica, los punteros de matriz son los más utilizados para pasar matrices multidimensionales de dimensiones constantes a funciones y para pasar en matrices de longitud variable. La sintaxis para declarar una variable como un puntero a una matriz rara vez se ve, ya que cada vez que pasan a una función, es algo más fácil usar parámetros de tipo "matriz de tamaño indefinido" en su lugar, así que en lugar de declarar

void func(int (*a)[10]);

uno podría usar

void func(int a[][10])

pasar en una matriz multidimensional de matrices de 10 enteros. Alternativamente, un typedef puede usarse para disminuir el dolor de cabeza.


La sintaxis C es lógica. Como un asterisco antes de que el identificador en la declaración signifique un puntero al tipo de la variable, dos asteriscos significan un puntero al tipo de la variable.

En este caso, arr es a pointer to a pointer to integer .

Hay varios usos de dobles punteros. Por ejemplo, podría representar una matriz con un puntero a un vector de punteros. Cada puntero en este vector apunta a la fila de la matriz misma.

También se puede crear una matriz bidimensional utilizándolo, como este

int **arr=(int**)malloc(row*(sizeof(int*))); for(i=0;i<row;i++) { *(arr+i)=(int*)malloc(sizeof(int)*col); //You can use this also. Meaning of both is same. // arr[i]=(int*)malloc(sizeof(int)*col); }


Los punteros no mantienen la información si apuntan a un solo objeto o un objeto que es un elemento de una matriz. Además, para el puntero, los objetos únicos aritméticos se consideran como matrices que constan de un elemento.

Considera estas declaraciones

int a; int a1[1]; int a2[10]; int *p; p = &a; //... p = a1; //... p = a2;

En este ejemplo, el puntero p trata las direcciones. No sabe si la dirección que almacena apunta a un solo objeto como a o al primer elemento de la matriz a1 que tiene solo un elemento o al primer elemento de la matriz a2 que tiene diez elementos.


Teniendo únicamente la declaración de la variable, no puede distinguir los tres casos. Todavía se puede discutir si no se debe usar algo como int *x[10] para expresar una matriz de 10 punteros a enteros u otra cosa; pero int **x puede: debido a la aritmética del puntero, se puede usar de tres maneras diferentes, asumiendo cada una un diseño de memoria diferente con la (buena) posibilidad de hacer una suposición incorrecta.

Considere el siguiente ejemplo, donde un int ** se utiliza de tres maneras diferentes, es decir, p2p2i_v1 como un puntero a un puntero a un (único) int, p2p2i_v2 como un puntero a un conjunto de punteros a int, y p2p2i_v3 como un puntero a un puntero a una matriz de ints. Tenga en cuenta que no puede distinguir estos tres significados únicamente por el tipo, que es int** para los tres. Pero con diferentes inicializaciones, acceder a cada una de ellas de forma incorrecta produce algo impredecible, excepto el acceso a los primeros elementos:

int i1=1,i2=2,i3=3,i4=4; int *p2i = &i1; int **p2p2i_v1 = &p2i; // pointer to a pointer to a single int int *arrayOfp2i[4] = { &i1, &i2, &i3, &i4 }; int **p2p2i_v2 = arrayOfp2i; // pointer to an array of pointers to int int arrayOfI[4] = { 5,6,7,8 }; int *p2arrayOfi = arrayOfI; int **p2p2i_v3 = &p2arrayOfi; // pointer to a pointer to an array of ints // assuming a pointer to a pointer to a single int: int derefi1_v1 = *p2p2i_v1[0]; // correct; yields 1 int derefi1_v2 = *p2p2i_v2[0]; // correct; yields 1 int derefi1_v3 = *p2p2i_v3[0]; // correct; yields 5 // assuming a pointer to an array of pointers to int''s int derefi1_v1_at1 = *p2p2i_v1[1]; // incorrect, yields ? or seg fault int derefi1_v2_at1 = *p2p2i_v2[1]; // correct; yields 2 int derefi1_v3_at1 = *p2p2i_v3[1]; // incorrect, yields ? or seg fault // assuming a pointer to an array of pointers to an array of int''s int derefarray_at1_v1 = (*p2p2i_v1)[1]; // incorrect; yields ? or seg fault; int derefarray_at1_v2 = (*p2p2i_v2)[1]; // incorrect; yields ? or seg fault; int derefarray_at1_v3 = (*p2p2i_v3)[1]; // correct; yields 6;


Ya hay buenas respuestas aquí, pero quiero mencionar mi sitio "goto" para declaraciones complicadas: http://cdecl.org/

Visite el sitio, pegue su declaración y lo traducirá al inglés.

Para int ** arr; , dice declare arr as pointer to pointer to int .

El sitio también muestra ejemplos. Ponte a prueba con ellos, luego pasa el cursor para ver la respuesta.

(double (^)(int , long long ))foo

lanzar foo en bloque (int, long long) devolviendo el doble

int (*(*foo)(void ))[3]

declarar foo como puntero a la función (void) devolver puntero a la matriz 3 de int

También traducirá las declaraciones de inglés a C, lo cual es muy bueno, si la descripción es correcta.


int ** arr = NULL;

Es decirle al compilador, arr is a double pointer of an integer y un valor NULL asignado.