Foi o que encontrei durante meu período de aprendizado:
#include<iostream>
using namespace std;
int dis(char a[1])
{
int length = strlen(a);
char c = a[2];
return length;
}
int main()
{
char b[4] = "abc";
int c = dis(b);
cout << c;
return 0;
}
Então, na variável int dis(char a[1])
, o [1]
parece não fazer nada e não funciona
, porque eu posso usar a[2]
. Assim como int a[]
ou char *a
. Eu sei que o nome do array é um ponteiro e como transmitir um array, então meu quebra-cabeça não é sobre essa parte.
O que eu quero saber é por que os compiladores permitem esse comportamento ( int a[1]
). Ou tem outros significados que eu não conheço?
typedef
tipo de matriz. Assim, a "decadência para ponteiro" em tipos de argumento não é apenas açúcar sintático substituindo[]
com*
, isso realmente está acontecendo através do sistema tipo. Isso tem consequências reais para alguns tipos padrão comova_list
esse que podem ser definidos com o tipo de matriz ou sem matriz.int dis(char (*a)[1])
. Em seguida, você passar um ponteiro para um array:dis(&b)
. Se você estiver disposto a usar recursos C que não existem no C ++, também pode dizer coisas comovoid foo(int data[static 256])
eint bar(double matrix[*][*])
, mas essa é outra lata de worms.Respostas:
É uma peculiaridade da sintaxe para passar matrizes para funções.
Na verdade, não é possível transmitir uma matriz em C. Se você escrever uma sintaxe que pareça que deveria passar na matriz, o que realmente acontece é que um ponteiro para o primeiro elemento da matriz é passado.
Como o ponteiro não inclui nenhuma informação de comprimento, o conteúdo da
[]
lista formal de parâmetros da função é realmente ignorado.A decisão de permitir essa sintaxe foi tomada na década de 1970 e causou muita confusão desde então ...
fonte
void foo(int (*somearray)[20])
sintaxe. nesse caso, 20 é aplicado nos sites de chamada.[]
não são ignorados em matrizes multidimensionais, como mostra a resposta de pat. Portanto, a inclusão da sintaxe da matriz era necessária. Além disso, nada impede o compilador de emitir avisos, mesmo em matrizes unidimensionais.void foo(int (*args)[20]);
Além disso, estritamente falando, C não possui matrizes multidimensionais; mas possui matrizes cujos elementos podem ser outras matrizes. Isso não muda nada.O comprimento da primeira dimensão é ignorado, mas o comprimento de dimensões adicionais é necessário para permitir que o compilador calcule as compensações corretamente. No exemplo a seguir, a
foo
função passa um ponteiro para uma matriz bidimensional.O tamanho da primeira dimensão
[10]
é ignorado; o compilador não impedirá a indexação final (observe que o formal deseja 10 elementos, mas o real fornece apenas 2). No entanto, o tamanho da segunda dimensão[20]
é usado para determinar o passo de cada linha e, aqui, o formal deve corresponder ao real. Novamente, o compilador também não impedirá que você indexe o final da segunda dimensão.O deslocamento de bytes da base da matriz para um elemento
args[row][col]
é determinado por:Observe que
col >= 20
, se você realmente indexará em uma linha subsequente (ou fora do final de toda a matriz).sizeof(args[0])
, retorna80
na minha máquina ondesizeof(int) == 4
. No entanto, se eu tentar fazer issosizeof(args)
, recebo o seguinte aviso do compilador:Aqui, o compilador avisa que fornecerá apenas o tamanho do ponteiro no qual a matriz se deteriorou, em vez do tamanho da própria matriz.
fonte
args
. Nesse caso, o primeiro elemento de args é uma "matriz de 20 ints". Ponteiros incluem informações de tipo; o que é passado é "ponteiro para uma matriz de 20 polegadas".int (*)[20]
tipo; "ponteiro para uma matriz de 20 polegadas".O problema e como superá-lo em C ++
O problema foi explicado extensivamente por pat e Matt . O compilador está basicamente ignorando a primeira dimensão do tamanho da matriz, efetivamente ignorando o tamanho do argumento passado.
Em C ++, por outro lado, você pode facilmente superar essa limitação de duas maneiras:
std::array
(desde C ++ 11)Referências
Se sua função estiver apenas tentando ler ou modificar uma matriz existente (sem copiá-la), você poderá usar facilmente as referências.
Por exemplo, suponha que você queira ter uma função que redefina uma matriz de dez
int
s configurando cada elemento para0
. Você pode fazer isso facilmente usando a seguinte assinatura de função:Não apenas isso funcionará bem , mas também aplicará a dimensão da matriz .
Você também pode usar modelos para tornar o código acima genérico :
E, finalmente, você pode tirar proveito da
const
correção. Vamos considerar uma função que imprime uma matriz de 10 elementos:Ao aplicar o
const
qualificador, estamos impedindo possíveis modificações .A classe de biblioteca padrão para matrizes
Se você considera a sintaxe acima feia e desnecessária, como eu, podemos jogá-la na lata e usá-la
std::array
(desde C ++ 11).Aqui está o código refatorado:
Não é maravilhoso? Sem mencionar que o truque genérico de código que eu ensinei anteriormente, ainda funciona:
Não apenas isso, mas você copia e move a semântica gratuitamente. :)
Então, o que você está esperando? Vá usar
std::array
.fonte
É uma característica divertida de C que permite que você atire no seu pé com eficiência, se você estiver tão inclinado.
Eu acho que a razão é que C é apenas um passo acima da linguagem assembly. A verificação de tamanho e os recursos de segurança semelhantes foram removidos para permitir o desempenho máximo, o que não é ruim se o programador estiver sendo muito diligente.
Além disso, atribuir um tamanho ao argumento da função tem a vantagem de que quando a função é usada por outro programador, há uma chance de eles perceberem uma restrição de tamanho. Apenas usar um ponteiro não transmite essas informações para o próximo programador.
fonte
Primeiro, C nunca verifica os limites da matriz. Não importa se são parâmetros locais, globais, estáticos, seja qual for. Verificar os limites da matriz significa mais processamento, e C é suposto ser muito eficiente; portanto, a verificação dos limites da matriz é feita pelo programador quando necessário.
Segundo, existe um truque que torna possível passar por valor uma matriz para uma função. Também é possível retornar por valor uma matriz de uma função. Você só precisa criar um novo tipo de dados usando struct. Por exemplo:
Você tem que acessar os elementos como este: foo.a [1]. O ".a" extra pode parecer estranho, mas esse truque adiciona grande funcionalidade à linguagem C.
fonte
Para informar ao compilador que o myArray aponta para uma matriz de pelo menos 10 ints:
Um bom compilador deve avisar se você acessar o myArray [10]. Sem a palavra-chave "estática", os 10 não significariam nada.
fonte
[static]
permite que o compilador avise se você chamarbar
com umint[5]
. Não determina o que você pode acessar dentrobar
. O ônus é inteiramente do lado do chamador.error: expected primary-expression before 'static'
nunca vi essa sintaxe. é improvável que seja C ou C ++ padrão.Esse é um "recurso" conhecido do C, passado para o C ++ porque o C ++ deve compilar corretamente o código C.
O problema surge de vários aspectos:
Você poderia dizer que matrizes não são realmente suportadas em C (isso não é verdade, como eu estava dizendo antes, mas é uma boa aproximação); uma matriz é realmente tratada como um ponteiro para um bloco de dados e acessada usando a aritmética do ponteiro. Como C NÃO possui nenhuma forma de RTTI, é necessário declarar o tamanho do elemento da matriz no protótipo da função (para suportar a aritmética do ponteiro). Isso é ainda "mais verdadeiro" para matrizes multidimensionais.
Enfim, tudo acima não é mais verdade: p
A maioria dos compiladores moderno C / C ++ fazer limites de apoio a verificação, mas normas requerem que ele seja desativado por padrão (para compatibilidade com versões anteriores). Versões razoavelmente recentes do gcc, por exemplo, fazem a verificação do intervalo em tempo de compilação com "-O3 -Wall -Wextra" e a verificação completa dos limites de tempo de execução com "-fbounds -ecking".
fonte
struct MyStruct s = { .field1 = 1, .field2 = 2 };
) para inicializar estruturas, porque é uma maneira muito mais clara de inicializar uma estrutura. Como resultado, a maioria dos códigos C atuais será rejeitada pelos compiladores C ++ padrão, porque a maioria dos códigos C estará inicializando estruturas.C não apenas transformará um parâmetro do tipo
int[5]
em*int
; dada a declaraçãotypedef int intArray5[5];
, ele irá transformar um parâmetro do tipointArray5
de*int
bem. Existem algumas situações em que esse comportamento, embora estranho, é útil (especialmente em itens como ova_list
definido emstdargs.h
, que algumas implementações definem como uma matriz). Seria ilógico permitir como parâmetro um tipo definido comoint[5]
(ignorando a dimensão), mas não permitirint[5]
que seja especificado diretamente.Acho absurdo o manuseio de C por parâmetros do tipo array, mas é uma conseqüência dos esforços para adotar uma linguagem ad-hoc, grande parte da qual não foi particularmente bem definida ou pensada, e tentar criar comportamentos. especificações consistentes com o que as implementações existentes fizeram para os programas existentes. Muitas das peculiaridades de C fazem sentido quando vistas sob essa luz, especialmente se considerarmos que, quando muitas delas foram inventadas, grande parte da linguagem que conhecemos hoje ainda não existia. Pelo que entendi, no antecessor de C, chamado BCPL, os compiladores não acompanharam muito bem os tipos de variáveis. Uma declaração
int arr[5];
era equivalente aint anonymousAllocation[5],*arr = anonymousAllocation;
; uma vez que a alocação foi anulada. o compilador não sabia nem se importava searr
era um ponteiro ou uma matriz. Quando acessado como umarr[x]
ou*arr
, seria considerado um ponteiro, independentemente de como foi declarado.fonte
Uma coisa que ainda não foi respondida é a pergunta real.
As respostas já dadas explicam que matrizes não podem ser passadas por valor para uma função em C ou C ++. Eles também explicam que um parâmetro declarado como
int[]
é tratado como se tivesse um tipoint *
e que uma variável do tipoint[]
pode ser passada para essa função.Mas eles não explicam por que nunca foi cometido um erro ao fornecer explicitamente um comprimento de matriz.
Por que o último destes não é um erro?
Uma razão para isso é que ele causa problemas com os typedefs.
Se fosse um erro especificar o comprimento da matriz nos parâmetros da função, você não conseguiria usar o
myarray
nome no parâmetro da função. E como algumas implementações usam tipos de matriz para tipos de biblioteca padrão, comova_list
, e todas as implementações são necessárias para criarjmp_buf
um tipo de matriz, seria muito problemático se não houvesse uma maneira padrão de declarar parâmetros de função usando esses nomes: sem essa capacidade, poderia não deve ser uma implementação portátil de funções comovprintf
.fonte
É permitido que os compiladores possam verificar se o tamanho da matriz passada é o mesmo que o esperado. Compiladores podem avisar um problema, se não for o caso.
fonte