Esta pergunta é dirigida aos C gurus por aí:
Em C, é possível declarar um ponteiro da seguinte maneira:
char (* p)[10];
.. que basicamente afirma que este ponteiro aponta para uma matriz de 10 caracteres. O bom de declarar um ponteiro como este é que você obterá um erro em tempo de compilação se tentar atribuir um ponteiro de um array de tamanho diferente a p. Também lhe dará um erro de tempo de compilação se você tentar atribuir o valor de um ponteiro char simples para p. Tentei fazer isso com o gcc e parece funcionar com ANSI, C89 e C99.
Parece-me que declarar um ponteiro como este seria muito útil - particularmente, ao passar um ponteiro para uma função. Normalmente, as pessoas escreveriam o protótipo de uma função assim:
void foo(char * p, int plen);
Se você esperava um buffer de um tamanho específico, simplesmente testaria o valor do plen. No entanto, você não pode ter certeza de que a pessoa que passar p para você realmente fornecerá localizações de memória válidas nesse buffer. Você tem que confiar que a pessoa que chamou essa função está fazendo a coisa certa. Por outro lado:
void foo(char (*p)[10]);
..forçaria o chamador a fornecer um buffer do tamanho especificado.
Isso parece muito útil, mas nunca vi um ponteiro declarado assim em nenhum código que já encontrei.
Minha pergunta é: existe alguma razão pela qual as pessoas não declaram dicas como essa? Não estou vendo alguma armadilha óbvia?
10
pode ser substituída por qualquer variável no escopoRespostas:
O que você está dizendo em sua postagem está absolutamente correto. Eu diria que todo desenvolvedor C chega exatamente à mesma descoberta e exatamente à mesma conclusão quando (se) eles alcançam certo nível de proficiência com a linguagem C.
Quando as especificações de sua área de aplicativo exigem um array de tamanho fixo específico (o tamanho do array é uma constante de tempo de compilação), a única maneira adequada de passar esse array para uma função é usando um parâmetro de ponteiro para array
(na linguagem C ++ isso também é feito com referências
)
Isso habilitará a verificação de tipo no nível do idioma, o que garantirá que o array de tamanho correto seja fornecido como argumento. Na verdade, em muitos casos as pessoas usam essa técnica implicitamente, sem nem perceber, escondendo o tipo de array atrás de um nome de typedef
Além disso, observe que o código acima é invariável em relação ao
Vector3d
tipo que é uma matriz ou astruct
. Você pode mudar a definição deVector3d
a qualquer momento de uma matriz para umastruct
e vice-versa, e não será necessário alterar a declaração da função. Em qualquer um dos casos, as funções receberão um objeto agregado "por referência" (há exceções a isso, mas no contexto desta discussão isso é verdadeiro).No entanto, você não verá esse método de passagem de array usado explicitamente com muita frequência, simplesmente porque muitas pessoas se confundem com uma sintaxe um tanto complicada e simplesmente não se sentem confortáveis o suficiente com tais recursos da linguagem C para usá-los apropriadamente. Por esse motivo, na vida real média, passar um array como um ponteiro para seu primeiro elemento é uma abordagem mais popular. Parece apenas "mais simples".
Mas, na realidade, usar o ponteiro para o primeiro elemento para a passagem de array é uma técnica de nicho, um truque, que serve a um propósito muito específico: seu único propósito é facilitar a passagem de arrays de tamanhos diferentes (ou seja, tamanho de tempo de execução) . Se você realmente precisa ser capaz de processar arrays de tamanho de tempo de execução, então a maneira adequada de passar tal array é por um ponteiro para seu primeiro elemento com o tamanho concreto fornecido por um parâmetro adicional
Na verdade, em muitos casos, é muito útil poder processar matrizes de tamanho de tempo de execução, o que também contribui para a popularidade do método. Muitos desenvolvedores C simplesmente nunca encontram (ou nunca reconhecem) a necessidade de processar um array de tamanho fixo, permanecendo assim alheios à técnica de tamanho fixo adequada.
No entanto, se o tamanho do array for fixo, passando-o como um ponteiro para um elemento
é um grande erro no nível da técnica, que infelizmente é bastante comum nos dias de hoje. Uma técnica de ponteiro para array é uma abordagem muito melhor nesses casos.
Outro motivo que pode impedir a adoção da técnica de passagem de matriz de tamanho fixo é o domínio da abordagem ingênua para a digitação de matrizes alocadas dinamicamente. Por exemplo, se o programa chama matrizes fixas do tipo
char[10]
(como em seu exemplo), um desenvolvedor médio usarámalloc
matrizes comoEsta matriz não pode ser passada para uma função declarada como
o que confunde o desenvolvedor médio e o faz abandonar a declaração do parâmetro de tamanho fixo sem pensar mais nisso. Na realidade, porém, a raiz do problema está na
malloc
abordagem ingênua . Omalloc
formato mostrado acima deve ser reservado para matrizes de tamanho de tempo de execução. Se o tipo de array tiver tamanho em tempo de compilação, uma maneira melhor demalloc
fazê - lo seria a seguinteIsso, é claro, pode ser facilmente passado para o acima declarado
foo
e o compilador executará a verificação de tipo apropriada. Mas, novamente, isso é muito confuso para um desenvolvedor C despreparado, e é por isso que você não verá isso com muita frequência no código "típico" do dia a dia.
fonte
const
com propriedade com essa técnica. Umconst char (*p)[N]
argumento não parece compatível com um ponteiro parachar table[N];
Em contraste, umchar*
ptr simples permanece compatível com umconst char*
argumento.(*p)[i]
e não fazer*p[i]
. O último irá pular pelo tamanho da matriz, que quase certamente não é o que você deseja. Pelo menos para mim, aprender essa sintaxe causou, ao invés de prevenir, um erro; Eu teria obtido o código correto mais rápido apenas passando um float *.const
ponteiro para um array de elementos mutáveis. E sim, isso é completamente diferente de um ponteiro para uma matriz de elementos imutáveis.Eu gostaria de acrescentar à resposta de AndreyT (no caso de alguém tropeçar nesta página procurando por mais informações sobre este tópico):
À medida que começo a brincar mais com essas declarações, percebo que há uma grande desvantagem associada a elas em C (aparentemente não em C ++). É bastante comum ter uma situação em que você gostaria de dar a um chamador um ponteiro const para um buffer no qual você escreveu. Infelizmente, isso não é possível ao declarar um ponteiro como este em C. Em outras palavras, o padrão C (6.7.3 - Parágrafo 8) está em desacordo com algo assim:
Essa restrição não parece estar presente em C ++, tornando esse tipo de declaração muito mais útil. Mas no caso de C, é necessário voltar a uma declaração de ponteiro regular sempre que quiser um ponteiro const para o buffer de tamanho fixo (a menos que o próprio buffer tenha sido declarado const para começar). Você pode encontrar mais informações neste tópico de e-mail: texto do link
Esta é uma restrição severa na minha opinião e pode ser uma das principais razões pelas quais as pessoas geralmente não declaram ponteiros como este em C. O outro é o fato de que a maioria das pessoas nem mesmo sabe que você pode declarar um ponteiro como este como AndreyT apontou.
fonte
O motivo óbvio é que este código não compila:
A promoção padrão de uma matriz é para um ponteiro não qualificado.
Veja também esta questão , o uso
foo(&p)
deve funcionar.fonte
foo(&p)
.Também quero usar essa sintaxe para permitir mais verificação de tipo.
Mas também concordo que a sintaxe e o modelo mental de uso de ponteiros são mais simples e fáceis de lembrar.
Aqui estão mais alguns obstáculos que encontrei.
O acesso à matriz requer o uso de
(*p)[]
:É tentador usar um ponteiro para char local em vez disso:
Mas isso anularia parcialmente o propósito de usar o tipo correto.
É preciso lembrar de usar o operador address-of ao atribuir o endereço de uma matriz a um ponteiro para matriz:
O operador address-of obtém o endereço de toda a matriz
&a
, com o tipo correto para atribuí-lop
. Sem o operador,a
é convertido automaticamente para o endereço do primeiro elemento da matriz, igual a in&a[0]
, que possui um tipo diferente.Uma vez que essa conversão automática já está ocorrendo, sempre fico intrigado de que isso
&
seja necessário. É consistente com o uso de&
variáveis on de outros tipos, mas tenho que lembrar que um array é especial e que preciso do&
para obter o tipo correto de endereço, embora o valor do endereço seja o mesmo.Uma razão para o meu problema pode ser que eu aprendi K&R C nos anos 80, que ainda não permitia o uso do
&
operador em matrizes inteiras (embora alguns compiladores ignorassem isso ou tolerassem a sintaxe). O que, a propósito, pode ser outro motivo pelo qual os ponteiros para matrizes têm dificuldade em serem adotados: eles só funcionam corretamente desde ANSI C, e a&
limitação do operador pode ter sido outro motivo para considerá-los muito estranhos.Quando não
typedef
é usado para criar um tipo para o ponteiro para array (em um arquivo de cabeçalho comum), um ponteiro global para array precisa de uma declaração mais complicada para compartilhá-lo entre arquivos:extern
fonte
Bem, simplesmente, C não faz as coisas dessa maneira. Um array de tipo
T
é passado como um ponteiro para o primeiroT
no array, e isso é tudo que você obtém.Isso permite alguns algoritmos interessantes e elegantes, como loop através da matriz com expressões como
A desvantagem é que o gerenciamento do tamanho depende de você. Infelizmente, a falha em fazer isso conscienciosamente também levou a milhões de bugs na codificação C e / ou oportunidades para exploração malévola.
O que se aproxima do que você pergunta em C é passar um
struct
(por valor) ou um ponteiro para um (por referência). Contanto que o mesmo tipo de estrutura seja usado em ambos os lados dessa operação, tanto o código que distribui a referência quanto o código que a usa estão de acordo sobre o tamanho dos dados que estão sendo tratados.Sua estrutura pode conter quaisquer dados que você deseja; ele pode conter seu array de um tamanho bem definido.
Ainda assim, nada impede que você ou um codificador incompetente ou malévolo usem casts para enganar o compilador para tratar sua estrutura como de tamanho diferente. A habilidade quase desenfreada de fazer esse tipo de coisa faz parte do projeto de C.
fonte
Você pode declarar uma matriz de caracteres de várias maneiras:
O protótipo de uma função que recebe uma matriz por valor é:
ou por referência:
ou por sintaxe de array:
fonte
char (*p)[10] = malloc(sizeof *p)
.Eu não recomendaria esta solução
uma vez que obscurece o fato de que Vector3D tem um tipo que você deve conhecer. Os programadores geralmente não esperam que variáveis do mesmo tipo tenham tamanhos diferentes. Considere:
onde sizeof a! = sizeof b
fonte
sizeof(a)
não é o mesmo quesizeof(b)
?Talvez eu esteja faltando alguma coisa, mas ... já que os arrays são ponteiros constantes, basicamente isso significa que não há motivo para passar ponteiros para eles.
Você não poderia simplesmente usar
void foo(char p[10], int plen);
?fonte
No meu compilador (vs2008), ele trata
char (*p)[10]
como uma matriz de ponteiros de caracteres, como se não houvesse parênteses, mesmo se eu compilar como um arquivo C. O compilador suporta esta "variável"? Nesse caso, esse é um dos principais motivos para não usá-lo.fonte