Depois de ter visto (e perguntado!) Tantas perguntas semelhantes a
O que
int (*f)(int (*a)[5])
significa em C?
e mesmo vendo que eles criaram um programa para ajudar as pessoas a entender a sintaxe C, não posso deixar de me perguntar:
Por que a sintaxe de C foi projetada dessa maneira?
Por exemplo, se eu estivesse projetando ponteiros, eu traduziria "um ponteiro para uma matriz de 10 elementos" em
int*[10]* p;
e não
int* (*p)[10];
o que eu acho que a maioria das pessoas concorda que é muito menos direto.
Então, eu estou me perguntando, por que a sintaxe não intuitiva? Houve um problema específico que a sintaxe resolve (talvez uma ambiguidade?) Que eu desconheço?
cdecl
comando é muito útil para decodificar declarações C complexas. Há também uma interface da web em cdecl.org .Respostas:
Meu entendimento da história disso é que ele se baseia em dois pontos principais ...
Em primeiro lugar, os autores da linguagem preferiram tornar a sintaxe centrada na variável em vez de centrada no tipo. Ou seja, eles queriam que um programador olhasse para a declaração e pensasse "se eu escrever a expressão
*func(arg)
, isso resultará em umint
; se eu escrever*arg[N]
, terei um float" em vez de "func
deve ser um ponteiro para uma função que aceita isso". e devolvendo isso ".A entrada C na Wikipedia afirma que:
... citando a p122 de K & R2 que, infelizmente, não preciso entregar para encontrar a cotação estendida para você.
Em segundo lugar, é realmente muito difícil criar uma sintaxe para declaração que seja consistente quando você estiver lidando com níveis arbitrários de indireção. Seu exemplo pode funcionar bem para expressar o tipo que você pensou lá fora, mas é dimensionado para uma função que leva um ponteiro para uma matriz desses tipos e retorna alguma outra bagunça hedionda? (Talvez sim, mas você verificou? Você pode provar isso? ).
Lembre-se de que parte do sucesso de C se deve ao fato de que os compiladores foram escritos para muitas plataformas diferentes e, portanto, poderia ter sido melhor ignorar algum grau de legibilidade para facilitar a gravação dos compiladores.
Dito isto, não sou especialista em gramática ou redação de compiladores. Mas eu sei o suficiente para saber que há muito o que saber;)
fonte
Muitas peculiaridades da linguagem C podem ser explicadas pela maneira como os computadores funcionavam quando foram projetados. Havia quantidades muito limitadas de memória de armazenamento, por isso era muito importante minimizar o tamanho dos arquivos de código-fonte . A prática de programação nos anos 70 e 80 era garantir que o código-fonte contivesse o mínimo de caracteres possível e, de preferência, nenhum comentário excessivo no código-fonte.
Hoje em dia isso é ridículo, com espaço de armazenamento praticamente ilimitado em discos rígidos. Mas é parte da razão pela qual C tem uma sintaxe tão estranha em geral.
Em relação aos ponteiros de array especificamente, seu segundo exemplo deve ser
int (*p)[10];
(sim, a sintaxe é muito confusa). Talvez eu lesse isso como "int ponteiro para uma série de dez" ... o que faz sentido. Se não fosse o parêntese, o compilador o interpretaria como uma matriz de dez ponteiros, o que daria à declaração um significado totalmente diferente.Como ponteiros de matriz e ponteiros de função têm sintaxe bastante obscura em C, a coisa mais sensata a fazer é digitar a estranheza. Talvez assim:
Exemplo obscuro:
Exemplo equivalente não obscuro:
As coisas podem ficar ainda mais obscuras se você estiver lidando com matrizes de ponteiros de função. Ou a mais obscura de todas: funções retornando indicadores de função (levemente úteis). Se você não usar typedefs para essas coisas, ficará rapidamente louco.
fonte
É bem simples:
int *p
significa que*p
é um int;int a[5]
significa quea[i]
é um int.Significa que
*f
é uma função,*a
é uma matriz de cinco números inteiros, assimf
como uma função que leva um ponteiro para uma matriz de cinco números inteiros e retorna int. No entanto, em C, não é útil passar um ponteiro para uma matriz.As declarações C muito raramente ficam complicadas.
Além disso, você pode esclarecer usando typedefs:
fonte
typedef
,const
,volatile
, e a capacidade de inicializar as coisas dentro de declarações. Muitas das ambiguidades irritantes da sintaxe da declaração (por exemplo, seint const *p, *q;
devem se vincularconst
ao tipo ou ao declarand) não poderiam surgir no idioma como originalmente projetado. Eu gostaria que o idioma tivesse adicionado dois pontos entre o tipo e o declarando, mas permitia sua omissão ao usar tipos internos de "palavra reservada" sem qualificadores. O significado deint: const *p,*q;
eint const *: p,*q;
teria sido claro.Eu acho que você deve considerar * [] como operadores anexados a uma variável. * é escrito antes de uma variável, [] depois.
Vamos ler a expressão de tipo
O elemento mais interno é p, uma variável, portanto
significa: p é uma variável.
Antes da variável existir um *, o operador * é sempre colocado antes da expressão a que se refere, portanto,
significa: a variável p é um ponteiro. Sem o (), o operador [] à direita teria maior precedência, ou seja,
seria analisado como
O próximo passo é []: como não há mais (), [] tem maior precedência que o externo *, portanto
significa: (a variável p é um ponteiro) para uma matriz. Então nós temos o segundo *:
significa: ((variável p é um ponteiro) para uma matriz) de ponteiros
Finalmente, você tem o operador int (um nome de tipo), que tem a menor precedência:
significa: (((variável p é um ponteiro) para uma matriz) de ponteiros) para inteiro.
Portanto, todo o sistema é baseado em expressões de tipo com operadores, e cada operador possui suas próprias regras de precedência. Isso permite definir tipos muito complexos.
fonte
Não é tão difícil quando você começa a pensar e C nunca foi uma linguagem muito fácil. E
int*[10]* p
realmente não é mais fácil do queint* (*p)[10]
E que tipo de k seriaint*[10]* p, k;
fonte
pointer to int
eint
nem são do mesmo tipo, portanto devem ser declarados separadamente. Período. Escute o homem. Ele tem 18k representantes por um motivo.