LINUX.ORG.RU

Использование «стража» вместо sizeof на массивах

 ,


1

2

Здравствуйте.

Читаю «Programming In Lua». В главе про C API автор постоянно заканчивает массивы структур «стражем» - структурой с нулевыми полями. Видимо для того, чтобы в цикле можно было проверить, что структура последняя.

Мне не понятно, почему используется такой «изобретательный» подход вместо sizeof?

      struct ColorTable {
        char *name;
        unsigned char red, green, blue;
      } colortable[] = {
        {"WHITE",   MAX_COLOR, MAX_COLOR, MAX_COLOR},
        {"RED",     MAX_COLOR,         0,         0},
        {"GREEN",           0, MAX_COLOR,         0},
        {"BLUE",            0,         0, MAX_COLOR},
        other colors
        {NULL, 0, 0, 0}  /* sentinel */
      };
★★★★★

Последнее исправление: makoven (всего исправлений: 1)

Вероятно, чтобы иметь возможность передавать только указатель на массив, без его размера в паре. getopt_long тоже так массив принимает.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

передавать только указатель на массив, без его размера в паре

а потом раз за разом крутить цикл по всем элементам

anonymous
()
Ответ на: комментарий от xaizek

Да, наверное поэтому. Как-то я об этом не подумал

makoven ★★★★★
() автор топика

Удобно же: в цикле на !NULL проверяешь — и все! А размер пришлось бы в структуре хранить, т.к. как ты динамический массив sizeof'ом?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

Короче, эдакий аналог null-terminated строк, который приходится использовать за неимением оператора length

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

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

next_time ★★★★★
()
Ответ на: комментарий от makoven

который приходится использовать за неимением оператора length

такой оператор в С легко реализуется, просто этот самый оператор потребует хранить (как и в любом другом ЯП) размер массива, а это накладно. однако, неясно, зачем так делать вообще на всех массивах структур: в приведённом примере, проще, как раз Length передавать.

next_time ★★★★★
()
Ответ на: комментарий от next_time

Все равно ведь меньше телодвижений. В данном случае обрабатываем так:

int fn(struct ColorTable *ptr){
    if(!ptr) return E_EMPTY;
    for(; ptr->name; ptr++){
        ... // делаем чавой-то
    }
    return 0;
}
А если определить эту структуру как
struct ColorTable {
    size_t size;   
    char *name;
    unsigned char red, green, blue;
}
то обрабатывать придется так:

int fn(struct ColorTable *ptr){
    if(!ptr) return E_EMPTY;
    size_t i, L = ptr->size;
    for(i = 0; i < L; i++, ptr++){
        ... // делаем чавой-то
    }
    return 0;
}

Ну и каждый раз при модификации придется не забывать этот size обновлять.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от makoven

за неимением оператора length

Напиши свой.

Deleted
()
Ответ на: комментарий от makoven

Да. Со строками ведь та же история: скажем, выделил ты буфер в килобайт под строчку. И, допустим, потихоньку читаешь в нее кусочками по 1-100 байт. В итоге каждый раз помимо memcpy тебе еще надо будет обновить length. Но в некоторых случаях действительно имеет смысл строки реализовать так:

typedef struct{
    char *text;
    size_t length;
} string;
...
    string some_str;
    some_str.text = strdup(argv[1]);
    some_str.length = strlen(some_str.text);
...
Оно будет удобно тем, что ты сможешь в обычные функции, которые char* хотят, передавать указатель на свою структуру:
printf("argv[1] = %s, its length is %zd\n", (char*)&some_str, some_str.length)
вместо явного указания
printf("argv[1] = %s, its length is %zd\n", some_str.text, some_str.length)
А в своих обертках, где часто нужно вычислять strlen, будешь пользоваться и полем length.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от next_time

однако, неясно, зачем так делать вообще на всех массивах структур:

Видимо, автор решил, что проще все массивы записывать одним стилем, чтобы не путаться

makoven ★★★★★
() автор топика
Ответ на: комментарий от makoven

Просто вижу впервые)

На самом деле очень распространенный подход, конформный со строками заканчивающимися на '\0'. Да и проще гораздо. Сам сравни:

size_t nelem = sizeof(colortable) / sizeof(colortable[0]);
int i;

for (i = 0; i < nelem; i++) {
        puts(colortable[i].name);
}
struct ColorTable *cp;

for (cp = colortable; cp->name != NULL; cp++) {
        puts(cp->name);
}

При этом, как уже сказали выше, первый подход работает только со «статическими» массивами, а второй как со «статикой», так и с «динамикой» (malloc).

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 3)
Ответ на: комментарий от beastie
#define SIZEOF(X) sizeof(X)/sizeof(X[0])

int i; 
for (i = 0; i < SIZEOF(colortable); i++) {
        puts(colortable[i].name);
}

вообще-то. причём, SIZEOF определён один раз и навсегда.

next_time ★★★★★
()
Последнее исправление: next_time (всего исправлений: 1)
Ответ на: комментарий от Eddy_Em

Ну и каждый раз при модификации придется не забывать этот size обновлять.

size всё равно придётся обновлять: записал на место нулевой структуры что-то - будь добр дописать в конец массива нулевую структуру опять. причём опять получаем бОльшие накладные расходы.

next_time ★★★★★
()
Ответ на: комментарий от makoven

обычный подход для избавления от глобальных переменных, функций на Over9000 строк, и прочей мерзости.

Проблема в том, что при передаче массива в функцию, массив оборачивается указателем, с которого sizeof() не сделать. И что делать?

1. не использовать функции, юзать массив от и до. Говнокод.

2. передавать размер. Хороший подход, но чреват ошибками(можно ошибиться в размере)

3. использовать «стража». Это работает, но к сожалению только если есть некое «особое» значение, например 0, или NaN (для чисел).

ЗЫЖ как бонус, многие алгоритмы ускоряются.

for(j=0; j<sz; j++)
{
  // обработка a[j]
}

// vs

for(p = a; *p; p++)
{
  // обработка *p
}

ускорение тут во первых из-за отсутствия лишней переменной j(работа с *p подразумевается), во вторых из-за упрощения адресации, ведь a[j] == *(a +j*sizeof(*a)).

emulek
()
Ответ на: комментарий от next_time

#define SIZEOF(X) sizeof(X)/sizeof(X[0])

тебе так сложно немного лишних букв написать? Жалко 5и секунд? Эти пять секунд сэкономят тебя 5 недель на сопровождении.

emulek
()
Ответ на: комментарий от emulek

отнюдь. SIZEOF один раз пишется, а используется всю жизнь. в итоге повышается и читабельность кода, и скорость печати.

next_time ★★★★★
()
Ответ на: комментарий от next_time

Во первых, ты забыл скобки. ;)

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

А во вторых, ты вычисляешь размер при каждой итерации for.

И ещё раз: этот подход не работает с динамически выделенными массивами.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от emulek

причём читается не только мной: большинство сишников используют независимо друг от друга этот макрос, причём, с тем же или крайне похожим названием (SIZE_OF видел как вариант)

next_time ★★★★★
()
Ответ на: комментарий от next_time

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

Тебе решать…

emulek
()
Ответ на: комментарий от beastie

А во вторых, ты вычисляешь размер при каждой итерации for.

нет. Любой вменяемый компилятор вычислит это константное выражение во время компиляции.

emulek
()
Ответ на: комментарий от beastie

да-да, скобки (хотя, как раз для SIZEOF не критично, ну да лан). я же говорю: один раз написал, а потом уже и забыл.

А во вторых, ты вычисляешь размер при каждой итерации for.

нет, потому, что sizeof вычисляется на этапе компиляции, это константа.

next_time ★★★★★
()
Ответ на: комментарий от next_time

причём читается не только мной

удивил. Я тоже говнокодер. И имя нам — легион. Вот только некоторые стараются не быть говнокодерами, а стать нормальными кодерами, а другим и так неплохо.

emulek
()
Ответ на: комментарий от next_time

повышается и читабельность кода

Получается SIZEOF, делающий совсем не то, что ожидают от sizeof

anonymous
()
Ответ на: комментарий от next_time

Хоть бы он SiZeOf писался, всё равно он состоит ровно из тех же букв в том же порядке. Кроме тебя нет дураков, которые будут ожидать от комманды cp копирования файла, а от CP - форматирования диска

anonymous
()
Ответ на: комментарий от next_time

SIZEOF(something) читается и отлаживается лучше sizeof(something)/sizeof(something[0]).

при программировании нужно и так Over9000 вещей помнить, нахрена свою память загаживать лишним? SIZEOF, SIZE_OF… Откуда я знаю, что это говно делает в твоём коде, и не забыл-ли ты опять скобки?

emulek
()
Ответ на: комментарий от emulek

и не забыл-ли ты опять скобки?

Ха!

makoven ★★★★★
() автор топика
Ответ на: комментарий от emulek

и не забыл-ли ты опять скобки?

хоть бы и забыл, то что? пусть будет даже

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

чем грозит?

Откуда я знаю, что это говно делает в твоём коде

RM click -> find declaration of ... -> profit!

next_time ★★★★★
()
Ответ на: комментарий от unt1tled

ну если так судить, то конечно. у разрабов плюсов видать Си ГМ поголовно...

next_time ★★★★★
()
Ответ на: комментарий от emulek

обоснуй

beastie сишник, но не плюсовик, насколько я мог судить по его сообщениям ранее.

спроси любого психа — он скажет «а я как раз — нет».

Qt-шник с Си ГМ == /0

next_time ★★★★★
()
Последнее исправление: next_time (всего исправлений: 1)
Ответ на: комментарий от next_time

чем грозит?

типичная логика говнокодера.

RM click -> find declaration of ... -> profit!

удобочитаемость по next_time'овски.

emulek
()
Ответ на: комментарий от next_time

beastie сишник, но не плюсовик

а если сишник и плюсовик?

Qt-шник с Си ГМ == /0

кутья межушного ганглия?

emulek
()
Ответ на: комментарий от next_time

а ещё у разрабов Qt тоже видать Си ГМ...

и у дворника Мамеда тоже…

emulek
()

Дал.Дейкстра.Хоор. Структурное программирование.

часть2 О структурной организации данных.

2.8 Последовательность.

ps. последовательность ширше массива понятия.

qulinxao ★★☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.