Existe um padrão ao lidar com matrizes e funções; é um pouco difícil de ver no começo.
Ao lidar com matrizes, é útil lembrar o seguinte: quando uma expressão de matriz aparece na maioria dos contextos, o tipo da expressão é implicitamente convertido de "matriz do elemento N de T" para "ponteiro para T" e seu valor é definido para apontar para o primeiro elemento na matriz. As exceções a esta regra são quando a expressão da matriz aparece como um operando de &
ousizeof
operadores, ou quando é uma string literal sendo usado como um inicializador em uma declaração.
Portanto, quando você chama uma função com uma expressão de matriz como argumento, a função receberá um ponteiro, não uma matriz:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
É por isso que você não usa o &
operador para argumentos correspondentes a "% s" em scanf()
:
char str[STRING_LENGTH];
...
scanf("%s", str);
Por causa da conversão implícita, scanf()
recebe um char *
valor que aponta para o início da str
matriz. Isso vale para qualquer função chamada com uma expressão de matriz como argumento (praticamente qualquer uma das str*
funções *scanf
e*printf
funções, etc.).
Na prática, você provavelmente nunca chamará uma função com uma expressão de matriz usando o &
operador, como em:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
Esse código não é muito comum; você precisa conhecer o tamanho da matriz na declaração da função, e a função funciona apenas com ponteiros para matrizes de tamanhos específicos (um ponteiro para uma matriz de 10 elementos de T é um tipo diferente de um ponteiro para uma matriz de 11 elementos de T).
Quando uma expressão de matriz aparece como um operando para o &
operador, o tipo da expressão resultante é "ponteiro para a matriz do elemento N de T" ou T (*)[N]
, que é diferente de uma matriz de ponteiros ( T *[N]
) e um ponteiro para o tipo base (T *
)
Ao lidar com funções e ponteiros, a regra a ser lembrada é: se você deseja alterar o valor de um argumento e refleti-lo no código de chamada, deve passar um ponteiro para o que deseja modificar. Novamente, as matrizes jogam um pouco de chave inglesa nos trabalhos, mas vamos lidar com os casos normais primeiro.
Lembre-se que C passa todos os argumentos da função por valor; o parâmetro formal recebe uma cópia do valor no parâmetro real e quaisquer alterações no parâmetro formal não são refletidas no parâmetro real. O exemplo comum é uma função de troca:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
Você obterá a seguinte saída:
antes da troca: a = 1, b = 2
após a troca: a = 1, b = 2
Os parâmetros formais x
e y
são objetos distintos de a
e b
, portanto, muda para x
e y
não são refletidos em a
e b
. Como queremos modificar os valores de a
e b
, devemos passar ponteiros para eles para a função swap:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
Agora sua saída será
antes da troca: a = 1, b = 2
após a troca: a = 2, b = 1
Observe que, na função swap, não alteramos os valores de x
e y
, mas os valores do que x
e y
apontamos . Escrever para *x
é diferente de escrever para x
; não estamos atualizando o valor em x
si, obtemos um local x
e atualizamos o valor nesse local.
Isso é igualmente verdadeiro se quisermos modificar um valor de ponteiro; se escrevermos
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
então estamos modificando o valor do parâmetro de entrada stream
, não o que stream
aponta para , portanto, a alteração stream
não afeta o valor de in
; para que isso funcione, devemos passar um ponteiro para o ponteiro:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
Novamente, as matrizes jogam um pouco de uma chave inglesa nos trabalhos. Quando você passa uma expressão de matriz para uma função, o que a função recebe é um ponteiro. Devido à forma como a assinatura de matriz é definida, você pode usar um operador de subscrito em um ponteiro da mesma maneira que em uma matriz:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
Observe que os objetos da matriz não podem ser atribuídos; ou seja, você não pode fazer algo como
int a[10], b[10];
...
a = b;
então você quer ter cuidado ao lidar com ponteiros para matrizes; algo como
void (int (*foo)[N])
{
...
*foo = ...;
}
não vai funcionar.