2016-05-05 3 views
0

я столкнулся с проблемой, проверяя code.I определить макрос для получения количества элементов массива следующим образом:Как подсчитать количество элементов массива

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

Этот макрос штраф для подсчета количества элементов массива, инициализаторы соответствует емкости (например, int buf[] = {1,2,3};), но не очень эффективно с массивами объявленных как: int buf[20] = {1,2,3};

Теперь я знаю подсчета элементов массива, как это довольно легко, но как насчет большое количество элементов? Как вы их считаете? подсчет может быть убийцей, вы знаете!

Рассмотрим следующий код:

#include <stdio.h> 
#include <string.h> 

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

void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize); 

int main(void) 
{ 
    int dst[20] = { 1,2,3 }; 
    int src[] = { 4,5,6 }; 

    size_t dstSize = 3; // dstSize = ARRAY_SIZE(dst) doesn't work 
    size_t srcSize = ARRAY_SIZE(src); 

    g_strcat(dst, dstSize, sizeof(int), src, srcSize); 

    size_t n, newSize = dstSize + srcSize; 
    for (n = 0; n < newSize; n++) { 
     printf("%d ", dst[n]); 
    } 
    putchar('\n'); 
    return 0; 
} 

void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize) 
{ 
    memcpy((char *)_Dst + (dstSize * bytes), _Src, srcSize * bytes); 
} 
+0

Вы хотите рассчитать размер списка инициализаторов вместо размера массива? – user3528438

+0

Да, вот что я имел в виду –

+0

Итак, сколько элементов вы ожидаете 'buf [20] = {1,2,3};' иметь? 3 или 20? – Soren

ответ

5

Если вы только частично инициализируете список примитивных типов данных (то есть: массив из int s), остальные элементы инициализируются до 0.

С99 Стандартного 6.7.8.21

Если есть меньше инициализаторов в распорной корпусе списка, чем есть элементы или члены совокупности, или меньше символов в строки буквального, используемые для инициализации массива Известный размер, чем есть, - это элементы в массиве, остальная часть совокупности должна быть , неявно инициализированной так же, как объекты, имеющие статическое хранилище .

В вашем случае вы пытаетесь определить размер списка инициализаторов. Не могу по-настоящему думать о действительной причине, чтобы это сделать, но вы можете просто проверить, когда элементы начинают равняться нулю. Конечно, это не удается, если вы намеренно установили элемент в ноль.

Макрос Вы писали будет работать правильно (т.е. возвращает количество элементов в массиве), но это не сработает, если вы используете его в функцию, которая принимает указатель на массив в качестве аргумента, as pointer decay causes sizeof to act differently than one might expect.

Все, как говорится, вы не можете определить размер списка инициализаторов в любом значимом смысле, если вы делаете что-то вроде так, где вы определяете список инициализатора как макрос:


листинг


#include <stdio.h> 
#define LIST {1,2,3} 

int main(void) 
{ 
    int i[20] = LIST; 
    int t[] = LIST; 

    printf("elements in i: %d\n", sizeof(i)/sizeof(int)); 
    printf("elements in t: %d\n", sizeof(t)/sizeof(int)); 

    return 0; 
} 

Пример вывода


elements in i: 20 
elements in t: 3 

Вы можете минимизировать впустую память, поместив одноразовая массив в новый блок сферы, а именно:


#include <stdio.h> 
#define LIST {1,2,3} 

int main(void) 
{ 
    int i[20] = LIST; 
    int initListSize = 0; 

    { 
     int t[] = LIST; 
     initListSize = sizeof(t)/sizeof(int); 
    } 

    printf("elements in t: %d\n", initListSize); 

    return 0; 
} 

Это ограничит срок хранения временного массива узким пространством между фигурными скобками. Опять же, я вижу, что это полезно, возможно, в качестве эксперимента, но не может видеть, что это когда-либо попадает в производственный код.

+0

s/pointer-to-array/указатель на элемент массива /, указатель на массив имеет тип массива (хотя я думаю, что компиляторы C позволяют назначать указатели на массивы разной длины.) – juanchopanza

+0

@juanchopanza Вы правы. Я просто использовал «указатель на массив», а не углублялся в детали, поскольку я не хотел путать OP или добавлять несколько абзацев к моему (уже длинному) сообщению. Вхождение в нюансы указателя к типу/указателю на массив и т. Д. - это длинный ответ. Спасибо, что указали на это, так как это, вероятно, поможет OP, если он это прочитает. – DevNull

+0

Отличный ответ. Хотя я клянусь, я нашел решение в своем ответе, прежде чем читать ваши! –

1

Ваш макрос работает отлично. Заявление:

int dst[20] = { 1,2,3 }; 

Создает следующие в стеке памяти:

|1|2|3|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 

Размер массива по-прежнему 20, даже если он только был инициализирован с первыми тремя значениями

Что касается вашего вопроса в комментариях: Как объединить массив? Массив должен быть достаточно, чтобы держать другую строку большим:

Если вы работаете со строками (а не количество массивов) функция строки int len = strlen(string); может быть использована для проверки существующего использования переменной строки перед тем конкатенации.

В отличие от sizeof макро, strlen это функция, которая ищет первый символ NULL в массиве символов:

char string[20] = {"yes"}; 

Создает следующие в памяти:

|y|e|s|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0| 

strlen начинается в адрес string и подсчитывает символы, пока не встретит первый NULL. (Или 0):

int len strlen(string); //will yield a len of 3 

вы можете использовать strncat(string, newStr, count); конкатенировать ЗАДАННОМУ количество символов, количество, в строку известной емкости, что позволяет избежать переполнения буфера.

иметь в виду, что строка переменная размера == 20, например, ограничен, содержащий строку длины == 19. 20-й позиции должны быть зарезервированы для символа NULL.

1

Это будет делать, если мы можем предположить, что последний инициализирован элемент не равен нулю (потому что неотличимы от неявным на ноль):

size_t trailing_zero_bytes(const void* data, size_t size) { 
    for (; size > 0; size--) { 
     if (((const char*)data)[size - 1] != 0) { 
      break; 
     } 
    } 
    return size; 
} 

#define ARRAY_SIZE(arr) \ 
    ((sizeof(arr) - trailing_zero_bytes(arr, sizeof(arr)) + sizeof(arr[0]) + 1)/sizeof(arr[0])) 

Если вы хотите считать эти два случая по-разному, вы «вновь полностью повезло (если вы не разобрать код, используя Clang или GCC-XML или любой другой):

int s1[5] = { 4,5,6 }; // 2 zeros implied 
int s2[5] = { 4,5,6,0 }; // 1 zero implied 

Оба выше даст 3 с моим подходом, и нет ничего, что может быть сделано об этом ,

0

Если вы хотите, чтобы определить количество инициализаторов массива, объявить массив для хранения элементов Инициализаторов и применить макрос ARRAY_SIZE() на it.Then использовать memcpy() скопировать инициализаторы в dst[20] массив.

Теперь у вас есть количество элементов без хлопот.

#include <stdio.h> 
#include <string.h> 

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

void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize); 

int main(void) 
{ 
    int dst[20], src[] = { 4,5,6 }; 
    int initializer_list[] = { 1,2,3 }; 

    size_t init_size = ARRAY_SIZE(initializer_list); 
    memcpy(dst, initializer_list, init_size * sizeof(int)); 

    size_t dstSize = init_size; 
    size_t srcSize = ARRAY_SIZE(src); 

    g_strcat(dst, dstSize, sizeof(int), src, srcSize); 
    dstSize += srcSize; 

    size_t n; 
    for (n = 0; n < dstSize; n++) { 
     printf("%d ", dst[n]); 
    } 
    putchar('\n'); 
    return 0; 
} 

void g_strcat(void *_Dst, size_t dstSize, size_t bytes, const void *_Src, size_t srcSize) 
{ 
    memcpy((char *)_Dst + (dstSize * bytes), _Src, srcSize * bytes); 
}