Esta pregunta va dirigida a los gurús de C:
En C, es posible declarar un puntero de la siguiente manera:
char (* p)[10];
.. que básicamente establece 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 ap. También le dará un error de tiempo de compilación si intenta asignar el valor de un puntero char simple ap. Intenté esto con gcc y parece funcionar con ANSI, C89 y C99.
Me parece que declarar un puntero como este sería muy útil, especialmente cuando se pasa un puntero a una función. Por lo general, la gente escribiría el prototipo de una función como esta:
void foo(char * p, int plen);
Si esperaba un búfer de un tamaño específico, simplemente probaría el valor de plen. Sin embargo, no se puede garantizar que la persona que le pasa p realmente le dará ubicaciones de memoria válidas en ese búfer. Debe 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 como este en ningún código con el que me haya encontrado.
Mi pregunta es: ¿Hay alguna razón por la que la gente no declare punteros como este? ¿No estoy viendo alguna trampa obvia?
10
puede ser reemplazada por cualquier variable en el alcanceRespuestas:
Lo que dices en tu publicación es absolutamente correcto. Yo diría que todos los desarrolladores de C llegan exactamente al mismo descubrimiento y a la misma conclusión cuando (si) alcanzan cierto nivel de competencia con el lenguaje C.
Cuando las características específicas de su área de aplicación requieren una matriz de tamaño fijo específico (el tamaño de la matriz es una constante en tiempo de compilación), la única forma correcta de pasar dicha matriz a una función es mediante el uso de un parámetro de puntero a matriz
(en lenguaje C ++ esto también se hace con referencias
).
Esto permitirá la verificación de tipos a nivel de idioma, lo que garantizará que la matriz del tamaño exactamente correcto se proporcione como argumento. De hecho, en muchos casos la gente usa esta técnica implícitamente, sin siquiera darse cuenta, ocultando el tipo de matriz detrás de un nombre typedef
Tenga en cuenta además que el código anterior es invariante con relación a que el
Vector3d
tipo sea una matriz o unstruct
. Puede cambiar la definición deVector3d
en cualquier momento de una matriz a aystruct
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á que este método de transferencia de matrices se use explícitamente con demasiada frecuencia, simplemente porque demasiadas personas se confunden con una sintaxis bastante complicada y simplemente no se sienten 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 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 la matriz es una técnica muy especializada, 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, tamaño en tiempo de ejecución) . Si realmente necesita poder procesar matrices de tamaño en 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
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 de 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 adecuada de tamaño fijo.
Sin embargo, si el tamaño de la matriz es fijo, pasarlo como puntero a un elemento
es un error importante a nivel de técnica, que lamentablemente 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 obstaculizar la adopción de la técnica de paso de arreglos de tamaño fijo es el predominio del enfoque ingenuo para la tipificación de arreglos asignados dinámicamente. Por ejemplo, si el programa requiere arreglos fijos de tipo
char[10]
(como en su ejemplo), un desarrollador promedio usarámalloc
arreglos comoEsta matriz no se puede pasar a una función declarada como
lo que confunde al desarrollador promedio y les hace abandonar la declaración de parámetros de tamaño fijo sin pensarlo más. Sin embargo, en realidad, la raíz del problema radica en el
malloc
enfoque ingenuo . Elmalloc
formato que se muestra arriba debe reservarse para matrices de tamaño en tiempo de ejecución. Si el tipo de matriz tiene un tamaño en tiempo de compilación, una mejor manera demalloc
hacerlo sería la siguienteEsto, por supuesto, se puede pasar fácilmente a lo declarado anteriormente
foo
y el compilador realizará la verificación de tipo adecuada. 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 "típico" promedio de todos los días.
fuente
const
propiedad con esta técnica. Unconst char (*p)[N]
argumento no parece compatible con un puntero achar table[N];
Por el contrario, unchar*
ptr simple sigue siendo compatible con unconst char*
argumento.(*p)[i]
y no*p[i]
. Este último saltará por el tamaño de la matriz, que es casi seguro que no es lo que desea. Al menos para mí, aprender esta sintaxis provocó, en lugar de prevenir, un error; Habría obtenido el código correcto más rápido simplemente pasando un flotador *.const
puntero a una matriz de elementos mutables. Y sí, esto es completamente diferente de un puntero a una matriz de elementos inmutables.Me gustaría agregar a la respuesta de AndreyT (en caso de que alguien se tope 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 gran desventaja asociada con ellas en C (aparentemente no en C ++). Es bastante común tener una situación en la que le gustaría darle a la persona que llama un puntero constante a un búfer 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:
Esta restricción no parece estar presente en C ++, lo que hace que este tipo de declaraciones sean mucho más útiles. Pero en el caso de C, es necesario recurrir a una declaración de puntero regular siempre que desee un puntero constante al búfer de tamaño fijo (a menos que el búfer en sí se haya declarado constante para empezar). Puede encontrar más información en este hilo de correo: texto del enlace
En mi opinión, esta es una restricción severa y podría ser una de las principales razones por las que las personas no suelen declarar punteros como este en C.La otra es el hecho de que la mayoría de las personas ni siquiera saben que se puede declarar un puntero como este como AndreyT ha señalado.
fuente
La razón obvia es que este código no se compila:
La promoción predeterminada de una matriz es a un puntero no calificado.
Consulte también esta pregunta , el uso
foo(&p)
debería funcionar.fuente
foo(&p)
.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 de usar punteros es más simple y más fácil de recordar.
Aquí hay algunos obstáculos más con los que me he encontrado.
Acceder a la matriz requiere usar
(*p)[]
:Es tentador usar un puntero a char local en su lugar:
Pero esto frustraría parcialmente el propósito de usar el tipo correcto.
Uno debe recordar usar el operador de dirección al asignar la dirección de una matriz a un puntero a matriz:
El operador address-of obtiene la dirección de toda la matriz
&a
, con el tipo correcto para asignarlap
. Sin el operador,a
se convierte automáticamente a 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 me sorprende que
&
sea necesario. Es consistente con el uso de&
on variables de otros tipos, pero debo recordar que una matriz es especial y que necesito&
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
&
todavía no permitía usar el operador en arreglos completos (aunque algunos compiladores lo ignoraron o toleraron la sintaxis). Lo cual, por cierto, puede ser otra razón por la que los apuntadores a matrices tienen dificultades 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
se no se utiliza para crear un tipo de la matriz de punteros-a-(en un archivo de cabecera común), entonces una matriz de punteros-a-global necesita una más complicadaextern
declaración compartirlo a través de archivos:fuente
Bueno, en pocas palabras, C no hace las cosas de esa manera. Una matriz de tipo
T
se pasa como un puntero al primeroT
de la matriz, y eso es todo lo que obtiene.Esto permite algunos algoritmos geniales y elegantes, como recorrer la matriz con expresiones como
La desventaja es que la gestión del tamaño depende de usted. Desafortunadamente, el no hacer esto concienzudamente también ha dado lugar a millones de errores en la codificación C y / u oportunidades de explotación malévola.
Lo que se acerca a lo que pregunta en C es pasar un
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 entrega la referencia como el código que lo usa están de acuerdo con el tamaño de los datos que se manejan.Su estructura puede contener los datos que desee; podría contener su matriz de un tamaño bien definido.
Aún así, nada impide que usted o un codificador incompetente o malévolo utilicen conversiones para engañar al compilador y que trate su estructura como una de diferente tamaño. La capacidad casi ilimitada de hacer este tipo de cosas es parte del diseño de C.
fuente
Puede declarar una matriz de caracteres de varias formas:
El prototipo de una función que toma una matriz por valor es:
o por referencia:
o por sintaxis de matriz:
fuente
char (*p)[10] = malloc(sizeof *p)
.No recomendaría esta solución
ya que oculta el hecho de que Vector3D tiene un tipo que debe conocer. Los programadores generalmente no esperan que las variables del mismo tipo tengan diferentes tamaños. Considerar :
donde sizeof a! = sizeof b
fuente
sizeof(a)
no es lo mismo quesizeof(b)
?Tal vez me esté perdiendo algo, pero ... dado que las matrices son punteros constantes, básicamente eso significa que no tiene sentido pasarles punteros.
¿No podrías simplemente usar
void foo(char p[10], int plen);
?fuente
En mi compilador (vs2008) se trata
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 es compatible con esta "variable"? Si es así, esa es una razón importante para no usarlo.fuente