Escrevi uma função contendo array como argumento e chamo-a passando o valor de array da seguinte maneira.
void arraytest(int a[])
{
// changed the array a
a[0]=a[0]+a[1];
a[1]=a[0]-a[1];
a[0]=a[0]-a[1];
}
void main()
{
int arr[]={1,2};
printf("%d \t %d",arr[0],arr[1]);
arraytest(arr);
printf("\n After calling fun arr contains: %d\t %d",arr[0],arr[1]);
}
O que descobri é que, embora esteja chamando a arraytest()
função passando valores, a cópia original de int arr[]
foi alterada.
Você pode explicar por quê?
c
arrays
function
parameters
parameter-passing
Mohan Mahajan
fonte
fonte
main()
deve retornarint
.Respostas:
Ao passar um array como parâmetro, este
void arraytest(int a[])
significa exatamente o mesmo que
void arraytest(int *a)
então você está modificando os valores em principal.
Por razões históricas, os arrays não são cidadãos de primeira classe e não podem ser passados por valor.
fonte
struct
foi adicionado ao idioma que isso foi alterado. E então foi considerado tarde demais para alterar as regras para matrizes. Já havia 10 usuários. :-)void arraytest(int a[1000])
etc etc. Resposta expandida aqui: stackoverflow.com/a/51527502/4561887 .Se você quiser passar uma matriz de dimensão única como um argumento em uma função , você terá que declarar um parâmetro formal em uma das três maneiras a seguir e todos os três métodos de declaração produzem resultados semelhantes porque cada um diz ao compilador que um ponteiro de inteiro está indo a ser recebido .
int func(int arr[], ...){ . . . } int func(int arr[SIZE], ...){ . . . } int func(int* arr, ...){ . . . }
Então, você está modificando os valores originais.
Obrigado !!!
fonte
Você não está passando a matriz como cópia. É apenas um ponteiro apontando para o endereço onde o primeiro elemento da matriz está na memória.
fonte
Você está passando o endereço do primeiro elemento da matriz
fonte
Arrays em C são convertidos, na maioria dos casos, em um ponteiro para o primeiro elemento do próprio array. E, mais detalhadamente, os arrays passados em funções são sempre convertidos em ponteiros.
Aqui está uma citação de K & R2nd :
Escrita:
void arraytest(int a[])
tem o mesmo significado que escrever:
void arraytest(int *a)
Portanto, apesar de você não estar escrevendo explicitamente, é como você está passando um ponteiro e, portanto, está modificando os valores no principal.
Para mais, eu realmente sugiro a leitura disso .
Além disso, você pode encontrar outras respostas sobre SO aqui
fonte
Você está passando o valor da localização da memória do primeiro membro da matriz.
Portanto, quando você começa a modificar o array dentro da função, está modificando o array original.
Lembre-se que
a[1]
é*(a+1)
.fonte
Em C, exceto em alguns casos especiais, uma referência de array sempre "decai" para um ponteiro para o primeiro elemento do array. Portanto, não é possível passar um array "por valor". Uma matriz em uma chamada de função será passada para a função como um ponteiro, o que é análogo a passar a matriz por referência.
EDITAR: Existem três casos especiais em que uma matriz não decai para um ponteiro para seu primeiro elemento:
sizeof a
não é o mesmo quesizeof (&a[0])
.&a
não é o mesmo que&(&a[0])
(e não exatamente o mesmo que&a[0]
).char b[] = "foo"
não é o mesmo quechar b[] = &("foo")
.fonte
int a[10]
e atribua um valor aleatório a cada elemento. Agora, se eu passar esse array para uma função usandoint y[]
ouint y[10]
ouint *y
. E nessa função que eu uso asizeof(y)
Resposta, o ponteiro de bytes foi alocado. Portanto, neste caso, ele se tornará um indicador. Seria útil se você incluir isso também. Veja este postimg.org/image/prhleuezdsizeof
operar na função no array que definimos originalmente, ele decairá como um array, mas se eu passar em outra função, então usar osizeof
operador decairá como um ponteiro.&a
não é exatamente o mesmo que&a[0]
quandoa
é um array. Como assim? Em meu programa de teste, ambos mostram ser os mesmos, tanto na função em que a matriz é declarada quanto quando passados para uma função diferente. 2. O escritor escreve que "char b[] = "foo"
não é o mesmo quechar b[] = &("foo")
". Para mim, este último nem compila. Sou apenas eu?Passando um array multidimensional como argumento para uma função. Passar um array de um dim como argumento é mais ou menos trivial. Vamos dar uma olhada em um caso mais interessante de passar um array 2 dim. Em C você não pode usar um ponteiro para ponteiro construct (
int **
) em vez de 2 dim array. Vamos dar um exemplo:void assignZeros(int(*arr)[5], const int rows) { for (int i = 0; i < rows; i++) { for (int j = 0; j < 5; j++) { *(*(arr + i) + j) = 0; // or equivalent assignment arr[i][j] = 0; } }
Aqui, especifiquei uma função que leva como primeiro argumento um ponteiro para uma matriz de 5 inteiros. Posso passar como argumento qualquer matriz de 2 dim que tenha 5 colunas:
int arr1[1][5] int arr1[2][5] ... int arr1[20][5] ...
Você pode ter a ideia de definir uma função mais geral que pode aceitar qualquer array 2 dim e alterar a assinatura da função da seguinte forma:
void assignZeros(int ** arr, const int rows, const int cols) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { *(*(arr + i) + j) = 0; } } }
Este código seria compilado, mas você obterá um erro de tempo de execução ao tentar atribuir os valores da mesma maneira que na primeira função. Portanto, em C, arrays multidimensionais não são o mesmo que ponteiros para ponteiros ... para ponteiros. Um
int(*arr)[5]
é um ponteiro para um array de 5 elementos, umint(*arr)[6]
é um ponteiro para um array de 6 elementos e eles são um ponteiro para diferentes tipos!Bem, como definir argumentos de funções para dimensões superiores? Simples, apenas seguimos o padrão! Aqui está a mesma função ajustada para obter uma matriz de 3 dimensões:
void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) { for (int i = 0; i < dim1; i++) { for (int j = 0; j < dim2; j++) { for (int k = 0; k < dim3; k++) { *(*(*(arr + i) + j) + k) = 0; // or equivalent assignment arr[i][j][k] = 0; } } } }
Como você esperaria, ele pode tomar como argumento qualquer 3 dim arrays que tenham na segunda dimensão 4 elementos e na terceira dimensão 5 elementos. Qualquer coisa assim estaria OK:
arr[1][4][5] arr[2][4][5] ... arr[10][4][5] ...
Mas temos que especificar todos os tamanhos de dimensões até o primeiro.
fonte
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 :
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 um
int *
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; // works fine because `array1` automatically decays from an array type // to `int *` arraytest(array1); // works fine because `array2` is already an `int *` 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):
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);
fazarray1
com que automaticamente decaia para umint *
. NO ENTANTO, se você pegar o endereço dearray1
e ligararraytest(&array1)
, terá um comportamento completamente diferente! Agora, ele NÃO decai em umint *
! 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]) { // my function here }
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 2int
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 paraint
).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]; // ok, since the type of `array1` is `int (*)[2]` (ptr to array of // 2 ints) arraytest(&array1); // you must use the & operator here to prevent // `array1` from otherwise automatically decaying // into `int *`, which is the WRONG input type here!
Isso, no entanto, produzirá um aviso:
int array1[2]; // WARNING! Wrong type since the type of `array1` decays to `int *`: // main.c:32:15: warning: passing argument 1 of ‘arraytest’ from // incompatible pointer type [-Wincompatible-pointer-types] // main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’ arraytest(array1); // (missing & operator)
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:vai se transformar neste erro de compilação:
Observe que você também pode criar ponteiros de "tipo seguro" para matrizes de um determinado tamanho, como este:
int array[2]; // "type safe" ptr to array of size 2 of int: 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:
fonte
void arraytest(int (*a)[1000])
é melhor porque o compilador irá errar se o tamanho estiver errado.Forcing type safety on arrays in C
, cobrindo seu ponto.As matrizes são sempre passadas por referência se você usar
a[]
ou*a
:int* printSquares(int a[], int size, int e[]) { for(int i = 0; i < size; i++) { e[i] = i * i; } return e; } int* printSquares(int *a, int size, int e[]) { for(int i = 0; i < size; i++) { e[i] = i * i; } return e; }
fonte