# статические/динамические массивы нулевой длины

Тема в разделе "LANGS.C", создана пользователем kaspersky, 6 сен 2007.

  1. kaspersky

    kaspersky New Member

    Публикаций:
    0
    Регистрация:
    18 май 2004
    Сообщения:
    3.006
    скажите, кто ни будь экспериментировал со статическими/динамическими массивами нулевой длинны? (char zero[0], malloc(0), new int[0]). сравнивал ли результат на разных компиляторах? сверялся ли со стандартом? ИМХО тут до фига всего интересного ;) я вот тут как раз рою в этом направлении и уже многое нарыл ;)
     
  2. Mental_Mirror

    Mental_Mirror New Member

    Публикаций:
    0
    Регистрация:
    7 май 2007
    Сообщения:
    431
    Интересный способ онанизма.
     
  3. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    По стандарту они запрещены и точка. Разве что пособирать камни для пуляния в огороды разработчиков компиляторов.
     
  4. kaspersky

    kaspersky New Member

    Публикаций:
    0
    Регистрация:
    18 май 2004
    Сообщения:
    3.006
    IceStudent
    стандарты иногда полезно читать, а не иметь свое мнение о них.
    section r.5.3.3 of the C++ Programming Language 2e, Stroustrup writes:
    "This implies that an operator new() can be called with the argument
    zero. In this case, a pointer to an object is returned. Repeated such
    calls return pointers to distinct objects."

    описание malloc из MSDN:
    "If size is 0, malloc allocates a zero-length item in the heap and returns a valid pointer to that item"

    а вот как создать char c[0] - этоуже без травы не обойтись ;))

    Mental_Mirror
    хорошо, типичная ситуация #define DATA_LEN, где DATA_LEN может меняться от нуля до... многодетной матери. если DATA_LEN == 0, значит, "нет данных", а теперь вообразим, что у нас есть код вида:

    foo()
    {
    char xxx[DATA_LEN];
    }

    ну и что мы будем делать?! громоздкие директивы препроцессора условной компиляции не предлагать ;)
     
  5. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Понятие "массивы нулевой длины" уже само по себе некорректно.

    Просто надо помнить, что char *arr[0] - это нечто, условно равное массиву нулевой длины (0! тоже условно равен 1, хотя реально факториал нуля никак не может быть равен 1).
    Если рассматривать arr[0] абстрактно, можно прийти к каким угодно интересным умозаключениям, в т.ч. и бредовым :)

    Чтобы получить реальный массив нулевого размера, нужно вытащить все планки озу, вот только как после этого сделать char *arr[0]? На бумажке написать?

    А любой другой массив (со вставленными в слот планками озу), в т.ч. и "нулевой" длины имеет размер [от указателя до максимально возможного адреса озу]

    Если рассматривать эти "парадоксы" в правильной системе координат, то они умрут сами собой, и говорить будет не о чем...

    Другое дело интересоваться вопросом, почему компилятор позволяет сделать
    char *arr = new char[0];
    strcpy (arr, "abcdef");
    ?
     
  6. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    kaspersky
    Я о массиве нулевой длины. Про игры с malloc/new не читал. Но порадовала фраза
    Это как? Как можно выделить память размером в 0 байт? Точнее, не так. Как можно выделить память под нечто, имеющее размер 0 байт?

    Если массив, например, в библиотеке, а за размер отвечает её пользователь — то он сам себе злобный буратино, определяя буфера нулевой длины. И компилятор ему на это укажет. Если же этот вариант предусмотрен разработчиком библиотеки, то нужно либо устанавливать инвариант DATA_LEN > 0 средствами препроцессора, либо полностью убирать код, работающий с этим массивом, если под нулевым размером имелось ввиду, что буфер не нужен вообще (как и то, для чего он предусматривался).
     
  7. kaspersky

    kaspersky New Member

    Публикаций:
    0
    Регистрация:
    18 май 2004
    Сообщения:
    3.006
    IceStudent
    в gcc реализация new вообще-то такая:

    Код (Text):
    1. void* operator new(size_t size) // реализация оператора new
    2. {
    3.     // если size равно нулю, принудительно устанавливаем размер в единицу
    4.     if( size == 0 ) size = 1;
    5.     :
    6.     // продолжение функции
    7.     :
    8. }
    malloc _обычно_ выделяет блоки памяти не менее 10h байт, поэтому malloc(0) в 99% равностильно malloc(1), хотя закладываться на это нельзя.

    char x[0] на стеке на 32-компиляторах резервирует 4 байта и 1 байт в секции данных. главное, как его _по_ _стандарту_ положить на стек, чтобы это было совместимо со всеми компиляторами. вот это и есть главная хитрость ;)

    убрать препроцессором буфер с нулевой длинной можно, но... в том-то и дело, что убрать ссылки на него из кода нельзя ;( например, функция write с аргументом 0 запишет ноль байт, то есть есть нужен только указатель на массив, и этот указатель стандарт получать разрешает.

    я пользовался этим, когда пришлось рихтовать одну программу... (не свою, чужую).
    там короче сначала были две опции DATA_LEN и DATA_ENABLED ну и...
    короче я решил выкинуть опцию DATA_ENABLED на фиг, т.к. при DATA_LEN == 0, мы получаем DATA_DISABLED (в смысле DATA_ENABLED = 0)

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

    зато получилось запихать char x[0] в структуру и все заработало. а вот sizeof(структуры) меня очень позабавило на разных компиляторах результат очень разный ;)
     
  8. r90

    r90 New Member

    Публикаций:
    0
    Регистрация:
    26 ноя 2005
    Сообщения:
    898
    этт досадно... я по-жизни gcc пользую и использовать char[0] -- как замечательный способ объявлять структуры переменного размера, чтобы к ним ещё и sizeof был бы применим. ну, типа:
    Код (Text):
    1. struct my_string
    2. {
    3.      int size;
    4.      char str[0];
    5. };
    6.  
    7. /*...*/
    8. struct my_string *str = malloc (sizeof (*str) + strlen ("hello") + 1);
    9. strcpy (str->str, "hello");
    10. /*...*/
     
  9. Smile

    Smile New Member

    Публикаций:
    0
    Регистрация:
    28 июл 2004
    Сообщения:
    129
    r90

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

    // Строка нулевой длины
    static struct my_string str = {0, '\x0'};

    Вообще, считаю, лучше использовать Type name[1] для переменного размера, меньше неоднозначностей и понятнее :)
     
  10. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    kaspersky
    Так структура = класс (если в С++), а в С++ размер класса всегда > 0. А уж какой в действительности размер — это уже на усмотрение компилятора/его разработчиков.

    Smile
    Согласен.
     
  11. AsmGuru62

    AsmGuru62 Member

    Публикаций:
    0
    Регистрация:
    12 сен 2002
    Сообщения:
    689
    Адрес:
    Toronto
    "-- Мы сами знаем, что она не имеет решения, -- сказал Хунта,
    немедленно ощетиниваясь. -- Мы хотим знать, как ее решать.
    -- К-как-то ты странно рассуждаешь, К-кристо... К-как же искать
    решение, к-когда его нет? Б-бессмыслица какая-то...
    -- Извини, Теодор, но это ты очень странно рассуждаешь. Бессмыслица
    - искать решение, если оно и так есть. Речь идет о том, как поступать с
    задачей, которая решения не имеет. Это глубоко принципиальный вопрос,
    который, как я вижу, тебе, прикладнику, к сожалению, не доступен."

    Аркадий и Борис Стругацкие.
    "Понедельник начинается в субботу"
     
  12. kaspersky

    kaspersky New Member

    Публикаций:
    0
    Регистрация:
    18 май 2004
    Сообщения:
    3.006
    IceStudent
    > Так структура = класс (если в С++), а в С++ размер класса всегда > 0.
    > А уж какой в действительности размер
    > это уже на усмотрение компилятора/его разработчиков.
    GCC реально резервирует 4 байта на стеке и 1 байт в секции данных,
    но sizeof возвращает ноль и не нужно гооврить про надуманность задачи.
    тем более, что стандарты это описывают достаточно четко.
    я же не собираюсь обращаться к массиву нулевого размера,
    мне просто достаточно создать его и иметь возможность получить
    на него валидный указатель ;)
     
  13. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Интересно, как?

    А по поводу #10
    Код (Text):
    1. class C {};
    2.  
    3. int main()
    4. {
    5.  char buf[sizeof(C)];
    6.  return 0;
    7. }
     
  14. letopisec

    letopisec New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2004
    Сообщения:
    228
    Это нечто не имеет размер 0 байт. Создается некоторый обьект (item) в памяти (не нулевой длины), который должен иметь поле содержащее количество байт выделенных malloc() и поле содержащее значение указателя на эти байты, чтобы потом при вызове free(), последняя знала сколько памяти освободить. Видимо об этом item речь и идет.
     
  15. crypto

    crypto Active Member

    Публикаций:
    0
    Регистрация:
    13 дек 2005
    Сообщения:
    2.533
    kaspersky
    В Билдере перед выделением памяти размер блока округляется в большую сторону до 64К:
    Код (Text):
    1. /*---------------------------------------------------------------------*
    2. Name            _getmem - allocate new memory for the heap
    3. Usage           int _getmem(size_t size);
    4. Prototype in    _malloc.h
    5.  
    6. Description     This function allocates a new memory block from OS/2,
    7.                 then adds it to the heap.  The allocation is performed
    8.                 by committing pages that have been previously allocated
    9.                 from the virtual address space but not committed.
    10.                 The block size is rounded up to the next multiple of
    11.                 64K.  Virtual address space is allocated in multiples
    12.                 of 4 Meg, and committed in multiples of 64K.
    13.  
    14. Return value    If the memory can be succesfully allocated and added
    15.                 to the heap, 0 is returned; otherwise -1.
    16. *---------------------------------------------------------------------*/
    17. int _getmem(size_t size)
    18. {
    19.     void *p;
    20.     unsigned long rem, vsize;
    21.  
    22.     /* Round up size to next multiple of 64K.
    23.      */
    24.     size = (size_t)ROUND_UP(size,SEG_SIZE);
    25.     ...
     
  16. wworld

    wworld New Member

    Публикаций:
    0
    Регистрация:
    16 фев 2007
    Сообщения:
    6
    как мне кажется то объявление char a[0] будет равносильно char *a;
     
  17. letopisec

    letopisec New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2004
    Сообщения:
    228
    visual studio 2005

    Код (Text):
    1. int main(int argc, char* argv[])
    2. {
    3.     char a[0];
    4. }
    5.  
    6. Error   1   error C2466: cannot allocate an array of constant size 0    c:\temp\test\test\test.cpp  27
     
  18. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    мелкомягкие в таких случаях предпочитают объявлять [1]
    дада)
     
  19. crypto

    crypto Active Member

    Публикаций:
    0
    Регистрация:
    13 дек 2005
    Сообщения:
    2.533
    Great
    А почему просто не
    char str[]
    если поле в конце структуры.
     
  20. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    crypto
    VC 8.0 C4200, Comeau - "incomplete type is not allowed char buf[];"

    обновлено

    Правда, под С++. Так что ответом на "а почему просто не..." может быть совместимость с С++ на уровне исходного кода. Так же, как и "typedef struct _mystruct {} mystruct;" является совместимостью с Си.