Como obter de forma confiável o tamanho da matriz de estilo C?

Como faço para obter o tamanho de uma matriz de estilo C de forma confiável? O método geralmente recomendado parece ser o uso de sizeof , mas não funciona na function foo , em que x é passado:

 #include  void foo(int x[]) { std::cerr << (sizeof(x) / sizeof(int)); // 2 } int main(){ int x[] = {1,2,3,4,5}; std::cerr << (sizeof(x) / sizeof(int)); // 5 foo(x); return 0; } 

As respostas a esta questão recomendam sizeof mas eles não dizem que (aparentemente?) Não funciona se você passar a matriz ao redor. Então, eu tenho que usar uma sentinela? (Eu não acho que os usuários da minha function foo sempre podem ser confiáveis ​​para colocar um sentinela no final. Claro, eu poderia usar std::vector , mas então eu não tenho a syntax abreviada legal {1,2,3,4,5} )

Em C array, os parâmetros em C são realmente apenas pointers, então sizeof() não funcionará. Você precisa passar o tamanho como outro parâmetro ou usar um sentinela – o que for mais apropriado para o seu projeto.

Algumas outras opções:

Algumas outras informações:

  • para C ++, em vez de passar um ponteiro de matriz bruta, convém que o parâmetro use algo que envolva a matriz em um modelo de class que monitore o tamanho da matriz e forneça methods para copiar dados na matriz de maneira segura. Algo como o template array_proxy do STLSoft ou o boost :: array do Boost pode ajudar. Eu usei um modelo array_proxy para um bom efeito antes. Dentro da function usando o parâmetro, você obtém operações do tipo std::vector , mas o chamador da function pode estar usando uma matriz C simples. Não há cópia da matriz – o modelo array_proxy cuida do empacotamento do ponteiro da matriz e do tamanho da matriz quase automaticamente.

  • uma macro para usar em C para determinar o número de elementos em uma matriz (para quando sizeof () pode ajudar – ou seja, você não está lidando com um ponteiro simples): Existe uma function padrão em C que retornaria o comprimento de uma matriz?

Um idioma comum mencionado na documentação do GNU Libstdc ++ é o lengthof function:

 template inline unsigned int lengthof(T (&)[sz]) { return sz; } 

Você pode usá-lo como

 int x[] = {1,2,3,4,5}; std::cerr << lengthof(x) << std::endl; 

Aviso: isso funcionará somente quando a matriz não tiver decaído em um ponteiro .

Você pode passar o tamanho, usar sentinela ou usar melhor std :: vector. Mesmo que std :: vector não tenha listas de boot, ainda é fácil construir um vetor com um conjunto de elementos (embora não seja tão bom)

 static const int arr[] = {1,2,3,4,5}; vector vec (arr, arr + sizeof(arr) / sizeof(arr[0]) ); 

A class std :: vector também faz com que erros sejam mais difíceis, o que vale seu peso em ouro. Outro bônus é que todo o C ++ deve estar familiarizado com ele e a maioria dos aplicativos C ++ devem estar usando um std :: vector em vez de um array C bruto.

Como uma nota rápida, o C ++ 0x adiciona listas inicializadoras

 std::vector v = {1, 2, 3, 4}; 

Você também pode usar o Boost.Assign para fazer a mesma coisa, embora a syntax seja um pouco mais complicada.

 std::vector v = boost::assign::list_of(1)(2)(3)(4); 

ou

 std::vector v; v += 1, 2, 3, 4; 

c não fornece suporte nativo para isso. Quando uma matriz é passada para fora do escopo declarado, seu tamanho é perdido.

Você pode passar o tamanho com o array. Você pode até empacotá-los em uma estrutura para manter sempre o tamanho, embora você tenha alguma sobrecarga de bookkeepping com isso.

Eu também concordo que o método de Corwin acima é muito bom.

 template  void foo(int (&x)[N]) { std::cerr << N; } 

Eu não acho que alguém tenha dado uma boa razão porque isso não é uma boa ideia.
Em java, por exemplo, podemos escrever coisas como:

 int numbers [] = {1, 2, 3, 4}; for(int i = 0; i < numbers.length(); i++) { System.out.println(numbers[i]+"\n"); } 

Em C ++ seria legal em vez de dizer

 int numbers [] = {1, 2, 3, 4}; int size = sizeof(numbers)/sizeof(int); for(int i = 0; i < size; i++) { cout << numbers[i] << endl; } 

Nós poderíamos dar um passo adiante e ir

 template  int size(int (&X)[N]) { return N; } 

Ou se isso causar problemas, suponho que você poderia escrever explicitamente:

 template < int N > int size(int (&X)[N]) { int value = (sizeof(X)/sizeof(X[0])); return value; } 

Então nós apenas temos que ir no main:

 int numbers [] = {1, 2, 3, 4}; for(int i = 0; i < size(numbers); i++) { cout << numbers[i] << endl; } 

Faz sentido para mim 🙂

Que tal agora?..

 template  void foo(int (&x)[N]) { std::cerr << N; } 

Uma expressão de array terá seu tipo implicitamente convertido de “N-element array de T” para “pointer to T” e seu valor será o endereço do primeiro elemento na matriz, a menos que a expressão array seja o operando do sizeof ou endereço-de ( & ) operadores, ou se a expressão de matriz é um literal de seqüência de caracteres que está sendo usado para inicializar outro array em uma declaração. Em resumo, você não pode passar um array para uma function como um array ; o que a function recebe é um valor de ponteiro, não um valor de matriz.

Você tem que passar o tamanho da matriz como um parâmetro separado.

Como você está usando o C ++, use vetores (ou algum outro contêiner STL adequado) em vez de matrizes no estilo C. Sim, você perde a syntax abreviada, mas a desvantagem vale mais do que isso. A sério.

Você precisa passar o tamanho junto com a matriz, assim como é feito em muitas funções da biblioteca, por exemplo strncpy() , strncmp() etc. Desculpe, é assim que funciona em C :-).

Alternativamente, você poderia implantar sua própria estrutura como:

 struct array { int* data; int size; }; 

e passe-o pelo seu código.

É claro que você ainda pode usar std::list ou std::vector se quiser ser mais C ++ -ish.

Desde c ++ 11, há uma maneira muito conveniente:

 static const int array[] = { 1, 2, 3, 6 }; int size = (int)std::distance(std::begin(array), std::end(array))+1;