sirven que punteros puntero parametros para los funciones ejemplo dev declaracion como cadenas aritmetica apuntadores c pointers size

que - Punteros C: apuntando a una matriz de tamaño fijo



punteros c++ (9)

Esta pregunta va para los gurús de C que están por ahí:

En C, es posible declarar un puntero de la siguiente manera:

char (* p)[10];

... que básicamente indica que este puntero apunta a una matriz de 10 caracteres. Lo bueno de declarar un puntero como este es que obtendrá un error de tiempo de compilación si intenta asignar un puntero de una matriz de diferente tamaño a p. También le dará un error de tiempo de compilación si intenta asignar el valor de un simple puntero a p. Intenté esto con gcc y parece funcionar con ANSI, C89 y C99.

Me parece que declarar un puntero como este sería muy útil, particularmente cuando se pasa un puntero a una función. Por lo general, las personas escriben el prototipo de una función como esta:

void foo(char * p, int plen);

Si esperaba un buffer de un tamaño específico, simplemente probaría el valor de plen. Sin embargo, no se puede garantizar que la persona que le transfiere p realmente le proporcione ubicaciones plenas de memoria válidas en ese búfer. Debes confiar en que la persona que llamó a esta función está haciendo lo correcto. Por otra parte:

void foo(char (*p)[10]);

... obligaría a la persona que llama a darle un búfer del tamaño especificado.

Esto parece muy útil, pero nunca he visto un puntero declarado así en ningún código que haya encontrado.

Mi pregunta es: ¿hay alguna razón por la cual la gente no declara indicadores como este? ¿No estoy viendo una trampa obvia?


Bueno, en pocas palabras, C no hace las cosas de esa manera. Una matriz de tipo T se pasa como un puntero a la primera T en la matriz, y eso es todo lo que obtienes.

Esto permite algunos algoritmos interesantes y elegantes, como el bucle de la matriz con expresiones como

*dst++ = *src++

La desventaja es que la gestión del tamaño depende de usted. Desafortunadamente, el hecho de no hacer esto concienzudamente también ha llevado a millones de errores en la codificación C, y / o oportunidades de explotación malévola.

Lo que se acerca a lo que pides en C es pasar una struct (por valor) o un puntero a uno (por referencia). Siempre que se use el mismo tipo de estructura en ambos lados de esta operación, tanto el código que distribuye la referencia como el código que lo utiliza están de acuerdo con el tamaño de los datos que se manejan.

Su estructura puede contener cualquier información que desee; podría contener su matriz de un tamaño bien definido.

Sin embargo, nada impide que usted o un codificador incompetente o malévolo use moldes para engañar al compilador para que trate su estructura como de otro tamaño. La capacidad casi no encadenada para hacer este tipo de cosas es parte del diseño de C.


En mi compilador (vs2008) trata a char (*p)[10] como una matriz de punteros de caracteres, como si no hubiera paréntesis, incluso si compilo como un archivo C. ¿El compilador admite esta "variable"? Si es así, esa es una razón importante para no usarlo.


La razón obvia es que este código no se compila:

extern void foo(char (*p)[10]); void bar() { char p[10]; foo(p); }

La promoción predeterminada de una matriz es un puntero no calificado.

También vea esta pregunta , el uso de foo(&p) debería funcionar.


Lo que dices en tu publicación es absolutamente correcto. Yo diría que cada desarrollador C llega exactamente al mismo descubrimiento y exactamente a la misma conclusión cuando (si) alcanzan cierto nivel de competencia con el lenguaje C.

Cuando los detalles de su área de aplicación requieren una matriz de tamaño fijo específico (el tamaño de matriz es una constante de tiempo de compilación), la única forma adecuada de pasar dicha matriz a una función es mediante el uso de un parámetro de puntero a matriz

void foo(char (*p)[10]);

(en lenguaje C ++ esto también se hace con referencias

void foo(char (&p)[10]);

)

Esto habilitará la verificación de tipo de nivel de idioma, lo que asegurará que la matriz de tamaño exactamente correcto se suministre como argumento. De hecho, en muchos casos las personas usan esta técnica implícitamente, sin siquiera darse cuenta, ocultando el tipo de matriz detrás de un nombre typedef

typedef int Vector3d[3]; void transform(Vector3d *vector); /* equivalent to `void transform(int (*vector)[3])` */ ... Vector3d vec; ... transform(&vec);

Tenga en cuenta, además, que el código anterior es invariable en relación con el tipo Vector3d es una matriz o una struct . Puede cambiar la definición de Vector3d en cualquier momento desde una matriz a una struct y volver, y no tendrá que cambiar la declaración de la función. En cualquier caso, las funciones recibirán un objeto agregado "por referencia" (hay excepciones a esto, pero dentro del contexto de esta discusión esto es cierto).

Sin embargo, no verá este método de paso de matriz utilizado explícitamente con demasiada frecuencia, simplemente porque demasiadas personas se confunden por una sintaxis complicada y simplemente no son lo suficientemente cómodas con tales características del lenguaje C para usarlas correctamente. Por esta razón, en la vida real promedio, pasar una matriz como un puntero a su primer elemento es un enfoque más popular. Simplemente parece "más simple".

Pero en realidad, usar el puntero al primer elemento para pasar los arreglos es una técnica muy específica, un truco, que tiene un propósito muy específico: su único propósito es facilitar el paso de matrices de diferentes tamaños (es decir, el tamaño del tiempo de ejecución) . Si realmente necesita poder procesar matrices de tamaño de tiempo de ejecución, entonces la forma correcta de pasar dicha matriz es mediante un puntero a su primer elemento con el tamaño concreto proporcionado por un parámetro adicional

void foo(char p[], unsigned plen);

De hecho, en muchos casos es muy útil poder procesar matrices de tamaño de tiempo de ejecución, lo que también contribuye a la popularidad del método. Muchos desarrolladores C simplemente nunca encuentran (o nunca reconocen) la necesidad de procesar una matriz de tamaño fijo, por lo que permanecen ajenos a la técnica de tamaño fijo adecuada.

Sin embargo, si el tamaño de la matriz es fijo, pasándolo como un puntero a un elemento

void foo(char p[])

es un error de nivel técnico importante, que desafortunadamente está bastante extendido en estos días. Una técnica de puntero a matriz es un enfoque mucho mejor en tales casos.

Otra razón que podría dificultar la adopción de la técnica de paso de matriz de tamaño fijo es el dominio del enfoque ingenuo para el tipado de matrices asignadas dinámicamente. Por ejemplo, si el programa llama a matrices fijas de tipo char[10] (como en su ejemplo), un desarrollador promedio malloc tales matrices como

char *p = malloc(10 * sizeof *p);

Esta matriz no se puede pasar a una función declarada como

void foo(char (*p)[10]);

lo que confunde al desarrollador promedio y los hace abandonar la declaración de parámetros de tamaño fijo sin darle más vueltas. Sin embargo, en realidad, la raíz del problema radica en el enfoque ingenuo malloc . El formato malloc que se muestra arriba se debe reservar para matrices de tamaño de tiempo de ejecución. Si el tipo de matriz tiene un tamaño de tiempo de compilación, una mejor forma de malloc sería el siguiente

char (*p)[10] = malloc(sizeof *p);

Esto, por supuesto, se puede pasar fácilmente al foo declarado anteriormente

foo(p);

y el compilador realizará la verificación de tipos correcta. Pero, de nuevo, esto es demasiado confuso para un desarrollador de C no preparado, por lo que no lo verá con demasiada frecuencia en el código diario promedio "típico".


Me gustaría agregar a la respuesta de AndreyT (en caso de que alguien se tropiece con esta página buscando más información sobre este tema):

A medida que empiezo a jugar más con estas declaraciones, me doy cuenta de que hay una desventaja importante asociada con ellas en C (aparentemente no en C ++). Es bastante común tener una situación en la que le gustaría dar a un llamante un puntero de const a un buffer en el que ha escrito. Desafortunadamente, esto no es posible cuando se declara un puntero como este en C. En otras palabras, el estándar C (6.7.3 - Párrafo 8) está en desacuerdo con algo como esto:

int array[9]; const int (* p2)[9] = &array; /* Not legal unless array is const as well */

Esta restricción no parece estar presente en C ++, lo que hace que este tipo de declaraciones sea mucho más útil. Pero en el caso de C, es necesario recurrir a una declaración de puntero regular siempre que desee un puntero de const para el buffer de tamaño fijo (a menos que el buffer en sí mismo haya sido declarado const para comenzar). Puede encontrar más información en este hilo de correo: texto del enlace

Esta es una restricción severa en mi opinión y podría ser una de las razones principales por las que las personas generalmente no declaran punteros como este en C. El otro es el hecho de que la mayoría de la gente ni siquiera sabe que puede declarar un puntero como este como AndreyT ha señalado.


No recomendaría esta solución

typedef int Vector3d[3];

ya que oscurece el hecho de que Vector3D tiene un tipo que debes conocer. Los programadores generalmente no esperan que las variables del mismo tipo tengan diferentes tamaños. Considerar :

void foo(Vector3d a) { Vector3D b; }

donde sizeof a! = sizeof b


Puede declarar una matriz de caracteres de varias maneras:

char p[10]; char* p = (char*)malloc(10 * sizeof(char));

El prototipo de una función que toma una matriz por valor es:

void foo(char* p); //cannot modify p

o por referencia:

void foo(char** p); //can modify p, derefernce by *p[0] = ''f'';

o por sintaxis de matriz:

void foo(char p[]); //same as char*


Tal vez me esté perdiendo algo, pero ... dado que las matrices son punteros constantes, básicamente eso significa que no tiene sentido pasarles los punteros.

¿No podría simplemente usar void foo(char p[10], int plen); ?


También quiero usar esta sintaxis para habilitar más verificación de tipos.

Pero también estoy de acuerdo en que la sintaxis y el modelo mental del uso de punteros es más simple y fácil de recordar.

Aquí hay algunos obstáculos más que he encontrado.

  • El acceso a la matriz requiere el uso de (*p)[] :

    void foo(char (*p)[10]) { char c = (*p)[3]; (*p)[0] = 1; }

    Es tentador usar un puntero-a-char local en su lugar:

    void foo(char (*p)[10]) { char *cp = (char *)p; char c = cp[3]; cp[0] = 1; }

    Pero esto vencería parcialmente el propósito de usar el tipo correcto.

  • Uno debe recordar usar el operador de dirección al asignar una dirección de matriz a un puntero a matriz:

    char a[10]; char (*p)[10] = &a;

    El operador de dirección obtiene la dirección de toda la matriz en &a , con el tipo correcto para asignarla a p . Sin el operador, a se convierte automáticamente en la dirección del primer elemento de la matriz, igual que en &a[0] , que tiene un tipo diferente.

    Dado que esta conversión automática ya se está llevando a cabo, siempre estoy desconcertado de que el & es necesario. Es consistente con el uso de & en variables de otros tipos, pero debo recordar que una matriz es especial y que necesito & para obtener el tipo correcto de dirección, aunque el valor de la dirección sea el mismo.

    Una razón de mi problema puede ser que aprendí K & R C en los años 80, lo que no permitía usar todavía el operador & en arreglos completos (aunque algunos compiladores ignoraron eso o toleraron la sintaxis). Que, por cierto, puede ser otra razón por la que los punteros a las matrices tienen un tiempo difícil para ser adoptados: solo funcionan correctamente desde ANSI C, y la limitación del operador puede haber sido otra razón para considerarlos demasiado incómodos.

  • Cuando typedef no se utiliza para crear un tipo de puntero a matriz (en un archivo de encabezado común), un puntero a matriz global necesita una declaración extern más complicada para compartirla en los archivos:

    fileA: char (*p)[10]; fileB: extern char (*p)[10];