¿Por qué las matrices C no realizan un seguimiento de su longitud?

77

¿Cuál fue el razonamiento detrás de no almacenar explícitamente la longitud de una matriz con una matriz en C?

A mi modo de ver, hay razones abrumadoras para hacerlo, pero no muchas en apoyo del estándar (C89). Por ejemplo:

  1. Tener longitud disponible en un búfer puede evitar el desbordamiento del búfer.
  2. Un estilo Java arr.lengthes claro y evita que el programador tenga que mantener muchos ints en la pila si se trata de varias matrices.
  3. Los parámetros de la función se vuelven más convincentes.

Pero quizás la razón más motivadora, en mi opinión, es que generalmente no se ahorra espacio sin mantener la longitud. Me aventuraría a decir que la mayoría de los usos de las matrices implican una asignación dinámica. Es cierto que puede haber algunos casos en los que las personas usan una matriz asignada en la pila, pero esa es solo una llamada de función *: la pila puede manejar 4 u 8 bytes adicionales.

Dado que el administrador de almacenamiento dinámico tiene que rastrear el tamaño de bloque libre utilizado por la matriz asignada dinámicamente de todos modos, ¿por qué no hacer que esa información sea utilizable (y agregar la regla adicional, verificada en el momento de la compilación, que uno no puede manipular la longitud explícitamente a menos que uno lo haga)? gusta dispararse en el pie).

La única cosa que puedo pensar en el otro lado es que no hay seguimiento longitud puede haber hecho compiladores más simple, pero no que mucho más simple.

* Técnicamente, se podría escribir algún tipo de función recursiva con una matriz con almacenamiento automático, y en este caso (muy elaborado) el almacenamiento de la longitud puede resultar en un mayor uso del espacio.

VF1
fuente
66
Supongo que se podría argumentar que cuando C incluye el uso de estructuras como parámetros y tipos de valor de retorno, debería haber incluido el azúcar sintáctico para los "vectores" (o cualquier nombre), que estaría debajo de estructura con longitud y matriz o puntero a matriz . El soporte de nivel de lenguaje para esta construcción común (también cuando se pasa como argumentos separados y no como estructura única) habría salvado innumerables errores y también habría simplificado la biblioteca estándar.
hyde
3
También puede encontrar por qué Pascal no es mi lenguaje de programación favorito Sección 2.1 para ser perspicaz.
34
Si bien todas las otras respuestas tienen algunos puntos interesantes, creo que la conclusión es que C fue escrito para que los programadores de lenguaje ensamblador puedan escribir código más fácilmente y que sea portátil. Con eso en mente, tener una longitud de matriz almacenada CON una matriz automáticamente habría sido una molestia y no una deficiencia (como lo haría con otros buenos deseos de recubrir dulces). Estas características parecen agradables hoy en día, pero en aquel entonces, con frecuencia, era difícil esforzarse por incluir un byte más de programa o datos en su sistema. El uso derrochador de la memoria habría limitado severamente la adopción de C.
Dunk
66
La parte real de su respuesta ya ha sido respondida muchas veces de la manera que lo hubiera hecho, pero puedo extraer un punto diferente: "¿Por qué no se malloc()puede solicitar el tamaño de un área ed de manera portátil?" Eso es algo que me hace preguntarme varias veces.
glglgl
55
Votación para reabrir. Hay alguna razón en alguna parte, incluso si es simplemente "K&R no lo pensó".
Telastyn

Respuestas:

106

Las matrices C hacen un seguimiento de su longitud, ya que la longitud de la matriz es una propiedad estática:

int xs[42];  /* a 42-element array */

Por lo general, no puede consultar esta longitud, pero no es necesario porque es estática de todos modos: simplemente declare una macro XS_LENGTHpara la longitud y listo.

El problema más importante es que las matrices C se degradan implícitamente en punteros, por ejemplo, cuando se pasan a una función. Esto tiene sentido y permite algunos buenos trucos de bajo nivel, pero pierde la información sobre la longitud de la matriz. Entonces, una mejor pregunta sería por qué C fue diseñado con esta degradación implícita a los punteros.

Otra cuestión es que los punteros no necesitan almacenamiento, excepto la dirección de memoria en sí. C nos permite convertir enteros en punteros, punteros a otros punteros y tratar punteros como si fueran matrices. Al hacer esto, C no es lo suficientemente loco como para fabricar algo de longitud de matriz, pero parece confiar en el lema de Spiderman: con gran poder, el programador cumplirá con la gran responsabilidad de realizar un seguimiento de las longitudes y desbordamientos.

amon
fuente
13
Creo que quiere decir, si no me equivoco, que los compiladores de C realizan un seguimiento de las longitudes de la matriz estática. Pero esto no sirve para las funciones que solo obtienen un puntero.
VF1
25
@ VF1 sí. Sin embargo, el importante es que las matrices y los punteros son cosas diferentes en C . Suponiendo que no está utilizando ninguna extensión del compilador, generalmente no puede pasar una matriz a una función, pero puede pasar un puntero e indexar un puntero como si fuera una matriz. Te estás quejando efectivamente de que los punteros no tienen una longitud adjunta. Debería quejarse de que las matrices no se pueden pasar como argumentos de función, o que las matrices se degradan a punteros implícitamente.
amon
37
"Por lo general, no puede consultar esta longitud" - en realidad puede, es el operador sizeof - sizeof (xs) devolvería 168 suponiendo que los int tienen cuatro bytes de longitud. Para obtener el 42, haga: sizeof (xs) / sizeof (int)
tcrosley
15
@tcrosley Eso solo funciona dentro del alcance de la declaración de matriz: intente pasar xs como parámetro a otra función y luego vea qué sizeof (xs) le da ...
Gwyn Evans
26
@GwynEvans nuevamente: los punteros no son matrices. Entonces, si "pasa una matriz como parámetro a otra función", no está pasando una matriz sino un puntero. Afirmar que sizeof(xs)dónde xsestá una matriz sería algo diferente en otro ámbito es descaradamente falso, porque el diseño de C no permite que las matrices abandonen su ámbito. Si sizeof(xs)where xsis a array es diferente de sizeof(xs)where xses un puntero, no es sorprendente porque está comparando manzanas con naranjas .
amon
38

Mucho de esto tuvo que ver con las computadoras disponibles en ese momento. El programa compilado no solo tuvo que ejecutarse en una computadora de recursos limitados, sino que, quizás lo más importante, el compilador mismo tuvo que ejecutarse en estas máquinas. En el momento en que Thompson desarrolló C, estaba usando un PDP-7, con 8k de RAM. Las funciones de lenguaje complejas que no tenían un análogo inmediato en el código de máquina real simplemente no se incluyeron en el idioma.

Una lectura cuidadosa de la historia de C arroja más información sobre lo anterior, pero no fue completamente el resultado de las limitaciones de la máquina que tenían:

Además, el lenguaje (C) muestra un poder considerable para describir conceptos importantes, por ejemplo, vectores cuya longitud varía en el tiempo de ejecución, con solo unas pocas reglas y convenciones básicas. ... Es interesante comparar el enfoque de C con el de dos lenguajes casi contemporáneos, Algol 68 y Pascal [Jensen 74]. Las matrices en Algol 68 tienen límites fijos o son 'flexibles:' se requiere un mecanismo considerable tanto en la definición del lenguaje como en los compiladores, para acomodar matrices flexibles (y no todos los compiladores los implementan completamente). Pascal original solo tenía un tamaño fijo matrices y cadenas, y esto resultó confinado [Kernighan 81].

Las matrices C son inherentemente más potentes. Agregarles límites restringe para qué los puede usar el programador. Dichas restricciones pueden ser útiles para los programadores, pero necesariamente también son limitantes.

Adam Davis
fuente
44
Esto prácticamente clava la pregunta original. Eso y el hecho de que C se mantenía deliberadamente "un toque ligero" cuando se trataba de verificar lo que estaba haciendo el programador, como parte de hacerlo atractivo para escribir sistemas operativos.
ClickRick
55
Gran enlace, también cambiaron explícitamente el almacenamiento de la longitud de las cadenas para usar un delimitador to avoid the limitation on the length of a string caused by holding the count in an 8- or 9-bit slot, and partly because maintaining the count seemed, in our experience, less convenient than using a terminator, bueno, eso es todo :-)
Voo
55
Las matrices no terminadas también encajan con el enfoque de C. de metal. Recuerde que el libro K&R C tiene menos de 300 páginas con un tutorial de idioma, una referencia y una lista de las llamadas estándar. Mi libro O'Reilly Regex es casi el doble que K&R C.
Michael Shopsen
22

En el día en que se creó C, ¡y 4 bytes adicionales de espacio para cada cadena, sin importar cuán corto hubiera sido un desperdicio!

Hay otro problema: recuerde que C no está orientado a objetos, por lo que si realiza el prefijo de longitud de todas las cadenas, debería definirse como un tipo intrínseco del compilador, no a char*. Si fuera un tipo especial, entonces no sería capaz de comparar una cadena con una cadena constante, es decir:

String x = "hello";
if (strcmp(x, "hello") == 0) 
  exit;

tendría que tener detalles especiales del compilador para convertir esa cadena estática en una cadena, o tener diferentes funciones de cadena para tener en cuenta el prefijo de longitud.

Sin embargo, creo que, en última instancia, simplemente no eligieron el prefijo de longitud a diferencia de decir Pascal.

gbjbaanb
fuente
10
La verificación de límites también lleva tiempo. Trivial en los términos de hoy, pero algo a lo que la gente le prestó atención cuando le importaban unos 4 bytes.
Gort the Robot
18
@StevenBurnap: no es tan trivial incluso hoy si estás en un bucle interno que recorre cada píxel de una imagen de 200 MB. En general, si está escribiendo C, quiere ir rápido , y no quiere perder el tiempo en una comprobación de límite inútil en cada iteración cuando su forciclo ya estaba configurado para respetar los límites.
Matteo Italia
44
@ VF1 "atrás en el día" bien podría haber sido dos bytes (¿DEC PDP / 11 alguien?)
ClickRick
77
No es solo "atrás en el día". Para el software al que C se dirige como un "lenguaje ensamblador portátil", como kernels del sistema operativo, controladores de dispositivo, software embebido en tiempo real, etc. desperdiciar media docena de instrucciones sobre la verificación de límites es importante y, en muchos casos, debe estar "fuera de límites" (¿cómo podría escribir un depurador si no puede acceder aleatoriamente al almacenamiento de otro programa?).
James Anderson
3
Este es en realidad un argumento bastante débil considerando que BCPL tenía argumentos contados en longitud. Al igual que Pascal, aunque eso se limitaba a 1 palabra, generalmente solo 8 o 9 bits, lo que era un poco limitante (también excluye la posibilidad de compartir partes de cadenas, aunque esa optimización probablemente era demasiado avanzada para el momento). Y declarar una cadena como una estructura con una longitud seguida por la matriz realmente no necesitaría soporte especial para el compilador ..
Voo
11

En C, cualquier subconjunto contiguo de una matriz también es una matriz y se puede operar como tal. Esto se aplica tanto a las operaciones de lectura como de escritura. Esta propiedad no se mantendría si el tamaño se almacenara explícitamente.

MSalters
fuente
66
"El diseño sería diferente" no es una razón para que el diseño sea diferente.
VF1
77
@ VF1: ¿Alguna vez ha programado en Pascal estándar? La capacidad de C de ser razonablemente flexible con las matrices fue una gran mejora con respecto al ensamblaje (sin seguridad en absoluto) y la primera generación de lenguajes de escritura segura (seguridad de escritura excesiva, incluidos los límites exactos de la matriz)
MSalters
55
Esta capacidad de cortar una matriz es de hecho un argumento masivo para el diseño C89.
Los hackers de Fortran de la vieja escuela también hacen un buen uso de esta propiedad (aunque requiere pasar la porción a una matriz en Fortran). Confuso y doloroso para programar o depurar, pero rápido y elegante cuando se trabaja.
dmckee
3
Hay una alternativa de diseño interesante que permite el corte: no almacene la longitud junto con las matrices. Para cualquier puntero a una matriz, almacene la longitud con el puntero. (Cuando solo tiene una matriz C real, el tamaño es una constante de tiempo de compilación y está disponible para el compilador). Se necesita más espacio, pero permite cortar mientras mantiene la longitud. Rust hace esto para los &[T]tipos, por ejemplo.
8

El mayor problema con tener matrices etiquetadas con su longitud no es tanto el espacio requerido para almacenar esa longitud, ni la cuestión de cómo debe almacenarse (usar un byte adicional para matrices cortas generalmente no sería objetable, ni tampoco usar cuatro bytes adicionales para matrices largas, pero puede ser el uso de cuatro bytes incluso para matrices cortas). Un problema mucho mayor es ese código dado como:

void ClearTwoElements(int *ptr)
{
  ptr[-2] = 0;
  ptr[2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo+2);
  ClearTwoElements(foo+7);
  ClearTwoElements(foo+1);
  ClearTwoElements(foo+8);
}

la única forma en que ese código podría aceptar la primera llamada ClearTwoElementspero rechazar la segunda sería que el ClearTwoElementsmétodo recibiera información suficiente para saber que en cada caso estaba recibiendo una referencia a parte de la matriz fooademás de saber qué parte. Eso normalmente duplicaría el costo de pasar los parámetros del puntero. Además, si cada matriz fue precedida por un puntero a una dirección justo después del final (el formato más eficiente para la validación), el código optimizado para ClearTwoElementsprobablemente se convertiría en algo como:

void ClearTwoElements(int *ptr)
{
  int* array_end = ARRAY_END(ptr);
  if ((array_end - ARRAY_BASE(ptr)) < 10 ||
      (ARRAY_BASE(ptr)+4) <= ADDRESS(ptr) ||          
      (array_end - 4) < ADDRESS(ptr)))
    trap();
  *(ADDRESS(ptr) - 4) = 0;
  *(ADDRESS(ptr) + 4) = 0;
}

Tenga en cuenta que una persona que llama al método podría, en general, pasar un puntero al inicio de la matriz o el último elemento a un método de forma legítima; solo si el método intenta acceder a elementos que van fuera de la matriz pasada, tales punteros causarían algún problema. En consecuencia, un método llamado tendría que asegurarse primero de que la matriz fuera lo suficientemente grande como para que la aritmética del puntero para validar sus argumentos no se salga de los límites, y luego haga algunos cálculos de puntero para validar los argumentos. El tiempo empleado en dicha validación probablemente excedería el costo dedicado a realizar cualquier trabajo real. Además, el método podría ser más eficiente si se escribiera y se llamara:

void ClearTwoElements(int arr[], int index)
{
  arr[index-2] = 0;
  arr[index+2] = 0;
}
void blah(void)
{
  static int foo[10] = {1,2,3,4,5,6,7,8,9,10};
  ClearTwoElements(foo,2);
  ClearTwoElements(foo,7);
  ClearTwoElements(foo,1);
  ClearTwoElements(foo,8);
}

El concepto de un tipo que combina algo para identificar un objeto con algo para identificar una pieza del mismo es bueno. Sin embargo, un puntero estilo C es más rápido si no es necesario realizar la validación.

Super gato
fuente
Si las matrices tuvieran un tamaño de tiempo de ejecución, el puntero a la matriz sería fundamentalmente diferente del puntero a un elemento de la matriz. Es posible que este último no sea directamente convertible a anterior (sin crear una nueva matriz). []La sintaxis aún podría existir para los punteros, pero sería diferente a la de estos arreglos hipotéticos "reales", y el problema que describas probablemente no existiría.
hyde
@hyde: La pregunta es si se debe permitir la aritmética en punteros cuya dirección base de objeto es desconocida. Además, olvidé otra dificultad: las matrices dentro de las estructuras. Pensando en ello, no estoy seguro de que haya alguno que tenga un tipo de puntero que pueda apuntar a una matriz almacenada dentro de una estructura, sin requerir que cada puntero incluya no solo la dirección del puntero en sí, sino también las letras legales superior e inferior rangos a los que puede acceder.
supercat
Punto de intersección. Sin embargo, creo que esto todavía se reduce a la respuesta de amon.
VF1
La pregunta se refiere a las matrices. El puntero es la dirección de memoria y no cambiaría con la premisa de la pregunta, en la medida en que comprenda la intención. Las matrices obtendrían longitud, los punteros no cambiarían (excepto que el puntero a la matriz debería ser un tipo nuevo, distinto y único, muy parecido al puntero a la estructura).
hyde
@hyde: Si uno cambiara suficientemente la semántica del lenguaje, podría ser posible que las matrices incluyeran una longitud asociada, aunque las matrices almacenadas dentro de las estructuras plantearían algunas dificultades. Con la semántica tal como son, la verificación de límites de matriz solo sería útil si esa misma verificación se aplicara a punteros a elementos de matriz.
supercat
7

Una de las diferencias fundamentales entre C y la mayoría de los otros lenguajes de tercera generación, y todos los lenguajes más recientes que conozco, es que C no fue diseñado para hacer la vida más fácil o segura para el programador. Fue diseñado con la expectativa de que el programador sabía lo que estaban haciendo y quería hacer exactamente y solo eso. No hace nada "detrás de escena" para que no te sorprendas. Incluso la optimización de nivel de compilador es opcional (a menos que use un compilador de Microsoft).

Si un programador quiere escribir límites comprobando su código, C hace que sea lo suficientemente simple como para hacerlo, pero el programador debe elegir pagar el precio correspondiente en términos de espacio, complejidad y rendimiento. Aunque no lo he usado con ira en muchos años, todavía lo uso al enseñar programación para transmitir el concepto de toma de decisiones basada en restricciones. Básicamente, eso significa que puede elegir hacer lo que quiera, pero cada decisión que tome tiene un precio que debe tener en cuenta. Esto se vuelve aún más importante cuando comienzas a decirles a los demás lo que quieres que hagan sus programas.

Paul Smith
fuente
3
C no fue tan "diseñado" como evolucionó. Originalmente, una declaración como int f[5];no se crearía fcomo una matriz de cinco elementos; en cambio, era equivalente a int CANT_ACCESS_BY_NAME[5]; int *f = CANT_ACCESS_BY_NAME;. La declaración anterior podría procesarse sin que el compilador tuviera que realmente "comprender" los tiempos de la matriz; simplemente tenía que generar una directiva de ensamblador para asignar espacio y luego podría olvidar que falguna vez tuvo algo que ver con una matriz. Los comportamientos inconsistentes de los tipos de matriz se derivan de esto.
supercat
1
Resulta que ningún programador sabe lo que está haciendo en el grado que C requiere.
CodesInChaos
7

Respuesta corta:

Debido a que C es un lenguaje de programación de bajo nivel , espera que usted se encargue de estos problemas usted mismo, pero esto agrega una mayor flexibilidad en cómo exactamente lo implementa.

C tiene un concepto de tiempo de compilación de una matriz que se inicializa con una longitud, pero en tiempo de ejecución todo se almacena simplemente como un puntero único al inicio de los datos. Si desea pasar la longitud de la matriz a una función junto con la matriz, hágalo usted mismo:

retval = my_func(my_array, my_array_length);

O podría usar una estructura con puntero y longitud, o cualquier otra solución.

Un lenguaje de nivel superior haría esto por usted como parte de su tipo de matriz. En C se le da la responsabilidad de hacerlo usted mismo, pero también la flexibilidad de elegir cómo hacerlo. Y si todo el código que está escribiendo ya conoce la longitud de la matriz, no necesita pasar la longitud como una variable.

El inconveniente obvio es que sin verificar los límites inherentes en las matrices que se pasan como punteros, puede crear un código peligroso, pero esa es la naturaleza de los lenguajes de bajo nivel / sistemas y la compensación que ofrecen.

thomasrutter
fuente
1
+1 "Y si todo el código que está escribiendo ya conoce la longitud de la matriz, no necesita pasar la longitud como una variable".
皞 皞
Si solo la estructura de puntero + longitud se hubiera integrado en el lenguaje y la biblioteca estándar. Se podrían haber evitado tantos agujeros de seguridad.
CodesInChaos
Entonces no sería realmente C. Hay otros idiomas que hacen eso. C te pone bajo nivel.
thomasrutter
C fue inventado como un lenguaje de programación de bajo nivel, y muchos dialectos aún admiten programación de bajo nivel, pero muchos escritores de compiladores prefieren dialectos que realmente no pueden llamarse lenguajes de bajo nivel. Permiten e incluso requieren sintaxis de bajo nivel, pero luego intentan inferir construcciones de nivel superior cuyo comportamiento puede no coincidir con la semántica implícita en la sintaxis.
supercat
5

El problema del almacenamiento adicional es un problema, pero en mi opinión, uno menor. Después de todo, la mayoría de las veces tendrá que rastrear la longitud de todos modos, aunque amon hizo un buen punto de que a menudo se puede rastrear estáticamente.

Un problema mayor es dónde almacenar la longitud y cuánto tiempo para hacerlo. No hay un solo lugar que funcione en todas las situaciones. Podría decir que simplemente almacene la longitud en la memoria justo antes de los datos. ¿Qué pasa si la matriz no apunta a la memoria, sino algo así como un búfer UART?

Dejar el espacio extendido le permite al programador crear sus propias abstracciones para la situación apropiada, y hay muchas bibliotecas preparadas disponibles para el caso de propósito general. La verdadera pregunta es ¿por qué no se utilizan esas abstracciones en aplicaciones sensibles a la seguridad?

Karl Bielefeldt
fuente
1
You might say just store the length in the memory just before the data. What if the array isn't pointing to memory, but something like a UART buffer?¿Podrías explicar esto un poco más? ¿También ese algo que podría suceder con demasiada frecuencia o es solo un caso raro?
Mahdi
Si lo hubiera diseñado, un argumento de función escrito como T[]no sería equivalente, T*sino que pasaría una tupla de puntero y tamaño a la función. Las matrices de tamaño fijo podrían decaer a un segmento de matriz de este tipo, en lugar de decaer a punteros como lo hacen en C. La principal ventaja de este enfoque no es que sea seguro en sí mismo, sino que es una convención en la que todo, incluida la biblioteca estándar, puede construir.
CodesInChaos
1

Del desarrollo del lenguaje C :

Al parecer, las estructuras deberían mapearse de forma intuitiva en la memoria de la máquina, pero en una estructura que contiene una matriz, no había un buen lugar para esconder el puntero que contenía la base de la matriz, ni ninguna forma conveniente de organizarlo. inicializado Por ejemplo, las entradas de directorio de los primeros sistemas Unix podrían describirse en C como
struct {
    int inumber;
    char    name[14];
};
Quería que la estructura no solo caracterizara un objeto abstracto, sino que también describiera una colección de bits que pudieran leerse desde un directorio. ¿Dónde podría el compilador ocultar el puntero al nameque exigía la semántica? Incluso si las estructuras se pensaran de manera más abstracta, y el espacio para los punteros pudiera ocultarse de alguna manera, ¿cómo podría manejar el problema técnico de inicializar adecuadamente estos punteros al asignar un objeto complicado, tal vez uno que especifica estructuras que contienen matrices que contienen estructuras a una profundidad arbitraria?

La solución constituyó el salto crucial en la cadena evolutiva entre BCPL sin tipo y tipo C. Eliminó la materialización del puntero almacenado y, en cambio, causó la creación del puntero cuando el nombre de la matriz se menciona en una expresión. La regla, que sobrevive en la C de hoy, es que los valores del tipo de matriz se convierten, cuando aparecen en expresiones, en punteros al primero de los objetos que forman la matriz.

Ese pasaje aborda por qué las expresiones de matriz decaen a punteros en la mayoría de las circunstancias, pero el mismo razonamiento se aplica a por qué la longitud de la matriz no se almacena con la matriz misma; Si desea un mapeo uno a uno entre la definición de tipo y su representación en la memoria (como lo hizo Ritchie), entonces no hay un buen lugar para almacenar esos metadatos.

Además, piense en matrices multidimensionales; ¿Dónde almacenaría los metadatos de longitud para cada dimensión de modo que aún pudiera recorrer la matriz con algo como

T *p = &a[0][0];

for ( size_t i = 0; i < rows; i++ )
  for ( size_t j = 0; j < cols; j++ )
    do_something_with( *p++ );
John Bode
fuente
-2

La pregunta supone que hay matrices en C. No las hay. Las cosas que se llaman matrices son solo un azúcar sintáctico para operaciones en secuencias continuas de datos y aritmética de punteros.

El siguiente código copia algunos datos de src a dst en fragmentos de tamaño int sin saber que en realidad es una cadena de caracteres.

char src[] = "Hello, world";
char dst[1024];
int *my_array = src; /* What? Compiler warning, but the code is valid. */
int *other_array = dst;
int i;
for (i = 0; i <= sizeof(src)/sizeof(int); i++)
    other_array[i] = my_array[i]; /* Oh well, we've copied some extra bytes */
printf("%s\n", dst);

¿Por qué C está tan simplificado que no tiene matrices adecuadas? No sé la respuesta correcta a esta nueva pregunta. Pero algunas personas a menudo dicen que C es simplemente (algo) un ensamblador más legible y portátil.

aragaer
fuente
2
No creo que hayas respondido la pregunta.
Robert Harvey
2
Lo que dijiste es cierto, pero la persona que pregunta quiere saber por qué es así.
99
Recuerde, uno de los apodos para C es "ensamblaje portátil". Si bien las versiones más nuevas del estándar han agregado conceptos de nivel superior, en esencia, consisten en construcciones simples de bajo nivel e instrucciones que son comunes en la mayoría de las máquinas no triviales. Esto impulsa la mayoría de las decisiones de diseño tomadas en el lenguaje. Las únicas variables que existen en tiempo de ejecución son enteros, flotantes y punteros. Las instrucciones incluyen aritmética, comparaciones y saltos. Casi todo lo demás es una construcción de capa delgada además de eso.
8
Es incorrecto decir que C no tiene matrices, considerando cómo realmente no puede generar el mismo binario con otras construcciones (bueno, al menos no si considera el uso de #defines para determinar los tamaños de matriz). Las matrices en C son "secuencias continuas de datos", no tienen nada de dulce. El uso de punteros como si fueran matrices es el azúcar sintáctico aquí (en lugar de la aritmética de puntero explícito), no las matrices en sí mismas.
hyde
2
Sí, tenga en cuenta este código: struct Foo { int arr[10]; }. arres una matriz, no un puntero.
Gort the Robot