Por que as matrizes C não controlam seu comprimento?

77

Qual foi o motivo por trás de não armazenar explicitamente o comprimento de uma matriz com uma matriz em C?

Do meu ponto de vista, há razões esmagadoras para fazê-lo, mas não muitas em apoio ao padrão (C89). Por exemplo:

  1. Ter o comprimento disponível em um buffer pode impedir a saturação do buffer.
  2. Um estilo Java arr.lengthé claro e evita que o programador precise manter muitos ints na pilha se estiver lidando com várias matrizes
  3. Os parâmetros de função se tornam mais convincentes.

Mas talvez a razão mais motivadora, na minha opinião, seja que, geralmente, nenhum espaço é economizado sem manter o comprimento. Atrevo-me a dizer que a maioria dos usos de matrizes envolve alocação dinâmica. É verdade que pode haver alguns casos em que as pessoas usam uma matriz alocada na pilha, mas essa é apenas uma chamada de função * - a pilha pode lidar com 4 ou 8 bytes extras.

Como o gerenciador de heap precisa rastrear o tamanho do bloco livre usado pela matriz alocada dinamicamente de qualquer maneira, por que não tornar essas informações utilizáveis ​​(e adicionar a regra adicional, verificada no tempo de compilação, que não é possível manipular o comprimento explicitamente, a menos que seja necessário? gosta de dar um tiro no próprio pé).

A única coisa que posso pensar no outro lado é que nenhum seguimento comprimento pode ter feito compiladores mais simples, mas não que muito mais simples.

* Tecnicamente, pode-se escrever algum tipo de função recursiva com uma matriz com armazenamento automático e, nesse caso (muito elaborado), armazenar o comprimento pode realmente resultar em um uso de espaço efetivamente maior.

VF1
fonte
6
Suponho que se possa argumentar que, quando C incluísse o uso de estruturas como parâmetro e retorne tipos de valor, deveria incluir açúcar sintático para "vetores" (ou qualquer nome), que embaixo de struct com length e matriz ou ponteiro para matriz . O suporte no nível de linguagem para essa construção comum (também quando passado como argumentos separados e não como uma estrutura única) também teria economizado inúmeros erros e simplificado a biblioteca padrão.
Hyde
3
Você também pode descobrir por que o Pascal não é meu idioma favorito de programação, Seção 2.1, para ser esclarecedor.
34
Enquanto todas as outras respostas têm alguns pontos interessantes, acho que a conclusão é que C foi escrito para que os programadores de linguagem assembly pudessem escrever código mais facilmente e que fosse portátil. Com isso em mente, ter um comprimento de matriz armazenado com uma matriz automaticamente teria sido um incômodo e não uma falha (como teriam outros bons desejos de revestimento de doces). Atualmente, esses recursos parecem bons, mas na época era realmente difícil esforçar mais um byte de programa ou dados em seu sistema. O uso desnecessário de memória teria limitado severamente a adoção de C.
Dunk
6
A parte real da sua resposta já foi respondida muitas vezes da maneira que eu gostaria, mas posso extrair um ponto diferente: "Por que o tamanho de uma malloc()área ed não pode ser solicitado de maneira portátil?" Isso é algo que me faz pensar várias vezes.
glglgl
5
Votação para reabrir. Há alguma razão em algum lugar, mesmo que seja simplesmente "K&R não pensou nisso".
Telastyn

Respostas:

106

As matrizes C controlam seu comprimento, pois o comprimento da matriz é uma propriedade estática:

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

Normalmente, você não pode consultar esse comprimento, mas não precisa, porque é estático de qualquer maneira - basta declarar uma macro XS_LENGTHpara o comprimento e pronto.

A questão mais importante é que as matrizes C se degradam implicitamente em ponteiros, por exemplo, quando passadas para uma função. Isso faz algum sentido e permite alguns bons truques de baixo nível, mas perde as informações sobre o comprimento da matriz. Portanto, uma pergunta melhor seria por que C foi projetado com essa degradação implícita em ponteiros.

Outra questão é que os ponteiros não precisam de armazenamento, exceto o próprio endereço de memória. C nos permite converter números inteiros para ponteiros, ponteiros para outros ponteiros e tratar ponteiros como se fossem matrizes. Enquanto isso, C não é insano o suficiente para fabricar um certo comprimento de matriz, mas parece confiar no lema do Homem-Aranha: com grande poder, o programador cumprirá a grande responsabilidade de acompanhar os comprimentos e transbordamentos.

amon
fonte
13
Eu acho que você quer dizer, se não me engano, que os compiladores C controlam os comprimentos de matriz estática. Mas isso não é bom para funções que apenas recebem um ponteiro.
VF1
25
@ VF1 sim. Mas o importante é que arrays e ponteiros são coisas diferentes em C . Supondo que você não esteja usando nenhuma extensão do compilador, geralmente não é possível passar um array em si para uma função, mas você pode passar um ponteiro e indexar um ponteiro como se fosse um array. Você está efetivamente reclamando que os ponteiros não têm comprimento anexado. Você deve reclamar que matrizes não podem ser passadas como argumentos de função ou que matrizes são degradadas em ponteiros implicitamente.
amon
37
"Normalmente, você não pode consultar esse comprimento" - na verdade, pode, é o operador sizeof - sizeof (xs) retornaria 168 assumindo que int's tem quatro bytes de comprimento. Para obter o 42, fazer: sizeof (xs) / sizeof (int)
tcrosley
15
@tcrosley Isso só funciona no âmbito da declaração de matriz - tente passar xs como um param para outra função, em seguida, ver o que sizeof (xs) dá-lhe ...
Gwyn Evans
26
@ GwynEvans novamente: ponteiros não são matrizes. Portanto, se você "passa uma matriz como parâmetro para outra função", não está passando uma matriz, mas um ponteiro. Afirmar que sizeof(xs)where xsé uma matriz seria algo diferente em outro escopo é descaradamente falso, porque o design de C não permite que as matrizes deixem seu escopo. Se sizeof(xs)where xsé uma matriz diferente de sizeof(xs)onde xsestá um ponteiro, isso não é surpresa, porque você está comparando maçãs com laranjas .
amon
38

Muito disso tinha a ver com os computadores disponíveis no momento. Não apenas o programa compilado teve que ser executado em um computador com recursos limitados, mas, talvez mais importante, o próprio compilador teve que ser executado nessas máquinas. Na época, Thompson desenvolveu o C, ele estava usando um PDP-7, com 8k de RAM. Recursos complexos de linguagem que não tinham um analógico imediato no código de máquina real simplesmente não foram incluídos no idioma.

Uma leitura cuidadosa do histórico de C gera mais entendimento do que foi dito acima, mas não foi inteiramente o resultado das limitações da máquina que eles tinham:

Além disso, a linguagem (C) mostra um poder considerável para descrever conceitos importantes, por exemplo, vetores cuja duração varia em tempo de execução, com apenas algumas regras e convenções básicas. ... É interessante comparar a abordagem de C com a de duas línguas quase contemporâneas, Algol 68 e Pascal [Jensen 74]. As matrizes no Algol 68 têm limites fixos ou são `flexíveis: 'é necessário um mecanismo considerável tanto na definição da linguagem quanto nos compiladores, para acomodar as matrizes flexíveis (e nem todos os compiladores as implementam totalmente.) O original Pascal tinha apenas tamanho fixo matrizes e strings, e isso se mostrou confinante [Kernighan 81].

Matrizes C são inerentemente mais poderosas. Adicionar limites a eles restringe o que o programador pode usá-los. Tais restrições podem ser úteis para programadores, mas necessariamente também são limitativas.

Adam Davis
fonte
4
Isso praticamente prega a questão original. Isso e o fato de C estar sendo mantido deliberadamente "leve" quando se tratava de verificar o que o programador estava fazendo, como parte de torná-lo atraente para a criação de sistemas operacionais.
ClickRick
5
Grande link, eles também mudou explicitamente armazenar o comprimento das cordas de usar um 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- bem tanto para que :-)
Voo
5
As matrizes não terminadas também se encaixam na abordagem bare metal de C. Lembre-se de que o livro K&R C tem menos de 300 páginas com um tutorial de idioma, referência e uma lista das chamadas padrão. Meu livro O'Reilly Regex é quase o dobro do tempo K & R C.
Michael Shopsin
22

No dia em que C foi criado, e 4 bytes de espaço extra para cada string, por mais curto que tenha sido um desperdício!

Há outro problema - lembre-se de que C não é orientado a objetos; portanto, se você usar o prefixo do comprimento de todas as strings, ele deverá ser definido como um tipo intrínseco do compilador, não a char*. Se fosse um tipo especial, não seria possível comparar uma sequência com uma constante, ou seja:

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

teria que ter detalhes especiais do compilador para converter essa string estática em uma String ou ter funções diferentes de string para levar em consideração o prefixo do comprimento.

Acho que, no final das contas, eles simplesmente não escolheram o prefixo de comprimento, diferentemente do que diz Pascal.

gbjbaanb
fonte
10
A verificação de limites também leva tempo. Trivial nos termos de hoje, mas algo em que as pessoas prestavam atenção quando se importavam com 4 bytes.
Gort the Robot
18
@StevenBurnap: não é tão trivial até hoje se você estiver em um loop interno que percorre todos os pixels de uma imagem de 200 MB. Em geral, se você está escrevendo C, deseja ir mais rápido e não deseja perder tempo em uma verificação limitada inútil a cada iteração quando o forloop já estiver configurado para respeitar os limites.
Matteo Italia
4
@ VF1 "de volta ao dia" poderia muito bem ter sido de dois bytes (DEC PDP / 11 alguém?)
ClickRick
7
Não é apenas "voltar no dia". O para o software que C é direcionado como uma "linguagem assembly portátil", como kernals do SO, drivers de dispositivo, software incorporado em tempo real etc. desperdiçar meia dúzia de instruções na verificação de limites é importante e, em muitos casos, você precisa estar "fora dos limites" (como você pode escrever um depurador se não pode acessar aleatoriamente outro armazenamento de programas?).
James Anderson
3
Este é realmente um argumento bastante fraco, considerando que o BCPL tinha argumentos contados em tamanho. Assim como Pascal, embora isso fosse limitado a 1 palavra, geralmente apenas 8 ou 9 bits, o que era um pouco limitante (também impede a possibilidade de compartilhar partes de strings, embora essa otimização provavelmente tenha sido muito avançada para a época). E declarar uma string como um struct com um comprimento seguido pela matriz realmente não precisam de apoio especial compilador ..
Voo
11

Em C, qualquer subconjunto contíguo de uma matriz também é uma matriz e pode ser operado como tal. Isso se aplica às operações de leitura e gravação. Esta propriedade não se manteria se o tamanho fosse armazenado explicitamente.

MSalters
fonte
6
"O design seria diferente" não é uma razão para o design ser diferente.
VF1 28/04
7
@ VF1: Você já programou no Standard Pascal? A capacidade do C de ser razoavelmente flexível com matrizes foi uma grande melhoria sobre a montagem (sem segurança qualquer) ea primeira geração de linguagens typesafe (typesafety exagero, incluindo limites de matriz exatas)
MSalters
5
Essa capacidade de dividir uma matriz é realmente um argumento massivo para o design do C89.
Os hackers Fortran da velha escola também fazem um bom uso dessa propriedade (embora exija passar a fatia para uma matriz no Fortran). Confuso e doloroso para programar ou depurar, mas rápido e elegante ao trabalhar.
dmckee
3
Existe uma alternativa interessante de design que permite fatiar: não armazene o comprimento ao lado das matrizes. Para qualquer ponteiro para uma matriz, armazene o comprimento com o ponteiro. (Quando você tem apenas uma matriz C real, o tamanho é uma constante de tempo de compilação e está disponível para o compilador.) Isso ocupa mais espaço, mas permite fatiar enquanto mantém o comprimento. Ferrugem faz isso para os &[T]tipos, por exemplo.
8

O maior problema em ter matrizes marcadas com seu comprimento não é tanto o espaço necessário para armazenar esse comprimento, nem a questão de como ele deve ser armazenado (o uso de um byte extra para matrizes curtas geralmente não seria questionável, nem o uso de quatro bytes extras para matrizes longas, mas usar quatro bytes mesmo para matrizes curtas pode ser). Um problema muito maior é esse código, 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);
}

a única maneira pela qual o código seria capaz de aceitar a primeira chamada, ClearTwoElementsmas rejeitar a segunda, seria o ClearTwoElementsmétodo receber informações suficientes para saber que, em cada caso, estava recebendo uma referência a parte da matriz foo, além de saber qual parte. Isso normalmente dobraria o custo de passar os parâmetros do ponteiro. Além disso, se cada matriz fosse precedida por um ponteiro para um endereço logo após o final (o formato mais eficiente para validação), o código otimizado para ClearTwoElementsprovavelmente se tornaria 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;
}

Observe que um responsável pela chamada de método pode, em geral, perfeitamente legitimamente transmitir um ponteiro para o início da matriz ou o último elemento para um método; somente se o método tentar acessar elementos que vão para fora da matriz passada, esses ponteiros causarão algum problema. Conseqüentemente, um método chamado precisaria primeiro garantir que a matriz fosse grande o suficiente para que o aritmético do ponteiro para validar seus argumentos não saia dos limites e faça alguns cálculos de ponteiro para validar os argumentos. O tempo gasto em tal validação provavelmente excederia o custo gasto com qualquer trabalho real. Além disso, o método provavelmente poderia ser mais eficiente se fosse escrito e chamado:

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);
}

O conceito de um tipo que combina algo para identificar um objeto com algo para identificar uma parte dele é bom. Um ponteiro no estilo C é mais rápido, no entanto, se não for necessário executar a validação.

supercat
fonte
Se as matrizes tiverem tamanho de tempo de execução, o ponteiro para a matriz será fundamentalmente diferente do ponteiro para um elemento da matriz. Os últimos podem não ser diretamente conversíveis em antigos (sem a criação de uma nova matriz). []a sintaxe ainda pode existir para ponteiros, mas seria diferente do que para essas matrizes hipotéticas "reais" e o problema que você descreve provavelmente não existiria.
Hyde
@hyde: A questão é se a aritmética deve ser permitida em ponteiros cujo endereço base do objeto é desconhecido. Além disso, esqueci outra dificuldade: matrizes dentro de estruturas. Pensando nisso, não sei se haveria um tipo de ponteiro que pudesse apontar para uma matriz armazenada dentro de uma estrutura, sem exigir que cada ponteiro incluísse não apenas o endereço do ponteiro em si, mas também as letras maiúsculas e minúsculas. intervalos que pode acessar.
Supercat
Ponto de interseção. Eu acho que isso ainda se reduz à resposta de amon, no entanto.
VF1
A pergunta pergunta sobre matrizes. Ponteiro é o endereço da memória e não mudaria com a premissa da pergunta, na medida em que entendesse a intenção. As matrizes teriam comprimento, os ponteiros não seriam alterados (exceto que o ponteiro para a matriz precisaria ser um tipo novo, distinto e exclusivo, bem como o ponteiro para estruturar).
Hyde
@hyde: Se alguém alterasse suficientemente a semântica da linguagem, seria possível que as matrizes incluíssem um comprimento associado, embora as matrizes armazenadas nas estruturas apresentem algumas dificuldades. Com a semântica em si, a verificação de limites da matriz só seria útil se a mesma verificação se aplicasse aos ponteiros dos elementos da matriz.
precisa saber é
7

Uma das diferenças fundamentais entre C e a maioria das outras linguagens de terceira geração, e todas as linguagens mais recentes que conheço, é que C não foi projetado para tornar a vida mais fácil ou segura para o programador. Foi projetado com a expectativa de que o programador soubesse o que estava fazendo e desejasse fazer exatamente e somente isso. Ele não faz nada nos bastidores, para que você não tenha surpresas. Até a otimização no nível do compilador é opcional (a menos que você use um compilador da Microsoft).

Se um programador deseja escrever limites de verificação em seu código, C torna mais simples, mas o programador deve optar por pagar o preço correspondente em termos de espaço, complexidade e desempenho. Embora eu não o use com raiva por muitos anos, ainda o uso quando ensino programação para entender o conceito de tomada de decisão baseada em restrições. Basicamente, isso significa que você pode optar por fazer o que quiser, mas todas as decisões tomadas têm um preço que você precisa estar ciente. Isso se torna ainda mais importante quando você começa a dizer aos outros o que deseja que os programas deles façam.

Paul Smith
fonte
3
C não foi tão "projetado" quanto evoluiu. Originalmente, uma declaração como int f[5];não criaria fcomo uma matriz de cinco itens; em vez disso, foi equivalente a int CANT_ACCESS_BY_NAME[5]; int *f = CANT_ACCESS_BY_NAME;. A declaração anterior poderia ser processada sem que o compilador realmente "entendesse" os tempos da matriz; ele simplesmente precisava emitir uma diretiva assembler para alocar espaço e, em seguida, poderia esquecer que falguma vez tinha algo a ver com uma matriz. Os comportamentos inconsistentes dos tipos de matriz decorrem disso.
Supercat
1
Acontece que nenhum programador sabe o que está fazendo na medida em que C exige.
código é o seguinte
7

Resposta curta:

Como C é uma linguagem de programação de baixo nível , ele espera que você cuide desses problemas, mas isso adiciona maior flexibilidade exatamente à maneira como você o implementa.

C tem um conceito em tempo de compilação de uma matriz que é inicializada com um comprimento, mas em tempo de execução a coisa toda é simplesmente armazenada como um ponteiro único para o início dos dados. Se você deseja passar o comprimento da matriz para uma função junto com a matriz, faça você mesmo:

retval = my_func(my_array, my_array_length);

Ou você pode usar uma estrutura com um ponteiro e comprimento, ou qualquer outra solução.

Uma linguagem de nível superior faria isso por você como parte de seu tipo de matriz. Em C, você tem a responsabilidade de fazer isso sozinho, mas também a flexibilidade de escolher como fazê-lo. E se todo o código que você está escrevendo já sabe o comprimento da matriz, você não precisa passar o comprimento como uma variável.

A desvantagem óbvia é que, sem a verificação de limites inerentes às matrizes passadas como ponteiros, você pode criar um código perigoso, mas essa é a natureza das linguagens de baixo nível / sistemas e o trade-off que elas oferecem.

thomasrutter
fonte
1
+1 "E se todo o código que você está escrevendo já sabe o comprimento da matriz, você não precisa passar o comprimento como uma variável."
林果皞
Se apenas a estrutura do ponteiro + comprimento tivesse sido inserida na linguagem e na biblioteca padrão. Tantas falhas de segurança poderiam ter sido evitadas.
código é o seguinte
Então não seria realmente C. Existem outras línguas que fazem isso. C leva você a um nível baixo.
thomasrutter
C foi inventado como uma linguagem de programação de baixo nível, e muitos dialetos ainda suportam programação de baixo nível, mas muitos escritores de compiladores preferem dialetos que não podem realmente ser chamados de linguagens de baixo nível. Eles permitem e até exigem sintaxe de baixo nível, mas tentam inferir construções de nível superior cujo comportamento pode não corresponder à semântica implícita na sintaxe.
Supercat
5

O problema do armazenamento extra é um problema, mas, na minha opinião, um problema menor. Afinal, na maioria das vezes você precisará rastrear o comprimento de qualquer maneira, embora amon tenha enfatizado que muitas vezes pode ser rastreado estaticamente.

Um problema maior é onde armazenar o comprimento e quanto tempo durar. Não há um lugar que funcione em todas as situações. Você pode dizer apenas armazene o comprimento na memória antes dos dados. E se a matriz não estiver apontando para a memória, mas algo como um buffer UART?

Deixar de lado o comprimento permite que o programador crie suas próprias abstrações para a situação apropriada, e há muitas bibliotecas prontas disponíveis para o caso de uso geral. A verdadeira questão é por que essas abstrações não estão sendo usadas em aplicativos sensíveis à segurança?

Karl Bielefeldt
fonte
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?Poderia explicar isso um pouco mais? Além disso, algo que pode acontecer com muita frequência ou é apenas um caso raro?
Mahdi
Se eu o tivesse projetado, um argumento de função escrito como T[]não seria equivalente, T*mas passaria uma tupla de ponteiro e tamanho para a função. Matrizes de tamanho fixo podem se deteriorar para uma fatia dessa matriz, em vez de se deteriorar para ponteiros como em C. A principal vantagem dessa abordagem não é que ela é segura por si só, mas é uma convenção na qual tudo, incluindo a biblioteca padrão, pode Construir.
código é o seguinte
1

Do desenvolvimento da linguagem C :

As estruturas, ao que parece, deveriam mapear de maneira intuitiva a memória da máquina, mas em uma estrutura que contenha uma matriz, não havia um bom lugar para esconder o ponteiro que continha a base da matriz, nem qualquer maneira conveniente de organizá-la. inicializado. Por exemplo, as entradas de diretório dos primeiros sistemas Unix podem ser descritas em C como
struct {
    int inumber;
    char    name[14];
};
Queria que a estrutura não apenas caracterizasse um objeto abstrato, mas também descrevesse uma coleção de bits que podem ser lidos em um diretório. Onde o compilador poderia ocultar o ponteiro para o nameque a semântica exigia? Mesmo se as estruturas fossem pensadas de maneira mais abstrata e o espaço para ponteiros pudesse ser oculto de alguma forma, como eu poderia lidar com o problema técnico de inicializar adequadamente esses ponteiros ao alocar um objeto complicado, talvez um que especificasse estruturas contendo matrizes contendo estruturas com profundidade arbitrária?

A solução constituiu o salto crucial na cadeia evolutiva entre o BCPL sem tipo e o tipo C. Ele eliminou a materialização do ponteiro no armazenamento e, em vez disso, causou a criação do ponteiro quando o nome do array é mencionado em uma expressão. A regra, que sobrevive no C de hoje, é que os valores do tipo de matriz são convertidos, quando aparecem em expressões, em ponteiros para o primeiro dos objetos que compõem a matriz.

Essa passagem aborda por que as expressões de matriz decaem para ponteiros na maioria das circunstâncias, mas o mesmo raciocínio se aplica ao motivo pelo qual o comprimento da matriz não é armazenado na própria matriz; se você deseja um mapeamento individual entre a definição de tipo e sua representação na memória (como Ritchie fez), não há um bom lugar para armazenar esses metadados.

Além disso, pense em matrizes multidimensionais; onde você armazenaria os metadados de comprimento para cada dimensão, para poder percorrer a matriz com 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
fonte
-2

A questão assume que existem matrizes em C. Não há. As coisas que são chamadas de matrizes são apenas um açúcar sintático para operações em seqüências contínuas de dados e aritmética de ponteiros.

O código a seguir copia alguns dados do src para o dst em partes com tamanho int sem saber que na verdade é uma sequência 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 que C é tão simplificado que não possui matrizes apropriadas? Não sei a resposta correta para esta nova pergunta. Mas algumas pessoas costumam dizer que C é apenas (um pouco) montador mais legível e portátil.

aragaer
fonte
2
Acho que você não respondeu à pergunta.
Robert Harvey
2
O que você disse é verdade, mas a pessoa que pergunta quer saber por que esse é o caso.
9
Lembre-se, um dos apelidos para C é "montagem portátil". Embora as versões mais recentes do padrão tenham adicionado conceitos de nível superior, em seu núcleo, ele consiste em construções e instruções simples de baixo nível, comuns na maioria das máquinas não triviais. Isso impulsiona a maioria das decisões de design tomadas no idioma. As únicas variáveis ​​que existem no tempo de execução são números inteiros, flutuantes e ponteiros. As instruções incluem aritmética, comparações e saltos. Praticamente tudo o resto é uma camada fina construída em cima disso.
8
É errado dizer que C não tem matrizes, considerando como você realmente não pode gerar o mesmo binário com outras construções (bem, pelo menos não se você considerar o uso de #defines para determinar os tamanhos das matrizes). Matrizes em C são "sequências contínuas de dados", nada de açucarado. Usar ponteiros como se fossem matrizes é o açúcar sintático aqui (em vez da aritmética explícita dos ponteiros), não as próprias matrizes.
Hyde
2
Sim, considere o seguinte código: struct Foo { int arr[10]; }. arré uma matriz, não um ponteiro.
Gort the Robot