Tipo de Matriz – Regras para atribuição / uso como parâmetro de function

quando eu preciso passar uma matriz para uma function, parece que todas as declarações a seguir da function funcionará

void f(int arr[]) void f(int arr[4]) // is this one correct? 

por esta:

 int a[]={1,2,3,4}; f(a); 

Mas quando eu atribuo um array a outro array, ele falha

 int a[]={1,2,3,4}; int b[4] = a; // error: array must be initialized with a brace-enclosed initializer 

Então, por que uma matriz passada como um argumento de uma function está bem, mas usada no rhs da atribuição simples está errada?

Para entender a diferença, precisamos entender dois contextos diferentes.

  • Em contextos de valor , o nome de uma matriz do tipo T é equivalente a um ponteiro para digitar T e é igual a um ponteiro para o primeiro elemento da matriz.
  • Em contextos de object , o nome de uma matriz do tipo T não reduz a um ponteiro.

O que é o contexto do object?

Em a = b; , está no contexto do object. Quando você pega o endereço de uma variável, ela é usada no contexto do object. Finalmente, quando você usa sizeof operator em uma variável, ela é usada no contexto do object. Em todos os outros casos, uma variável é usada no contexto de valor.

Agora que temos esse conhecimento, quando fazemos:

 void f(int arr[4]); 

É exatamente equivalente a

 void f(int *arr); 

Como você descobriu, podemos omitir o tamanho (4 acima) da declaração da function. Isso significa que você não pode saber o tamanho da “matriz” passada para f() . Mais tarde, quando você faz:

 int a[]={1,2,3,4}; f(a); 

Na chamada de function, o nome a está em contexto de valor, então reduz para um ponteiro para int . Isso é bom, porque f espera um ponteiro para um int , então a definição da function e use match. O que é passado para f() é o ponteiro para o primeiro elemento de a ( &a[0] ).

No caso de

 int a[]={1,2,3,4}; int b[4] = a; 

O nome b é usado em um contexto de object e não reduz a um ponteiro. (Incidentalmente, a aqui está em um contexto de valor e reduz a um ponteiro.)

Agora, int b[4]; atribui o valor de armazenamento de 4 int dá o nome b a ele. a também foi designado armazenamento similar. Portanto, na verdade, a atribuição acima significa “Quero tornar o local de armazenamento o mesmo que o local anterior”. Isso não faz sentido.

Se você quiser copiar o conteúdo de a em b , então você poderia fazer:

 #include  int b[4]; memcpy(b, a, sizeof b); 

Ou, se você quisesse um ponteiro b que apontasse para:

 int *b = a; 

Aqui, a está no contexto de valor, e reduz a um ponteiro para int , então podemos atribuir a a um int * .

Finalmente, ao inicializar um array , você pode atribuir valores explícitos:

 int a[] = {1, 2, 3, 4}; 

Aqui, a tem 4 elementos, inicializados para 1, 2, 3 e 4. Você também pode fazer:

 int a[4] = {1, 2, 3, 4}; 

Se houver menos elementos na lista do que o número de elementos na matriz, o restante dos valores será considerado 0:

 int a[4] = {1, 2}; 

define a[2] e a[3] para 0.

 void f(int arr[]); void f(int arr[4]); 

A syntax é enganosa. Eles são os mesmos que isso:

 void f(int *arr); 

isto é, você está passando um ponteiro para o início da matriz. Você não está copiando a matriz.

C não suporta a atribuição de matrizes. No caso de uma chamada de function, a matriz decai para um ponteiro. C suporta a atribuição de pointers. Isso é perguntado aqui todos os dias – que livro de texto C vocês estão lendo que não explica isso?

Tente memcpy.

 int a[]={1,2,3,4}; int b[4]; memcpy(b, a, sizeof(b)); 

Obrigado por apontar isso, Steve, tem sido um tempo desde que eu usei C.

Para obter sua intuição sobre isso, você deve entender o que está acontecendo no nível da máquina.

A semântica de boot (= {1,2,3,4}) significa “coloque-a exatamente na sua imagem binária” para que isso possa ser compilado.

A atribuição da matriz seria diferente: o compilador teria que traduzi-la em um loop, o qual, na verdade, iteraria sobre os elementos. Compilador C (ou C ++, para esse assunto) nunca faz uma coisa dessas. Espera-se legitimamente que você faça isso sozinho. Por quê? Porque você pode. Então, deve ser uma sub-rotina, escrita em C (memcpy). Isso é tudo sobre simplicidade e proximidade de sua arma, que é C e C ++.

Note que o tipo de em int a[4] é int [4] .

Mas TypeOf ( &a ) == int (*)[4] ! = int [4] .

Observe também que o tipo do valor de a é int * , o que é diferente de todos os itens acima!

Aqui está um exemplo de programa que você pode tentar:

 int main() { // All of these are different, incompatible types! printf("%d\n", sizeof (int[4])); // 16 // These two may be the same size, but are *not* interchangeable! printf("%d\n", sizeof (int (*)[4])); // 4 printf("%d\n", sizeof (int *)); // 4 } 

Eu quero esclarecer. Existem algumas sugestões enganadoras nas respostas … Todas as funções seguintes podem receber matrizes inteiras:

 void f(int arr[]) void f(int arr[4]) void f(int *arr) 

Mas os argumentos formais não são os mesmos. Então, o compilador pode lidar com eles de maneira diferente. No sentido de gerenciamento de memory interna, todos os argumentos levam a pointers.

 void f(int arr[]) 

… f () aceita um array de qualquer tamanho.

 void f(int arr[4]) 

… O argumento formal indica um tamanho de matriz.

 void f(int *arr) 

… Você também pode passar um ponteiro inteiro. f () não sabe nada sobre o tamanho.