1. Си / Говнокод #17150

    +139

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    SGELISTENTRY *sgeListAdd(SGELIST *l, const char *id, void *data) {
       SGELISTENTRY *ret;
    
       sgeNew(ret, SGELISTENTRY);
       l->numberOfEntries++;
       if (l!=NULL) {
          ret->prev=l->last;
       } else {
          ret->prev=NULL;
       }
       if (l!=NULL && l->last!=NULL) {
          l->last->next=ret;
       }
       ret->next=NULL;
       ret->id=strdup(id);
       ret->data=data;
    
       if (l==NULL) return ret;
    
       if (l->first==NULL) l->first=ret;
       l->last=ret;
    
       return ret;
    }

    Эх, проверяй, не проверяй, один хрен все грохнется при l==NULL

    Запостил: Pythoner, 21 Ноября 2014

    Комментарии (54) RSS

    • На месте IDE я бы подсветил строчку 06, однозначно поняв строчку 05 как l != NULL.

      Кстати, а есть ведь наверняка для сей какой-то способ указать контракт: дескать параметр не может быть NULL, и чтоб потом статический анализ делать и ругаться на такие вещи?
      ------------
      Например для Java в Idea у менять есть аннотация @NotNull.
      Ну и для C# тоже
      Ответить
      • >На месте IDE я бы подсветил строчку 06, однозначно поняв строчку 05 как l != NULL.

        Компиляторы эту строчку так и понимают и радостно выкидывают последующие проверки нахер. Нормальные анализаторы должны на это ругаться (линт и шланг ругаются)
        Ответить
      • Да вы почти - "дип блю".
        Ответить
    • Автор хотел, чтобы при l == NULL функция запиливала новую ноду, но никуда ее не привязывала?
      Ответить
      • Oн вoбщe чeгo тo cтpaннoгo xoтeл.
        A нa caмoм тo дeлe этo нe пpocтoй cвязнoй cпиcoк, a нeкaя cyщнocть, пycть и кpивo, нo peaлизyющaя интepфeйc accoциaтивнoгo мaccивa нa eгo ocнoвe.
        Поиск ноды вобще красота:
        SGELISTENTRY *sgeListSearch(SGELIST *l, char *id) {
           SGELISTENTRY *act=l->first;
        
           while (act!=NULL) {
              if (strcmp(act->id,id)==0) return act;
              act=act->next;
           }
           return NULL;
        }
        Ответить
        • А что не так с поиском? Классика жанра же. Association list.

          P.S. Ну разве что id должно быть const char *, а не char *.
          Ответить
          • Ну переголова же.
            Я бы сделал как const int id, задефайнил где то ид ноды, и далее искал простым сравнением вместо strcmp.
            Ответить
            • Оптимизация головного мозга ;) А если id приходит из внешнего источника, и является именно строкой?

              P.S. Небольшие alist'ы не так уж и тормозят.
              Ответить
            • Поцчему вообще не взять какой-нить готовый контейнер который хранил бы всё в красно-черном дереве по хешу и поиск бы летал.
              Ответить
              • зачем 16-битному хешу КД?
                Ответить
                • Для скорости) Или Вы считаете что два байта того не стоят? А 4 байта?
                  Ответить
                  • Для скорости открой для себя обычный линейный массив)
                    Ответить
                    • И хеши использовать в качестве индексов массива?
                      Тоесть если там лежит три калеки, то он все равно будет занимать 2^16 байт?
                      Ответить
          • >>А что не так с поиском?
            А Вы всегда используете ассоциативные массивы c O(N)?
            Ответить
            • > всегда
              Хоспаде... Нет конечно. Но если мне надо распарсить какой-нибудь конфиг при старте проги, а под рукой только alist или вообще ничего - я не пойду искать/писать хешмапу/дерево только из-за того, что она быстрее.
              Ответить
              • А я вообще не буду конфиг на сях парсить)) Возьму какой-нить пайтон где всё уже есть.

                Если человек пишет на плейн сях, то скорее всего он хочет СКОРОСТИ, разве нет?

                Другой вопрос что может быть в этот лист чаще добавляют, чем пишут
                Ответить
                • > разве нет
                  Нет.
                  Ответить
                  • А зачем тогда?

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

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

                      > вручную писать ассоциативный массив
                      Изкоробочного то нет. А использование внешних либ под вопросом.
                      Ответить
                      • Помните историю с yasmом, да?
                        Так вот казалось бы сишка — сверхбыстрый язык, а пострадали от квадратичной питушни.
                        Ответить
                      • >>ядра, дрова
                        вот прямо там нету мапы готовой?

                        >>рантаймы
                        хм.. а почему не кресты с их std::map?

                        >>внешних либ
                        Ну он все равно не будет одно и тоже писать 10 раз, все равно он сделает себе либу и будет с нею линковаца (пусть и статически). Поцчему не взять готовое то?
                        Ответить
                        • >нету мапы готовой?

                          Только петушилово со свитчами.
                          Ответить
            • И вообще - это уже другой вопрос.

              Одно дело - как реализовать конкретный контейнер.
              И совсем другое - в каких случаях нужно юзать его, а не какой-то другой.

              С точки зрения реализации конкретного контейнера - тут всё ок. Мелкие косяки, не более того. Тестирование и ревью их пофиксят.
              Ответить
            • Во-первых для коротких списков будет быстрее. Нужно же понимать о чем говорится в анализе асимптотической сложности: говорится о том, что есть такое значение x для которого любые значения f(x+n), где n>=0 выполняется f > анализируемая функция. Т.е. никто не обязуется что для всех значение x f > анализируемая функция (и вполне возможно, что x после которого наступает переломный момент очень даже большой).
              По этой же причине, например, практические реализации квиксорта использую сортировку вставкой для маленьких массивов.
              Реализация ассоциативного списка без наворотов имеет все шансы обогнать хеш-таблицу на списках до 8-16 элементов.
              Ответить
              • Да, для малого количества значений лист и правда быстрее: не надо хеш считать, дерево балансировать итд
                Ответить
      • Даже если бы он хотел именно это, все равно странно - счетчик в листе синкрементили, а ноду не привязали.
        Ответить
        • Да банально забыл спрятать этот инкремент под проверку на не NULL.

          Говно тут скорее всего в отсутствии тестов.
          Ответить
    • Автор знает, что l может быть NULL, но радостно разыменовывает его в строке 05? Втф?
      Ответить
      • Это говнокод же!
        Ответить
        • Даже для говнокода слишком говно.
          Ответить
          • А вдруг это C++, инкремент у поля numberOfEntries перегружен и его применение вызывает деструктор?
            Ответить
            • > вызывает деструктор
              У NULL'а?
              Ответить
              • Ну а вдруг l становится (может стать) NULL'ом только после 05-й строчки?
                Ответить
                • Каким хуем образом?

                  Оператор ++ анализирует стек вызвавшей процедуры, отыскивает в нем переменную l и тайком помещает в нее NULL?
                  Ответить
                  • Зачем стек? Может быть в numberOfEntries хранится указатель на l, и тогда можно по адресу l положить NULL, разве нет?
                    Ответить
                    • l - локальная переменная. Откуда в l->numberOfEntries взяться ее адресу?

                      Разве что макрос sgeNew его туда положил...
                      #define sgeNew(Ret, Type) \
                          l->numberOfEntries.address = &l; \
                          Ret = malloc(sizeof(Type));
                      Ответить
                      • хм) Какой из этих пунктов неверен:
                        1) l -- указатель на структуру SGELIST
                        2) Внутри этой структуры храница указатель на какой-то numberOfEntries
                        3) Что было с инстансом этой структуры до её попадания в функцию мы не знаем
                        4) Если представить что numberOfEntries это не int, а какой-то объект то где-то наверху могло быть сказано [тут_идет_приведенный_Вами_код
                        Ответить
                        • l - локальная переменная. И ее адрес можно узнать (без извращений) только внутри этой функции. Поэтому адрес переменной l в numberOfEntries могут запрятать только строчки 2-4.

                          Так что всё верно, но "где-то наверху" ограничивается строчками 2-4 этой функции.
                          Ответить
                          • P.S. Хотя можно еще вот так:
                            #define numberOfEntries \
                                numberOfEntries; \
                                if (!l) l = malloc(sizeof(SGELIST));
                                l->numberOfEntries
                            Ответить
                          • Или даже так:
                            #define l \
                                (l ? l : l = malloc(sizeof(SGELIST)))
                            Ответить
                          • Я понял что я не понял: *l же передается по значению, создается на стеке в момент вызова функции, и что там было до этого уже не важно.

                            Как вредно писать неподумав:-.
                            Ответить
            • Деструктор на нулевом указателе тоже вызывать не стоит.
              Ответить
            • Блин, да не фантазируйте. Сишка там, pure C.
              http://sourceforge.net/projects/sge2d/
              Ответить
            • А может быть там вот так написано:
              #define sgeNew(Ret, Type) \
                  if (!l) l = malloc(sizeof(SGELIST)); \
                  Ret = malloc(sizeof(Type));
              Тогда код не будет крашиться.
              Ответить
              • Копипаст:
                #define sgeBailOut(format, args...) do {\
                		fprintf(stderr,(format),args); \
                		exit(-1); \
                	} while (0) 
                ...
                #define sgeMalloc(target,type,amount) do {\
                		(target)=(type *)malloc((amount+1)*sizeof(type));\
                		if ((target)==NULL) {\
                			sgeBailOut("could not allocate %d bytes of ram\n", (int)((amount)*sizeof(type)));\
                		}\
                		memset(target,0,((amount)*sizeof(type)));\
                	} while (0) 
                ...
                #define sgeNew(target,type) sgeMalloc(target,type,1);
                Ответить
                • Жуть какая-то: на каждое выделение памяти вкомпиливается эта хрень с проверкой, printf'ом и занулением... Неужели нельзя было вынести в функцию?
                  #define SGE_MALLOC_ARRAY(Type, Amount)
                      sgeMalloc(sizeof(Type) * (Amount))
                  #define SGE_MALLOC(Type)
                      sgeMalloc(sizeof(Type))
                  
                  inline void * sgeMallocImpl(size_t size) {
                      void *p = malloc(size);
                      if (!p)
                          sgeBailOut("could not allocate %d bytes of ram\n", (int)((amount)*sizeof(type)));
                      memset(p, 0, size);
                      return p;
                  }
                  Ответить
                  • Ну да, это достойно отдельного треда на ГК.
                    Ответить
                • P.S. За макросы без капса в именах надо сажать на кол.
                  Ответить
                • P.P.S. А еще оно выделяет на 1 элемент больше чем надо. Стратегический запас на случай переполнения буферов? :)
                  Ответить
    • Вот еще - реаллок на каждый чих:
      void sgeArrayAdd(SGEARRAY *a, void *e) {
      	a->numberOfElements++;
      	sgeRealloc(a->element, void *, a->numberOfElements);
      	a->element[a->numberOfElements-1]=e;
      }
      
      void sgeArrayInsert(SGEARRAY *a, Uint32 offset, void *e) {
      	if (offset>=a->numberOfElements) {
      		sgeArrayAdd(a, e);
      		return;
      	}
      
      	sgeRealloc(a->element, void *, ++a->numberOfElements);
      	memmove(a->element+offset+1,a->element+offset,(a->numberOfElements-offset-1)*sizeof(void *));
      	a->element[offset]=e;
      }

      Тоже производительности добавляет.
      Ответить

    Добавить комментарий