1. C++ / Говнокод #9576

    +1006

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    7. 7
    if(mHyperlinks.size()>0)
    	{
    		for(size_t i=0;i<mHyperlinks.size();i++)
    		{
    			//some code
    		}
    	}

    Запостил: lifemaker, 01 Марта 2012

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

    • Благодаря условию в первой строке избегаем инициализации переменной i при size<=0.
      Ответить
      • Внимательно посмотрите на цикл
        Ответить
        • Расскажите мне поподробнее. А я вам поведаю про свой сарказм в ответ.
          Ответить
          • Окей. Условие здесь не нужно. Ведайте про сарказм.
            Ответить
            • Рассмотрим отличия в поведении исходного кода (вариант А) и кода, с убранной первой строкой (вариант Б).
              Сразу отсекаем случай size<0, как невозможный при использовании правильно реализованного контейнера.
              Остаются 2 случая: size==0 и size>0.
              И вот, при size==0 в варианте Б, переменная i инициализируется нулем, вызывается метод size() и сравнение "<".
              В варианте А при size==0, вызывается метод size() и сравнение ">".
              Т.е. вся разница в отсутствии инициализации переменной i, о чем я и сказал в первом комментарии.
              http://govnokod.ru/9576#comment131647
              Ответить
              • А нафиг избегать инициализации int'а? Если уж и заниматься мелочной оптимизацией, то на вызове mHyperlinks.size() и проверке мы больше потеряем, даже если оно и инлановое.
                Ответить
                • Вот! А теперь вспомните про сарказм :)
                  Ответить
                  • Ну я так предполагаю, что причина данного кода - отнюдь не в оптимизации.
                    А вообще у меня с юмором сегодня не очень :)
                    Ответить
                    • Россия на пороге гражданской войны выборов президента. Понимаю.
                      Ответить
                      • Нее, я тут просто сижу и открываю для себя новое в Obj-C..
                        Например, что (int)-1 > (unsigned int)1...
                        Ответить
              • typedef unsigned long		__darwin_size_t;	/* sizeof() */
                typedef __darwin_size_t		size_t;

                Ладно, ансайнед лонга
                Ответить
          • http://farm4.static.flickr.com/3300/3419672929_b08da43abf_o.jpg
            Ответить
      • Для таких случаев предусмотрен метод empty().
        Ответить
        • Т.е. вы думаете, если заменить первую строку на empty() будет порядок? :)
          Ответить
          • Если ещё третью строку заменить на for_each, то будет порядок :)
            Ответить
        • Мне иногда на этом ресурсе хочется спросить: "Вы серьёзно так думаете? Оо"
          Ответить
          • Разрабы STL рекомендуют проверять контейнер на пустоту методом empty(), нежели сравнивая size() с нулём.
            Ответить
            • ещё бы: для некоторых контейнеров size() может быть операцией O(n), а empty() вроде всегда O(1)
              Ответить
            • Фэйспалм. Там проверка вообще не нужна.
              Ответить
              • Угу. Что-то я разумом помутился, сам же ведь так не делаю.
                Ответить
          • и не только вам :)
            Ответить
    • и размер зря берется снова
      Ответить
    • Ну тут, во-первых, возмущает использование типа 'size_t' для итерирования по размеру контейнера (не говоря даже о множественном вызове метода 'size()'). Более-менее оправдано использование этого типа только для итерирования по размерам массивов (векторов). Из кода не видно, вектор ли 'mHyperlinks' или нет. Если вдруг 'mHyperlinks' является 'std::list' или 'std::deque', то за 'size_t' надо сразу бить по рукам.

      В любом случае, если 'mHyperlinks' - стандартный контейнер типа 'T', то пользоваться надо было бы 'T::size_type', а не 'size_t'.

      А во-вторых, 'if', возможно, был заведен потому, что перед и/или после цикла мог идти еще какой-то условный код. Может его еще не написали или уже выкосили.
      Ответить
      • >Если вдруг 'mHyperlinks' является 'std::list' или 'std::deque', то за 'size_t' надо сразу бить по рукам.
        Если не вспоминать второй абзац, то почему?
        Ответить
        • В смысле "почему"? 'size_t' - это тип, который описывает количество байтов в непрерывном С/С++ объекте (т.е. результат 'sizeof'). Это - его прямое назначение. По этой причине тип 'size_t' заведомо достаточен для индексации любого массива, ибо массив в С/С++ сам по себе является непрерывным объектом.

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

          Классический пример: 16-тибитные платформы с сегментированной моделью памяти, где диапазон 'size_t' ограничивался 65535, в то время как количество элементов в списке запросто могло превышать это значение.

          Собственно, вопрос тут даже не в том, достаточен ли диапазон 'size_t', а в чисто концептуальной ошибке. Тип 'size_t' предназначен для предсталения размера объекта, а нам нужен тип, предназначенный для подсчета количества отельных объектов. "Размер объекта" и "количество объектов" - это совершенно разные ортогональные концепции. Не надо их путать.

          Для массивов между этими концепциями существует паразитная взяимосвязь (ибо массив - сам по себе объект), поэтому для индексации и подсчета массивов 'size_t' еще кое-как применим (хотя и тут кривизна прослеживается). А как только речь заходит о других типов контейнеров, о 'size_t' речи быть не может в приниципе.

          Если вам нужен целый беззнаковый тип, который достаточен для подсчета размера любого контейнера в пямяти, то это будет 'uintptr_t', но никак не 'size_t'.
          Ответить
          • Ясно, согласен.
            Ответить
          • С каждым новым таким комментарием, я думаю, а не пора ли издать книгу "Советы от TheCalligrapher"...
            Ответить
          • >Тип 'size_t'
            >нужен тип
            >других типов
            >целый беззнаковый тип

            СТАТИКОБЛЯДИ_ВЕСЕЛО_И_ЗАДОРНО_"РЕШАЮТ"_В ЫДУМАННЫЕ_ПРОБЛЕМЫ.ДЖПЕГ
            Ответить
          • > Классический пример: 16-тибитные платформы с сегментированной моделью памяти, где диапазон 'size_t' ограничивался 65535, в то время как количество элементов в списке запросто могло превышать это значение.

            Это вряд ли. Такой список просто не помещался бы в адресуемую память.
            Ответить
            • А этот бред откуда? Из "Упанишад"? На 16-битных платформах объем адресуемой памяти заведомо превышал 64К. В обыкновенной Win16 можно было запросто навыделять маленьких блочков памяти в количестве более 65535. Да и в банальном DOS с его 640К можно было.
              Ответить
              • Нельзя. malloc и системные функции выделяли память кратной 16 байт. Впрочем, элемент списка всё равно содержит два 32-битных указателя (а если использовать только 16-битные, номера сегментов, в ограничение упираемся само собой), это 8 байт, да ещё полезная нагрузка — итого минимум 16 байт. В мегабайт адресного пространства влезает 65536 элемента. Ах да, ещё ведь есть 65520 байт адресуемого сверх мегабайта пространства. Но всё равно в этом мегабайте будет и ПЗУ, и видеопамять, и сама программа с огрызками ОС — мегабайт свободной не наберётся.

                Вот с vector<bool> можно было бы легко получить больше 64К элементов. Или с чем-то вроде deque.
                Ответить
                • Это в DOS-к еще можно пытаться аргументировать таким образом. К защищенному режиму Win16, где сегментная часть указателя была уже полноценным селектором, эта логика уже не применима. Количество адресуемой памяти там ограничивалось, ни много ни мало, 4 гигабайтами.
                  Ответить
    • Вспомнилось, как один дельфин на лурке советовал по списку проходить так:
      if L.Count>0 then
      for i := 0 to L.Count-1 do DoSmth(L.Items[i]);
      потому что типа если убрать проверку на >0, то цикл всё равно будет выполняться и типа это страшный баг дельфей.
      Потом оказалось, что он блять i беззнаковое брал.
      Ответить
      • Во-первых не кто-то, а я, во-вторых не советовал, а каялся.
        Но в лужу я тогда сел знатно, спорить не буду.
        Ответить

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