Como determino o tamanho da minha matriz em C?

Respostas:

1260

Sumário executivo:

int a[17];
size_t n = sizeof(a)/sizeof(a[0]);

Resposta completa:

Para determinar o tamanho da sua matriz em bytes, você pode usar o sizeof operador:

int a[17];
size_t n = sizeof(a);

No meu computador, ints têm 4 bytes de comprimento, então n é 68.

Para determinar o número de elementos na matriz, podemos dividir o tamanho total da matriz pelo tamanho do elemento da matriz. Você poderia fazer isso com o tipo, assim:

int a[17];
size_t n = sizeof(a) / sizeof(int);

e obtenha a resposta correta (68/4 = 17), mas se o tipo de aalteração for alterado, você terá um erro desagradável se esquecer de alterar sizeof(int)também.

Portanto, o divisor preferido é sizeof(a[0])ou equivalente sizeof(*a), o tamanho do primeiro elemento da matriz.

int a[17];
size_t n = sizeof(a) / sizeof(a[0]);

Outra vantagem é que agora você pode facilmente parametrizar o nome da matriz em uma macro e obter:

#define NELEMS(x)  (sizeof(x) / sizeof((x)[0]))

int a[17];
size_t n = NELEMS(a);
Mark Harrison
fonte
6
O código gerado será idêntico, pois o compilador conhece o tipo de * int_arr em tempo de compilação (e, portanto, o valor de sizeof (* int_arr)). Será uma constante, e o compilador pode otimizar adequadamente.
Mark Harrison
10
Deve ser o caso de todos os compiladores, pois os resultados de sizeof são definidos como uma constante em tempo de compilação.
Mark Harrison
451
Importante : Não pare de ler aqui, leia a próxima resposta! Isso funciona apenas para matrizes na pilha , por exemplo, se você estiver usando malloc () ou acessando um parâmetro de função, estará sem sorte. Ver abaixo.
Markus
7
Para a programação da API do Windows em C ou C ++, existe o ARRAYSIZEmakro definido em WinNT.h(que é puxado por outros cabeçalhos). Portanto, os usuários do WinAPI não precisam definir seu próprio makro.
Lumi
17
@ Markus funciona para qualquer variável que tenha um tipo de array; isso não precisa estar "na pilha". Por exemplo static int a[20];. Mas seu comentário é útil para leitores que podem não perceber a diferença entre uma matriz e um ponteiro.
MM
808

O sizeofcaminho é o caminho certo se você estiver lidando com matrizes não recebidas como parâmetros. Uma matriz enviada como parâmetro para uma função é tratada como um ponteiro; portanto sizeof, retornará o tamanho do ponteiro, em vez do tamanho da matriz.

Assim, funções internas esse método não funciona. Em vez disso, sempre passe um parâmetro adicional size_t sizeindicando o número de elementos na matriz.

Teste:

#include <stdio.h>
#include <stdlib.h>

void printSizeOf(int intArray[]);
void printLength(int intArray[]);

int main(int argc, char* argv[])
{
    int array[] = { 0, 1, 2, 3, 4, 5, 6 };

    printf("sizeof of array: %d\n", (int) sizeof(array));
    printSizeOf(array);

    printf("Length of array: %d\n", (int)( sizeof(array) / sizeof(array[0]) ));
    printLength(array);
}

void printSizeOf(int intArray[])
{
    printf("sizeof of parameter: %d\n", (int) sizeof(intArray));
}

void printLength(int intArray[])
{
    printf("Length of parameter: %d\n", (int)( sizeof(intArray) / sizeof(intArray[0]) ));
}

Saída (em um sistema operacional Linux de 64 bits):

sizeof of array: 28
sizeof of parameter: 8
Length of array: 7
Length of parameter: 2

Saída (em um sistema operacional Windows de 32 bits):

sizeof of array: 28
sizeof of parameter: 4
Length of array: 7
Length of parameter: 1
Elideb
fonte
10
por que length of parameter:2se apenas um ponteiro para o primeiro elemento da matriz for passado?
Bbvarghe 5/08/2013
16
@Bbvarghe Isso ocorre porque os ponteiros nos sistemas de 64 bits têm 8 bytes (sizeof (intArray)), mas as entradas ainda têm (geralmente) 4 bytes de comprimento (sizeof (intArray [0])).
Elideb
13
@ Pacerier: Não há código correto - a solução usual é passar o comprimento junto com o array como um argumento separado.
precisa saber é o seguinte
10
Espere, então não há como acessar a matriz diretamente de um ponteiro e ver seu tamanho? Novo para C aqui.
Sudo #
7
@ Michael Trouw: você pode usar a sintaxe operador, se é te faz sentir melhor: (sizeof array / sizeof *array).
precisa saber é
134

Vale ressaltar que sizeofisso não ajuda ao lidar com um valor de matriz que se deteriorou para um ponteiro: mesmo que aponte para o início de uma matriz, para o compilador é o mesmo que um ponteiro para um único elemento dessa matriz . Um ponteiro não "lembra" mais nada sobre a matriz que foi usada para inicializá-lo.

int a[10];
int* p = a;

assert(sizeof(a) / sizeof(a[0]) == 10);
assert(sizeof(p) == sizeof(int*));
assert(sizeof(*p) == sizeof(int));
Magnus Hoff
fonte
1
@ Magnus: O padrão define sizeof como produzindo o número de bytes no objeto e esse sizeof (char) é sempre um. O número de bits em um byte é específico da implementação. Editar: seção padrão ANSI C ++ 5.3.3 Sizeof: "O operador sizeof gera o número de bytes na representação do objeto de seu operando. Sizeof [...] (char), sizeof (char assinado) e sizeof (char não assinado) são 1; o resultado do tamanho do aplicado a qualquer outro tipo fundamental é definido pela implementação ".
Skizz
Seção 1.6 O modelo de memória C ++: "A unidade de armazenamento fundamental no modelo de memória C ++ é o byte. Um byte é pelo menos grande o suficiente para conter qualquer membro do conjunto de caracteres de execução básica e é composto por uma sequência contígua de bits, o número dos quais é definido pela implementação ".
Skizz
2
Lembro que o CRAY tinha C com char32 bits. Tudo o que o padrão diz é que valores inteiros de 0 a 127 podem ser representados e seu intervalo é de pelo menos -127 a 127 (o caractere é assinado) ou de 0 a 255 (o caracter não é assinado).
21133 vonbrand
5
Esta é uma excelente resposta. Quero comentar que todas as afirmações acima são avaliadas como TRUE.
Javad 4/15
49

O tamanho do "truque" é a melhor maneira que conheço, com uma pequena mas (para mim, essa é uma grande irritação) importante mudança no uso de parênteses.

Como a entrada da Wikipedia deixa claro, os Cs sizeofnão são uma função; é um operador . Portanto, ele não requer parênteses em torno de seu argumento, a menos que o argumento seja um nome de tipo. Isso é fácil de lembrar, pois faz com que o argumento pareça uma expressão convertida, que também usa parênteses.

Então: se você tem o seguinte:

int myArray[10];

Você pode encontrar o número de elementos com código como este:

size_t n = sizeof myArray / sizeof *myArray;

Isso, para mim, é muito mais fácil do que a alternativa entre parênteses. Também sou a favor do uso do asterisco na parte direita da divisão, pois é mais conciso do que a indexação.

Obviamente, isso também é tempo de compilação, portanto não há necessidade de se preocupar com a divisão que afeta o desempenho do programa. Portanto, use este formulário sempre que puder.

É sempre melhor usar sizeof em um objeto real quando você tiver um, em vez de em um tipo, pois você não precisará se preocupar em cometer um erro e declarar o tipo errado.

Por exemplo, digamos que você tenha uma função que gera alguns dados como um fluxo de bytes, por exemplo, em uma rede. Vamos chamar a função send()e fazer com que ela tome como argumento um ponteiro para o objeto a ser enviado e o número de bytes no objeto. Então, o protótipo se torna:

void send(const void *object, size_t size);

E então você precisa enviar um número inteiro, para codificá-lo assim:

int foo = 4711;
send(&foo, sizeof (int));

Agora, você introduziu uma maneira sutil de se dar um tiro no pé, especificando o tipo fooem dois lugares. Se um mudar, mas o outro não, o código será quebrado. Assim, sempre faça assim:

send(&foo, sizeof foo);

Agora você está protegido. Claro, você duplica o nome da variável, mas isso tem uma alta probabilidade de quebrar de uma maneira que o compilador possa detectar, se você a alterar.

descontrair
fonte
Btw, são instruções idênticas no nível do processador? Será que sizeof(int)requerem instruções menores do que sizeof(foo)?
Pacerier 21/09/13
@ Pacerier: não, eles são idênticos. Pense em int x = 1+1;versus int x = (1+1);. Aqui, os parênteses são puramente absolutamente estéticos.
quetzalcoatl
@Aidiakapi Isso não é verdade, considere os VLAs C99.
desenrole
@unwind Obrigado, estou corrigido. Para corrigir meu comentário, sizeofsempre será constante em C ++ e C89. Com matrizes de comprimento variável do C99, ele pode ser avaliado em tempo de execução.
Aidiakapi
2
sizeofpode ser um operador, mas deve ser tratado como uma função de acordo com Linus Torvalds. Concordo. Leia seu racional aqui: lkml.org/lkml/2012/7/11/103
Dr. Person Person II
37
int size = (&arr)[1] - arr;

Confira este link para obter explicações

Arjun Sreedharan
fonte
7
Nitpick pequeno: o resultado da subtração do ponteiro tem tipo ptrdiff_t. (Normalmente, no sistema de 64 bits, esse será um tipo maior que int). Mesmo se você mudar intpara ptrdiff_tesse código, ele ainda arrterá um erro se ocupar mais da metade do espaço de endereço.
6134 MM
2
@MM Outro pequeno detalhe: dependendo da arquitetura do sistema, o espaço de endereço não é tão grande quanto o tamanho do ponteiro na maioria dos sistemas. O Windows, por exemplo, limita o espaço de endereço para aplicativos de 64 bits a 8 TB ou 44 bits. Portanto, mesmo se você tiver uma matriz maior que a metade do seu espaço de endereço de 4,1 TB, por exemplo, não será um erro. Somente se seu espaço de endereço exceder 63 bits nesses sistemas, é possível até encontrar esse bug. Em geral, não se preocupe.
Aidiakapi
1
@Aidiakapi no Linux x86 de 32 bits ou no Windows, com a /3Gopção de divisão de usuário / kernel 3G / 1G, que permite que as matrizes tenham até 75% do tamanho do espaço de endereço.
Ruslan
1
Considere foo buf1[80]; foo buf2[sizeof buf1/sizeof buf1[0]]; foo buf3[(&buf1)[1] - buf1];como variáveis ​​globais. buf3[]declaração falha porque (&buf1)[1] - buf1não é uma constante.
chux - Restabelece Monica
2
Esse é um comportamento tecnicamente indefinido, pois o padrão desaprova explicitamente a exclusão de referências após o final de uma matriz (mesmo que você não tente ler o valor armazenado).
MM
26

Você pode usar o operador sizeof, mas ele não funcionará para funções, porque terá a referência do ponteiro. Você pode fazer o seguinte para encontrar o comprimento de uma matriz:

len = sizeof(arr)/sizeof(arr[0])

Código originalmente encontrado aqui: programa C para encontrar o número de elementos em uma matriz

Mohd Shibli
fonte
21

Se você conhece o tipo de dados da matriz, pode usar algo como:

int arr[] = {23, 12, 423, 43, 21, 43, 65, 76, 22};

int noofele = sizeof(arr)/sizeof(int);

Ou, se você não souber o tipo de dados da matriz, poderá usar algo como:

noofele = sizeof(arr)/sizeof(arr[0]);

Nota: Isso funciona apenas se a matriz não estiver definida em tempo de execução (como malloc) e a matriz não for passada em uma função. Nos dois casos, arr(nome da matriz) é um ponteiro.

Abhitesh khatri
fonte
4
int noofele = sizeof(arr)/sizeof(int);é apenas metade do caminho melhor que a codificação int noofele = 9;. O uso sizeof(arr)mantém a flexibilidade caso o tamanho da matriz mude. No entanto, sizeof(int)precisa de uma atualização, caso o tipo de arr[]alteração. Melhor usar sizeof(arr)/sizeof(arr[0])mesmo se o tipo for bem conhecido. Não está claro por que usar intfor noofelevs. size_t, o tipo retornado por sizeof().
chux - Restabelece Monica 20/05
19

A macro ARRAYELEMENTCOUNT(x)que todo mundo está usando avalia incorretamente . Realisticamente, isso é apenas uma questão delicada, porque você não pode ter expressões que resultem em um tipo de 'matriz'.

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x[0]))

ARRAYELEMENTCOUNT(p + 1);

Realmente avalia como:

(sizeof (p + 1) / sizeof (p + 1[0]));

Enquanto que

/* Compile as: CL /P "macro.c" */
# define ARRAYELEMENTCOUNT(x) (sizeof (x) / sizeof (x)[0])

ARRAYELEMENTCOUNT(p + 1);

Ele avalia corretamente como:

(sizeof (p + 1) / sizeof (p + 1)[0]);

Isso realmente não tem muito a ver com o tamanho das matrizes explicitamente. Acabei de notar muitos erros ao não observar verdadeiramente como o pré-processador C funciona. Você sempre quebra o parâmetro macro, e não uma expressão pode estar envolvida.


Isto está correto; meu exemplo foi ruim. Mas isso é exatamente o que deveria acontecer. Como mencionei anteriormente p + 1, acabará como um tipo de ponteiro e invalidará toda a macro (como se você tentasse usar a macro em uma função com um parâmetro de ponteiro).

No final do dia, nesse caso em particular , a falha realmente não importa (por isso estou perdendo tempo de todos; huzzah!), Porque você não tem expressões com um tipo de 'array'. Mas, na verdade, acho importante a questão dos sutis de avaliação pré-processador.

Peter Mortensen
fonte
2
Obrigada pelo esclarecimento. A versão original resulta em um erro em tempo de compilação. Clang reporta "o valor subscrito não é uma matriz, ponteiro ou vetor". Esse comportamento parece preferível nesse caso, embora seus comentários sobre a ordem de avaliação nas macros sejam bem aceitos.
Mark Harrison
1
Eu não tinha pensado na reclamação do compilador como uma notificação automática de um tipo incorreto. Obrigado!
3
Existe uma razão para não usar (sizeof (x) / sizeof (*x))?
seriousdev
16

Para matrizes multidimensionais , é um pouco mais complicado. Frequentemente, as pessoas definem constantes macro explícitas, ou seja,

#define g_rgDialogRows   2
#define g_rgDialogCols   7

static char const* g_rgDialog[g_rgDialogRows][g_rgDialogCols] =
{
    { " ",  " ",    " ",    " 494", " 210", " Generic Sample Dialog", " " },
    { " 1", " 330", " 174", " 88",  " ",    " OK",        " " },
};

Mas essas constantes também podem ser avaliadas em tempo de compilação com sizeof :

#define rows_of_array(name)       \
    (sizeof(name   ) / sizeof(name[0][0]) / columns_of_array(name))
#define columns_of_array(name)    \
    (sizeof(name[0]) / sizeof(name[0][0]))

static char* g_rgDialog[][7] = { /* ... */ };

assert(   rows_of_array(g_rgDialog) == 2);
assert(columns_of_array(g_rgDialog) == 7);

Observe que esse código funciona em C e C ++. Para matrizes com mais de duas dimensões, use

sizeof(name[0][0][0])
sizeof(name[0][0][0][0])

etc., ad infinitum.

Andreas Spindler
fonte
15
sizeof(array) / sizeof(array[0])
Ted Percival
fonte
Dependendo do tipo array, você não precisa usar sizeof(array) / sizeof(array[0])if arrayé uma matriz de char, unsigned charou signed char- Quote from C18,6.5.3.4 / 4: "Quando sizeof é aplicado a um operando que possui o tipo char, char não assinado ou char assinado , (ou uma versão qualificada), o resultado é 1. " Nesse caso, você pode simplesmente fazer o sizeof(array)que foi explicado na minha resposta dedicada .
RobertS apoia Monica Cellio
15

Tamanho de uma matriz em C:

int a[10];
size_t size_of_array = sizeof(a);      // Size of array a
int n = sizeof (a) / sizeof (a[0]);    // Number of elements in array a
size_t size_of_element = sizeof(a[0]); // Size of each element in array a                                          
                                       // Size of each element = size of type
Yogeesh HT
fonte
4
Curioso que o código usado size_t size_of_elementainda intcom int n = sizeof (a) / sizeof (a[0]); e nãosize_t n = sizeof (a) / sizeof (a[0]);
Chux - Reintegrar Monica
1
Olá @Yogeesh HT, você pode responder à dúvida do chux. Também estou muito curioso para saber como int n = sizeof (a) / sizeof (a [0]) está fornecendo o comprimento da matriz e por que não estamos usando size_t para o comprimento da matriz. Alguém pode responder?
Cérebro
1
@Brain sizeof (a) fornece sizeof de todos os elementos presentes na matriz a sizeof (a [0]) fornece sizeof dos primeiros elementos. Suponha que a = {1,2,3,4,5}; sizeof (a) = 20bytes (se sizeof (int) = 4bytes multiplicam 5), sizeof (a [0]) = 4bytes, assim 20/4 = 5 ou seja, nenhum dos elementos
Yogeesh HT
2
@YogeeshHT Para matrizes muito grandes char a[INT_MAX + 1u];, int ncomo usadas em, int n = sizeof (a) / sizeof (a[0]);é insuficiente (é UB). O uso size_t n = sizeof (a) / sizeof (a[0]);não incorre nesse problema.
chux - Restabelece Monica
13

Eu recomendaria nunca usar sizeof(mesmo que possa ser usado) para obter qualquer um dos dois tamanhos diferentes de uma matriz, seja em número de elementos ou em bytes, que são os dois últimos casos que mostro aqui. Para cada um dos dois tamanhos, as macros mostradas abaixo podem ser usadas para torná-lo mais seguro. A razão é tornar óbvia a intenção do código para mantenedores, ea diferença sizeof(ptr)de sizeof(arr), à primeira vista (que escrito desta forma não é óbvia), de modo que os erros são então óbvio para todos a leitura do código.


TL; DR:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]) + must_be_array(arr))

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

#define ARRAY_BYTES(arr)    (sizeof((arr)[0]) * ARRAY_SIZE(arr))

must_be_array(arr)(definido abaixo) É necessário como o -Wsizeof-pointer-divbuggy (a partir de abril / 2020):

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

Houve erros importantes em relação a este tópico: https://lkml.org/lkml/2015/9/3/428

Eu discordo da solução que Linus fornece, que é nunca usar a notação de matriz para parâmetros de funções.

Eu gosto da notação de matriz como documentação de que um ponteiro está sendo usado como uma matriz. Mas isso significa que uma solução infalível precisa ser aplicada para que seja impossível escrever um código de buggy.

A partir de uma matriz, temos três tamanhos que podemos querer saber:

  • O tamanho dos elementos da matriz
  • O número de elementos na matriz
  • O tamanho em bytes que a matriz usa na memória

O tamanho dos elementos da matriz

O primeiro é muito simples, e não importa se estamos lidando com uma matriz ou um ponteiro, porque é feito da mesma maneira.

Exemplo de uso:

void foo(ptrdiff_t nmemb, int arr[static nmemb])
{
        qsort(arr, nmemb, sizeof(arr[0]), cmp);
}

qsort() precisa desse valor como seu terceiro argumento.


Para os outros dois tamanhos, que são o tópico da pergunta, queremos ter certeza de que estamos lidando com uma matriz e interromper a compilação, se não, porque se estivermos lidando com um ponteiro, obteremos valores errados . Quando a compilação é interrompida, poderemos ver facilmente que não estávamos lidando com uma matriz, mas com um ponteiro, e teremos que escrever o código com uma variável ou macro que armazene o tamanho da matriz. matriz atrás do ponteiro.


O número de elementos na matriz

Esta é a mais comum e muitas respostas forneceram a macro típica ARRAY_SIZE:

#define ARRAY_SIZE(arr)     (sizeof(arr) / sizeof((arr)[0]))

Como o resultado de ARRAY_SIZE é comumente usado com variáveis ​​assinadas do tipo ptrdiff_t, é bom definir uma variante assinada dessa macro:

#define ARRAY_SSIZE(arr)    ((ptrdiff_t)ARRAY_SIZE(arr))

Matrizes com mais de PTRDIFF_MAXmembros fornecerão valores inválidos para esta versão assinada da macro, mas, ao ler C17 :: 6.5.6.9, matrizes como essa já estão brincando com fogo. Somente ARRAY_SIZEesize_t deve ser usado nesses casos.

Versões recentes de compiladores, como o GCC 8, avisam quando você aplica essa macro a um ponteiro, portanto é seguro (existem outros métodos para torná-lo seguro em compiladores mais antigos).

Ele funciona dividindo o tamanho em bytes de toda a matriz pelo tamanho de cada elemento.

Exemplos de uso:

void foo(ptrdiff_t nmemb)
{
        char buf[nmemb];

        fgets(buf, ARRAY_SIZE(buf), stdin);
}

void bar(ptrdiff_t nmemb)
{
        int arr[nmemb];

        for (ptrdiff_t i = 0; i < ARRAY_SSIZE(arr); i++)
                arr[i] = i;
}

Se essas funções não usassem matrizes, mas as obtivessem como parâmetros, o código anterior não seria compilado, portanto seria impossível ter um bug (dado que uma versão recente do compilador é usada ou que outro truque é usado) , e precisamos substituir a chamada de macro pelo valor:

void foo(ptrdiff_t nmemb, char buf[nmemb])
{

        fgets(buf, nmemb, stdin);
}

void bar(ptrdiff_t nmemb, int arr[nmemb])
{

        for (ptrdiff_t i = 0; i < nmemb; i++)
                arr[i] = i;
}

O tamanho em bytes que a matriz usa na memória

ARRAY_SIZE é comumente usado como uma solução para o caso anterior, mas esse caso raramente é escrito com segurança, talvez por ser menos comum.

A maneira comum de obter esse valor é usar sizeof(arr). O problema: o mesmo que o anterior; se você tiver um ponteiro em vez de uma matriz, seu programa ficará louco.

A solução para o problema envolve o uso da mesma macro de antes, que sabemos ser segura (interrompe a compilação se aplicada a um ponteiro):

#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

Como funciona é muito simples: desfaz a divisão que o ARRAY_SIZEfaz; portanto, após cancelamentos matemáticos, você acaba com apenas um sizeof(arr), mas com a segurança adicional doARRAY_SIZE construção.

Exemplo de uso:

void foo(ptrdiff_t nmemb)
{
        int arr[nmemb];

        memset(arr, 0, ARRAY_BYTES(arr));
}

memset() precisa desse valor como seu terceiro argumento.

Como antes, se o array for recebido como um parâmetro (um ponteiro), ele não será compilado e teremos que substituir a chamada de macro pelo valor:

void foo(ptrdiff_t nmemb, int arr[nmemb])
{

        memset(arr, 0, sizeof(arr[0]) * nmemb);
}

Atualização (23 / abr / 2020): -Wsizeof-pointer-divé de buggy :

Hoje descobri que o novo aviso no GCC funciona apenas se a macro estiver definida em um cabeçalho que não seja um cabeçalho do sistema. Se você definir a macro em um cabeçalho instalado no seu sistema (normalmente /usr/local/include/ou /usr/include/) ( #include <foo.h>), o compilador NÃO emitirá um aviso (tentei o GCC 9.3.0).

Então, temos #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))e queremos torná-lo seguro. Vamos precisar de _Static_assert()extensões C11 e GCC: Declarações e declarações em expressões , __builtin_types_compatible_p :

#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        sizeof(arr) / sizeof((arr)[0]);                                \
}                                                                       \
)

Agora ARRAY_SIZE()é completamente seguro e, portanto, todos os seus derivados estarão seguros.


Atualização: libbsd fornece __arraycount() :

Libbsd fornece a macro __arraycount()em <sys/cdefs.h>que não é seguro porque falta um par de parênteses, mas podemos adicionar os parênteses nós mesmos, e, portanto, não precisa mesmo de escrever a divisão em nosso cabeçalho (por que nós duplicar o código que já existe? ) Essa macro é definida no cabeçalho do sistema; portanto, se a usarmos, somos forçados a usar as macros acima.

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")

#define ARRAY_SIZE(arr)         (                                       \
{                                                                       \
        Static_assert_array(arr);                                       \
        __arraycount((arr));                                            \
}                                                                       \
)

#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))

Alguns sistemas fornecem nitems(), em <sys/param.h>vez disso, e alguns sistemas de fornecer tanto. Você deve verificar seu sistema e usar o que possui, e talvez usar alguns condicionais do pré-processador para portabilidade e suporte aos dois.


Atualização: permita que a macro seja usada no escopo do arquivo:

Infelizmente, a ({})extensão gcc não pode ser usada no escopo do arquivo. Para poder usar a macro no escopo do arquivo, a asserção estática deve estar dentro sizeof(struct {}). Em seguida, multiplique 0para não afetar o resultado. Uma conversão para (int)pode ser boa para simular uma função que retorna (int)0(nesse caso, não é necessária, mas é reutilizável para outras coisas).

#include <sys/cdefs.h>


#define is_same_type(a, b)      __builtin_types_compatible_p(typeof(a), typeof(b))
#define is_array(a)             (!is_same_type((a), &(a)[0]))
#define Static_assert_array(a)  _Static_assert(is_array(a), "Not a `[]` !")
#define must_be_array(a)        (                                       \
        0 * (int)sizeof(                                                \
                struct {                                                \
                        Static_assert_array(a);                         \
                        char ISO_C_forbids_a_struct_with_no_members__;  \
                }                                                       \
        )                                                               \
)

#define ARRAY_SIZE(arr)         (__arraycount((arr)) + must_be_array(arr))
#define ARRAY_SSIZE(arr)        ((ptrdiff_t)ARRAY_SIZE(arr))
#define ARRAY_BYTES(arr)        (sizeof((arr)[0]) * ARRAY_SIZE(arr))
Cacahuete Frito
fonte
3
Você se importaria de explicar por que o voto negativo? Este mostra uma solução para uma construção inseguro e comum ( sizeof(arr)) que não é mostrada em outra parte: ARRAY_BYTES(arr).
Cacahuete Frito
2
ARRAY_SIZE é comum o suficiente para ser usado livremente, e ARRAY_BYTES é muito explícito em seu nome, deve ser definido ao lado de ARRAY_SIZE para que um usuário possa ver ambos facilmente e, por seu uso, não acho que alguém que esteja lendo o código tenha dúvidas sobre o que faz. O que eu quis dizer é não usar um simples sizeof, mas usar essas construções; se você quiser escrever essas construções todas as vezes, provavelmente cometerá um erro (muito comum se você copiar colar e também muito comum se você as escrever todas as vezes porque elas têm muitos parênteses) ...
Cacahuete Frito
3
..., portanto, estou na conclusão principal: uma única sizeofé claramente insegura (as razões estão na resposta), e não usando macros, mas usando as construções que forneci, a cada vez, é ainda mais insegura, então o único caminho a percorrer são macros.
Cacahuete Frito
3
@ MarkHarrison Eu sei a diferença entre ponteiros e matrizes. Mas houve momentos em que tive uma função que posteriormente refatorei em pequenas funções, e o que primeiro era uma matriz, depois um ponteiro, e esse é um ponto em que, se você esquecer de alterar o tamanho, você estraga tudo, e é fácil não ver um daqueles.
Cacahuete Frito
3
@hyde Também sei que a diferença não significa que todo mundo sabe a diferença, e por que não usar algo que basicamente remova 100% desses bugs? Esse bug quase chegou ao Linux; chegou a Linus, o que significa que passou por um exame minucioso e também significa que há a possibilidade de o mesmo bug ter entrado no Linux em outra parte do kernel, como ele diz.
Cacahuete Frito
11

"você introduziu uma maneira sutil de se dar um tiro no pé"

As matrizes 'nativas' C não armazenam seu tamanho. Portanto, é recomendável salvar o comprimento da matriz em uma variável / const separada e passá-lo sempre que passar na matriz, ou seja:

#define MY_ARRAY_LENGTH   15
int myArray[MY_ARRAY_LENGTH];

Você sempre deve evitar matrizes nativas (a menos que não consiga, nesse caso, cuidar do seu pé). Se você estiver escrevendo C ++, use o contêiner 'vector' do STL . "Em comparação com matrizes, elas fornecem quase o mesmo desempenho" e são muito mais úteis!

// vector is a template, the <int> means it is a vector of ints
vector<int> numbers;  

// push_back() puts a new value at the end (or back) of the vector
for (int i = 0; i < 10; i++)
    numbers.push_back(i);

// Determine the size of the array
cout << numbers.size();

Consulte: http://www.cplusplus.com/reference/stl/vector/

Ohad
fonte
Eu li que a maneira correta de declarar constantes inteiras em C é usar uma enumdeclaração.
Raffi Khatchadourian
A questão é sobre C, não C ++. Portanto, não STL.
Cacahuete Frito 23/04
11
#define SIZE_OF_ARRAY(_array) (sizeof(_array) / sizeof(_array[0]))
Andy Nugent
fonte
6
Observe que isso funciona apenas para matrizes reais, não para ponteiros que apontam para matrizes.
David Schwartz
5

Se você realmente deseja fazer isso para distribuir sua matriz, sugiro implementar uma estrutura para armazenar um ponteiro para o tipo do qual você deseja uma matriz e um número inteiro representando o tamanho da matriz. Então você pode passar isso para suas funções. Apenas atribua o valor da variável da matriz (ponteiro ao primeiro elemento) a esse ponteiro. Em seguida, você pode Array.arr[i]obter o i-ésimo elemento e usar Array.sizepara obter o número de elementos na matriz.

Eu incluí algum código para você. Não é muito útil, mas você pode estendê-lo com mais recursos. Para ser sincero, no entanto, se é isso que você deseja, deve parar de usar C e usar outro idioma com esses recursos incorporados.

/* Absolutely no one should use this...
   By the time you're done implementing it you'll wish you just passed around
   an array and size to your functions */
/* This is a static implementation. You can get a dynamic implementation and 
   cut out the array in main by using the stdlib memory allocation methods,
   but it will work much slower since it will store your array on the heap */

#include <stdio.h>
#include <string.h>
/*
#include "MyTypeArray.h"
*/
/* MyTypeArray.h 
#ifndef MYTYPE_ARRAY
#define MYTYPE_ARRAY
*/
typedef struct MyType
{
   int age;
   char name[20];
} MyType;
typedef struct MyTypeArray
{
   int size;
   MyType *arr;
} MyTypeArray;

MyType new_MyType(int age, char *name);
MyTypeArray newMyTypeArray(int size, MyType *first);
/*
#endif
End MyTypeArray.h */

/* MyTypeArray.c */
MyType new_MyType(int age, char *name)
{
   MyType d;
   d.age = age;
   strcpy(d.name, name);
   return d;
}

MyTypeArray new_MyTypeArray(int size, MyType *first)
{
   MyTypeArray d;
   d.size = size;
   d.arr = first;
   return d;
}
/* End MyTypeArray.c */


void print_MyType_names(MyTypeArray d)
{
   int i;
   for (i = 0; i < d.size; i++)
   {
      printf("Name: %s, Age: %d\n", d.arr[i].name, d.arr[i].age);
   }
}

int main()
{
   /* First create an array on the stack to store our elements in.
      Note we could create an empty array with a size instead and
      set the elements later. */
   MyType arr[] = {new_MyType(10, "Sam"), new_MyType(3, "Baxter")};
   /* Now create a "MyTypeArray" which will use the array we just
      created internally. Really it will just store the value of the pointer
      "arr". Here we are manually setting the size. You can use the sizeof
      trick here instead if you're sure it will work with your compiler. */
   MyTypeArray array = new_MyTypeArray(2, arr);
   /* MyTypeArray array = new_MyTypeArray(sizeof(arr)/sizeof(arr[0]), arr); */
   print_MyType_names(array);
   return 0;
}
Joel Dentici
fonte
1
Não é possível aprovar o código que faz strcpy(d.name, name);sem manipulação de estouro.
chux - Restabelece Monica
4

A melhor maneira é salvar essas informações, por exemplo, em uma estrutura:

typedef struct {
     int *array;
     int elements;
} list_s;

Implemente todas as funções necessárias, como criar, destruir, verificar a igualdade e tudo o mais que você precisa. É mais fácil passar como parâmetro.

Paulo Pinheiro
fonte
6
Alguma razão para int elementsvs. size_t elements?
chux - Restabelece Monica
3

A função sizeofretorna o número de bytes que é usado por sua matriz na memória. Se você deseja calcular o número de elementos em sua matriz, divida esse número com o sizeoftipo de variável da matriz. Digamos que int array[10];, se o número inteiro do tipo variável no seu computador for de 32 bits (ou 4 bytes), para obter o tamanho da sua matriz, faça o seguinte:

int array[10];
int sizeOfArray = sizeof(array)/sizeof(int);
Keivan
fonte
2

Você pode usar o &operador. Aqui está o código fonte:

#include<stdio.h>
#include<stdlib.h>
int main(){

    int a[10];

    int *p; 

    printf("%p\n", (void *)a); 
    printf("%p\n", (void *)(&a+1));
    printf("---- diff----\n");
    printf("%zu\n", sizeof(a[0]));
    printf("The size of array a is %zu\n", ((char *)(&a+1)-(char *)a)/(sizeof(a[0])));


    return 0;
};

Aqui está a saída da amostra

1549216672
1549216712
---- diff----
4
The size of array a is 10
Shih-En Chou
fonte
7
Não diminuí o voto, mas isso é como acertar um prego com um tijolo, porque você não notou um martelo ao seu lado. Além disso, as pessoas tendem a desaprovar o uso de variáveis ​​não inicializadas ... mas aqui eu acho que serve bem a seu propósito.
Dmitri
2
@Dmitri variáveis não não inicializadas são acessados aqui
MM
1
Hummm. Subtração de ponteiro leva a ptrdiff_t. sizeof()resulta em size_t. C não define qual é a classificação mais ampla ou mais alta / mesma. Portanto, o tipo de quociente ((char *)(&a+1)-(char *)a)/(sizeof(a[0]))não é certamente size_te, portanto, imprimir com zpode levar a UB. Simplesmente usar printf("The size of array a is %zu\n", sizeof a/sizeof a[0]);é suficiente.
chux - Restabelece Monica
1
(char *)(&a+1)-(char *)anão é uma constante e pode ser calculado em tempo de execução, mesmo com um tamanho fixo a[10]. sizeof(a)/sizeof(a[0])é constante feito em tempo de compilação neste caso.
chux - Restabelece Monica
1

A resposta mais simples:

#include <stdio.h>

int main(void) {

    int a[] = {2,3,4,5,4,5,6,78,9,91,435,4,5,76,7,34};//for Example only
    int size;

    size = sizeof(a)/sizeof(a[0]);//Method

    printf ("size = %d",size);
    return 0;
}
Jency
fonte
1

Uma solução mais elegante será

size_t size = sizeof(a) / sizeof(*a);
Azatik1000
fonte
0

Além das respostas já fornecidas, quero destacar um caso especial pelo uso de

sizeof(a) / sizeof (a[0])

Se aé ou um conjunto de char, unsigned charou signed charvocê não precisa usar sizeofduas vezes desde a sizeofexpressão com um operando destes tipos sempre resultam para 1.

Citação de C18,6.5.3.4 / 4:

" Quando sizeofé aplicada a um operando que tem o tipo char, unsigned charou signed char, (ou uma versão dos mesmos qualificado) o resultado é 1."

Assim, sizeof(a) / sizeof (a[0])seria equivalente a NUMBER OF ARRAY ELEMENTS / 1se aé uma matriz do tipo char, unsigned charou signed char. A divisão até 1 é redundante.

Nesse caso, você pode simplesmente abreviar e fazer:

sizeof(a)

Por exemplo:

char a[10];
size_t length = sizeof(a);

Se você quer uma prova, aqui está um link para o GodBolt .


No entanto, a divisão mantém a segurança, se o tipo mudar significativamente (embora esses casos sejam raros).

RobertS suporta Monica Cellio
fonte
1
Você provavelmente prefere ainda aplicar uma macro à divisão, porque o tipo pode mudar no futuro (embora talvez improvável), e a divisão é conhecida no momento da compilação, portanto, o compilador a otimizará (se não mudar, por favor seu compilador).
Cacahuete Frito 23/04
1
@CacahueteFrito Sim, eu também pensei nisso. Eu tomei isso como uma nota lateral na resposta. Obrigado.
RobertS apoia Monica Cellio em
-1

Nota: Este pode fornecer um comportamento indefinido, conforme apontado por MM no comentário.

int a[10];
int size = (*(&a+1)-a) ;

Para mais detalhes, veja aqui e também aqui .

Pygirl
fonte
2
Esse é um comportamento tecnicamente indefinido; o *operador não pode ser aplicado a um ponteiro passado
MM
3
"comportamento indefinido" significa que o Padrão C não define o comportamento. Se você experimentá-lo em seu programa, tudo pode acontecer.
MM
@MM você *(&a+1) - a;está dizendo que é diferente do que está (&a)[1] - a;acima, não conte *(&a+1)e (&a)[1]conte como 1 após o final?
QuentinUK
@QuentinUK suas duas expressões são iguais, x[y]é definido como*(x + (y))
MM
@ MM eu pensei que sim. Mas a outra resposta, de Arjun Sreedharan, tem 38 setas para cima e isso tem -1. E a resposta de Arjun Sreedharan não menciona comportamento indefinido.
QuentinUK