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 -Werror
ativadas (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_t
digite 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 array1
com 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 int
s). 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