Uso de array padrão em C com decaimento de tipo natural de array para ptr
@Bo Persson afirma corretamente em sua ótima resposta aqui :
Ao passar um array como parâmetro, este
void arraytest(int a[])
significa exatamente o mesmo que
void arraytest(int *a)
No entanto, deixe-me acrescentar também que as duas formas acima também:
significa exatamente o mesmo que
void arraytest(int a[0])
o que significa exatamente o mesmo que
void arraytest(int a[1])
o que significa exatamente o mesmo que
void arraytest(int a[2])
o que significa exatamente o mesmo que
void arraytest(int a[1000])
etc.
Em cada um dos exemplos de array acima, o tipo de parâmetro de entrada decai para umint * e pode ser chamado sem avisos e sem erros, mesmo com as opções de compilação -Wall -Wextra -Werrorativadas (veja meu repositório aqui para obter detalhes sobre essas 3 opções de compilação), como esta:
int array1[2];
int * array2 = array1;
arraytest(array1);
arraytest(array2);
Por uma questão de fato, o valor "tamanho" ( [0], [1], [2], [1000], etc.) dentro do parâmetro de matriz aqui é, aparentemente, apenas para fins de auto-documentação / estética, e pode ser qualquer inteiro positivo ( size_tdigite eu acho) que você quiser!
Na prática, entretanto, você deve usá-lo para especificar o tamanho mínimo do array que você espera que a função receba, de modo que, ao escrever o código, seja fácil rastrear e verificar. O padrão MISRA-C-2012 ( compre / baixe o PDF 236-pg versão 2012 do padrão por £ 15,00 aqui ) vai mais longe a ponto de declarar (ênfase adicionada):
Regra 17.5 O argumento da função correspondente a um parâmetro declarado como tendo um tipo de array deve ter um número apropriado de elementos.
...
Se um parâmetro for declarado como uma matriz com um tamanho especificado, o argumento correspondente em cada chamada de função deve apontar para um objeto que tenha pelo menos tantos elementos quanto a matriz.
...
O uso de um declarador de matriz para um parâmetro de função especifica a interface da função mais claramente do que usar um ponteiro. O número mínimo de elementos esperado pela função é declarado explicitamente, embora isso não seja possível com um ponteiro.
Em outras palavras, eles recomendam usar o formato de tamanho explícito, mesmo que o padrão C tecnicamente não o imponha - pelo menos ajuda a esclarecer para você como desenvolvedor e para outros que usam o código, qual array de tamanho a função espera você para passar.
Forçando a segurança de tipo em matrizes em C
Como @Winger Sendon aponta em um comentário abaixo da minha resposta, podemos forçar C a tratar um tipo de array como diferente com base no tamanho do array !
Primeiro, você deve reconhecer que, no meu exemplo acima, usando o int array1[2];seguinte: arraytest(array1);faz array1com que automaticamente decaia para um int *. NO ENTANTO, se você pegar o endereço de array1 e ligar arraytest(&array1), terá um comportamento completamente diferente! Agora, ele NÃO decai em um int *! Em vez disso, o tipo de &array1é int (*)[2], que significa "ponteiro para uma matriz de tamanho 2 de int" ou "ponteiro para uma matriz de tamanho 2 do tipo int" . Portanto, você pode FORÇAR C para verificar a segurança de tipo em uma matriz, assim:
void arraytest(int (*a)[2])
{
}
Essa sintaxe é difícil de ler, mas semelhante à de um ponteiro de função . A ferramenta online, cdecl , nos diz que isso int (*a)[2]significa: "declara um as ponteiro para array 2 de int" (ponteiro para array de 2 ints). NÃO confunda isso com a versão sem parênteses:, int * a[2]que significa: "declara a como array 2 do ponteiro para int" (array de 2 ponteiros para int).
Agora, esta função EXIGE que você a chame com o operador de endereço ( &) assim, usando como parâmetro de entrada um PONTEIRO PARA UMA MATRIZ DO TAMANHO CORRETO !:
int array1[2];
arraytest(&array1);
Isso, no entanto, produzirá um aviso:
int array1[2];
arraytest(array1);
Você pode testar este código aqui .
Para forçar o compilador C a transformar este aviso em um erro, de modo que você DEVE sempre chamar arraytest(&array1);usando apenas uma matriz de entrada do tamanho e tipo corretos ( int array1[2];neste caso), adicione -Werroràs suas opções de construção. Se estiver executando o código de teste acima em onlinegdb.com, faça isso clicando no ícone de engrenagem no canto superior direito e clique em "Extra Compiler Flags" para digitar esta opção. Agora, este aviso:
main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
vai se transformar neste erro de compilação:
main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
arraytest(array1);
^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
void arraytest(int (*a)[2])
^~~~~~~~~
cc1: all warnings being treated as errors
Observe que você também pode criar ponteiros de "tipo seguro" para matrizes de um determinado tamanho, como este:
int array[2];
int (*array_p)[2] = &array;
... mas eu não recomendo necessariamente isso, pois me lembra muito das artimanhas C ++ usadas para forçar a segurança de tipos em todos os lugares, com o custo excepcionalmente alto da complexidade da sintaxe da linguagem, verbosidade e dificuldade de arquitetura de código, e das quais eu não gosto e já falei sobre isso muitas vezes antes (ex: veja "Meus pensamentos sobre C ++" aqui )
Para testes e experimentações adicionais, consulte também o link abaixo.
Referências
Veja os links acima. Além disso:
- Minha experimentação de código online: https://onlinegdb.com/B1RsrBDFD