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 strmatriz. Isso vale para qualquer função chamada com uma expressão de matriz como argumento (praticamente qualquer uma das str*funções *scanfe*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 xe ysão objetos distintos de ae b, portanto, muda para xe ynão são refletidos em ae b. Como queremos modificar os valores de ae 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 xe y, mas os valores do que xe y apontamos . Escrever para *xé diferente de escrever para x; não estamos atualizando o valor em xsi, obtemos um local xe 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 streamnã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.