¿Cuál es el propósito de las matrices en C, cuando los punteros podrían haber hecho el trabajo?

8

Las matrices y los punteros no son lo mismo en C, aunque están relacionados y pueden usarse de manera similar. Hasta ahora todos estamos de acuerdo.

Sin embargo, no veo por qué se incluyeron matrices en C, cuando los punteros podrían haber hecho su trabajo perfectamente.

No estoy diciendo que elimine la notación de matriz (por ejemplo, a [5] o int a [4] = {0,1,2,3};), que es bastante útil y conveniente. Pero podría tener esa misma notación trabajando sobre punteros (como es el caso), como una medida cosmética. Entonces, la notación de matriz no es una razón para tener matrices, ¡solo la notación!

La única diferencia que veo es que las matrices son punteros constantes, y el tamaño de la memoria a la que apuntan no se puede cambiar. Pero esto también se puede lograr con punteros, exactamente haciéndolos constantes (la memoria no sería de tamaño fijo, pero no estoy seguro de si esto es un problema).

Entonces, ¿por qué no tener solo punteros y dejar que el programador decida cómo debe comportarse el puntero (es decir, constante, no constante, tamaño fijo, tamaño variable, etc.)?

Daniel Scocco
fuente
66
¿Por qué tener char cuando puedes lograr lo mismo con byte?
WuHoUnited
2
Sin embargo, en una nota más seria, ¿cómo piensa lograr la asignación de memoria con un puntero?
WuHoUnited
55
Personalmente, pienso en los punteros como (a veces) pretendiendo ser matrices en lugar de viceversa. En muchos otros idiomas, los punteros no tienen un comportamiento similar a una matriz: si desea acceder a una matriz a través de un puntero, use un tipo de puntero a matriz o haga una aritmética de puntero (contando bytes, no elementos). Apostaría una buena cantidad de dinero que los inventores de C pensaron en términos de ajustar punteros para obtener un comportamiento similar a una matriz más conveniente. Dudo mucho que ya tuvieran un comportamiento de puntero similar a una matriz y luego decidieron "Lo sé, agreguemos matrices también".
Steve314
3
¿Por qué tener 0xAB cuando puedes lograr lo mismo con 171?
e-MEE
2
¿Por qué tener expresiones compuestas como x = a + b * 2;cuando podrías lograr lo mismo con una secuencia de expresiones simples como x = b; x*=2; x+=a;?
fortran

Respuestas:

14

Las matrices son memorias contiguas creadas en la pila. No puede garantizar una memoria de pila contigua sin este azúcar sintáctico, e incluso si pudiera, tendría que asignar un puntero separado para poder hacer la aritmética del puntero (a menos que quisiera hacerlo *(&foo + x), lo cual no soy claro, pero podría violar la semántica del valor l, pero al menos es bastante incómodo, y gritaría por algún tipo de azúcar sintáctico). En cuanto al diseño, también es una forma de encapsulación, ya que puede referirse a la colección con un solo identificador (que de lo contrario requeriría un puntero separado). E incluso si pudiera asignarlos contiguamente y asignar un puntero separado para hacer referencia a ellos, tendría cualquiera int fooForSomething, fooForSomethingElse... lo que fuerza una gran cantidad de creatividad a medida que su colección crece, por lo que podría pensar en simplificar con int foo1, foo2 ..., que se parece a una matriz pero es más difícil de mantener.

kylben
fuente
Ah, y pasar por valor para foo1, foo2 ... se vuelve desordenado rápidamente, especialmente si desea permitir un número variable de miembros.
kylben
2
Solo las matrices declaradas como automáticas terminan en la pila. Cualquier otra cosa (es decir, statics) generalmente termina en otro lugar dependiendo del entorno.
Blrfl
No es un "azúcar sintáctico", es mucho más profundo: una parte importante del sistema de tipo C, así como la infame decadencia de la matriz.
SK-logic
1
¿Cuál es la diferencia entre una matriz en la pila y alloca ()
sylvanaar
@sylvanaar, vea esta pregunta SO: stackoverflow.com/questions/1018853/… En realidad nunca había oído hablar de él antes, pero aparentemente no es estándar y es peligroso en la forma en que esperaría que tal cosa sea peligrosa.
kylben
12

La notación de matriz es conveniente, más fácil de leer y menos propensa a errores. Proporciona un formalismo sobre punteros. Puede ser azúcar sintáctico, pero todos necesitamos un poco de dulzura de vez en cuando, ¿no?

Al igual que con todas las abstracciones, renuncia a un poco de flexibilidad para la comodidad que brinda la abstracción.

Robert Harvey
fuente
1
Como mencioné en la pregunta, no estoy en contra de la notación de matriz. Pero esa misma notación podría funcionar por encima de los punteros, entonces, ¿dónde está la necesidad de las matrices mismas?
Daniel Scocco
1
@DanielScocco - Tal vez podría. Pero se hizo así, sin embargo. Fin de la historia. Nadie puede realmente responder esto, ya que nadie sabe lo que estaba pasando por la cabeza del autor en ese momento.
Torre
10

Me sorprende que nadie haya comentado nada sobre las matrices multidimensionales todavía.

Si tiene una matriz hecha de "punteros anidados" (digamos int **p), lo que tiene en cada "fila" (dimensión exterior) es un puntero que apunta al primer elemento de esa fila, por lo que acceder a un valor requiere dos accesos de memoria. Además, la memoria que requiere es sizeof(*int)*n + n*m*sizeof(int).

En el escenario de matriz bidimensional int p[n][m], el acceso a un elemento requiere solo un acceso a la memoria, porque la dirección de la fila se calcula en lugar de buscarla; y la memoria requerida es sólo n*m*sizeof(int).

Otro lugar donde una matriz no puede ser reemplazada por un puntero es dentro de las estructuras.

struct s {
    int[2];
    float;
}

definitivamente no es lo mismo que

struct s {
   *int;
   float;
}

El tamaño de la matriz es importante allí, y los punteros no tienen esa información.

Entonces, sí, las matrices unidimensionales y los punteros individuales son en su mayoría intercambiables, pero sus similitudes terminan allí.

fortran
fuente
Muéstranos la diferencia en ensamblador. Las matrices multidimensionales, el empaquetamiento de estructuras y el relleno son solo formas de proporcionar un marco de referencia que tiene sentido para los humanos que tienen que entender y usar el lenguaje.
sylvanaar
@sylvanaar ¿hablas en serio? ¿realmente necesita ver el ensamblador para asegurarse de que la primera estructura tenga espacio para dos enteros y un flotante, y el segundo solo para almacenar una dirección de memoria y un flotante?
fortran
1
No te sigo ¿Qué tiene que ver el conjunto de instrucciones con todo esto? La operación le preguntaba qué cosas podía hacer con matrices y no con punteros, y declarar una estructura con una matriz de tamaño fijo no se puede emular con punteros. Acerca de las matrices multidimensionales, por supuesto, usted puede tener sólo un trozo de memoria y calcular el índice de células i,jpor i*cols+j, pero creo que al no tener que hacerlo solo es una razón suficientemente buena para justificar la existencia de tipos de matriz.
fortran
2
"Declarar estructuras [...] crea asignaciones de memoria estática en la imagen compilada" No, declarar una variable (de tipo estructura o cualquier otro tipo) hace esto. Declarar una estructura solo le dice al compilador qué tan grande es ese tipo y qué compensa cada uno de sus miembros; nunca podría asignar uno, o solo asignarlos como variables locales.
Random832
1
+1 para identificar de manera convincente el valor práctico de que las matrices de un tamaño fijo sean un "tipo" en lo que respecta al compilador: matrices multidimensionales, y en particular el hecho de que puede indexarlas trivialmente con el compilador haciendo la aritmética del puntero con el tamaño adecuado para usted, descienda de forma natural y elegante de las otras reglas de lenguaje simples al hacer que las matrices sean un "tipo" real: podría lograr el mismo azúcar sintáctico sin eso, pero la lógica tendría que ser un caso más especial, ambos en el compilador y en la especificación del lenguaje.
mtraceur
2

¿Por qué querría no poder usar matrices para tipos de valor?

int a[4] = {0,1,2,3};

Demian Brecht
fuente
Aún podría usar eso incluso si solo tuviéramos punteros, ¿no? El compilador solo tendría que saber qué hacer.
Daniel Scocco
2
@DanielScocco y cómo sabría qué hacer? matrices
monstruo de trinquete
De acuerdo: ¿cómo llamaría a la característica que reserva espacio para una secuencia de elementos del mismo tipo como una variable local o global, si no una matriz? Un tipo de puntero es un tipo de puntero: no especifica si lo que apunta es una secuencia o qué secuencia de tamaño.
Steve314
44
@DanielScocco Lo que viene con una matriz que no viene con un puntero es información sobre el tamaño. Por lo tanto, un par (puntero, tamaño) o un par (puntero, puntero) puede contener tanta información como el nombre de una matriz. Si esto significa que las matrices son pares de punteros (o viceversa) parece más una pregunta metafísica, por lo que no estoy realmente interesado en responder eso. (La respuesta es probablemente no, ya que los pares de punteros se parecen más a rebanadas y, por lo tanto, pueden hacer más que solo referirse a una matriz.)
Luc Danton,
1
@Daniel Scocco, las matrices tienen un tamaño . Puede asignar una matriz en la pila, puede colocar una matriz en una estructura contigua, puede usarla dentro de una unión. Si el tipo de matriz no existiera, no sería posible.
SK-logic
1

¿Cómo manejaría plataformas, como la 8031 ​​sin memoria externa, que no admiten malloco alloca? Quizás se esté olvidando de que C no es solo para hierro grande, sino también para controladores de elevadores y tostadoras.

David Schwartz
fuente
1

En C, la notación de matriz dentro de las expresiones siempre es simplemente aritmética de puntero. Todos los usos de un identificador de matriz en una expresión se convierten inmediatamente de "matriz de T" a "puntero a T" y el valor se convierte en un puntero al primer elemento de la matriz. La notación de matriz (por ejemplo a[1][2]) siempre se expande en aritmética de puntero (por ejemplo *(*(t+1)+2)).

Sin embargo, la notación de matriz en declaraciones y definiciones es algo completamente distinto. Un declarador matriz describe un "array de T de tipo" donde los valores de este tipo son secuencias de elementos de tipo T . La definición de un objeto de matriz tiene que ver con el uso de una notación de matriz conveniente y fácil de entender para asignar la cantidad adecuada de almacenamiento para la matriz de objetos deseada de modo que el identificador de matriz se refiera a este almacenamiento sin que parezca un puntero. En efecto, la notación de matriz en una declaración o definición es una macro que genera una expresión usando sizeof()y aritmética, y en el caso de una definición, el equivalente dealloca() para arreglos automáticos o su equivalente en el vinculador para arreglos globales, y hacerlo todo en tiempo de compilación (bueno, excepto para arreglos de longitud variable C99).

El uso de la notación de matriz en expresiones y el uso de tipos de matriz no está tan estrechamente conectado, aunque es tradición y modismo usar la notación de matriz en expresiones para hacer referencia al almacenamiento en objetos declarados y / o definidos como matrices. Puede usar la notación de matriz con un tipo de puntero para hacer que la aritmética del puntero sea más limpia y significativa. De hecho, en C una expresión de la forma e1[e2]es precisamente equivalente a la expresión *((e1)+(e2)). La conversión binaria habitual se aplica a los dos operandos, y el resultado es siempre un valor l . Como el operador de indirección ( *) debe tener un puntero como su operando, uno de e1y e2debe ser un puntero y el otro debe ser un entero, pero no importa cuáldado que la conversión unaria inicial para cualquier "matriz de T" es convertirla en "puntero a T". La notación de matriz en expresiones es en efecto una macro compiladora (nivel de lenguaje) para generar expresiones aritméticas de puntero.

Entonces, realmente C ya funciona de la manera que sugieres, pero estás confundiendo el uso de la notación en dos contextos muy separados.

Greg A. Woods
fuente