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

    +141

    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
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    void array_random_input(int *array, unsigned int *array_size)
    {
    	unsigned register int i, j;
    	for(i=0; i<(*array_size); i++)
    		for(j=0; j<(*array_size); j++)
    			*(array + (*array_size) * i + j) = rand()%100;
    }
    
    void array_recombination(int *array, unsigned int *array_size)
    {
    	unsigned register int i, j, tmp;
    	for(i=0; i<(*array_size)/2; i++)
    		for(j=0; j<(*array_size)/2; j++)
    			{
    				tmp = *(array + (*array_size) * i + j);
    				*(array + (*array_size) * i + j) = *(array + (*array_size) * ((*array_size)/2 + i) + ((*array_size)/2 + j));
    				*(array + (*array_size) * ((*array_size)/2 + i) + ((*array_size)/2 + j)) = tmp;
    			}
    			
    	for(i=0; i<(*array_size)/2; i++)
    		for(j=(*array_size)/2; j<(*array_size); j++)
    			{
    				tmp = *(array + (*array_size) * i + j);
    				*(array + (*array_size) * i + j) = *(array + (*array_size) * ((*array_size)/2 + i) + (j - (*array_size)/2));
    				*(array + (*array_size) * ((*array_size)/2 + i) + (j - (*array_size)/2)) = tmp;
    			}
    }
    
    //ardent greetings to my teacher F.S.V.! :)

    Условие задачи: необходимо создать динамический двумерный массив размерностью 2n и сделать перестановку крест накрест его четвертей. В качестве параметров все функции принимают указатели. Для пущего brainfuck'а доступ к элементам массива осуществлялся с использованием одномерной адресации (по другому никак не получалось :)
    Для сокращения привожу только функции наполнения массива случайными значениями и перестановки элементов.

    Запостил: mr_visor, 23 Декабря 2010

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

    • (Закрывая глаза на все остальное) А с какой, простите, целью передача размера массива производится "по указателю"? Чтобы побольше скобочек и звездочек в коде мелькало?

      P.S. Или для пущей суровости размер массива у вас из другого потока прямо на лету меняться может? :)))
      Ответить
      • "А с какой, простите, целью передача размера массива производится "по указателю"?" - это условие задачи (как и в случае с размером массива) + экономия памяти в качестве бонуса за счёт использования этих самых указателей (ну блин! ну не влезет же всё это в 4 гб оперативы! :))
        Ответить
        • "Условие задачи" - ОК, это я могу понять. Но вот как из этого может получиться экономия памяти - этого я, извините, в упор не понимаю.
          Ответить
          • При использовании указателей в качестве аргументов, внутри функций мы работаем напрямую со значениями по переданным адресам.
            Если же будем в качестве аргументов передавать переменные, то внутри функций мы создаём копии этих переменных (вот тот самый момент с памятью!), проводим над ними манипуляции и в конце возвращаем их значение.
            Уже получается, что в коде не функции, а процедуры :)
            Ответить
            • Для массивов это не верно. Массивы автоматически передаются по указателю. Или в Си не так, как в С++?
              Ответить
            • Лучше конечно же сто раз разыменовывать размер ? Да и при вызове придется неконстантный объект использовать.
              Раз уж речь об оптимизации зашла - к чему два цикла в функции по i, j ? Массив-то непрерывный с точки зрения размещения в памяти. Понимаю еще если бы какая-то логика была от i,j, так там обычный рандом.
              Ответить
            • >При использовании указателей в качестве аргументов, внутри функций мы работаем напрямую со значениями по переданным адресам.

              Это называется ссылка. Указатель -- это тип данных, который хранит адрес, а не предоставляет значение по адресу. Для получения значения его следует разимеровать (звездонуть *ptr).
              Кроме того. Указатель всегда, кроме может быть критических случаев, имеет туже длину, что и int. На всякие unsigned int нужно больше уделить внимания, потому что они могут оказать 4х байтными, когда система 8и байтная. Тогда вы ещё и потеряете в памяти, создав внутри функции автоматический указатель.
              Ответить
              • >кроме может быть критических случаев
                Таковым случаем являются указатели на члены классов (правда в с++, а не в си).
                На 64хбитных архитектурах указатель может быть больше int.
                Зато с size_t или с ptrdiff_t - указатель совпадает по размеру.
                Ваш К.О.
                Ответить
                • >указатели на члены классов
                  На 32хразрядных платформах до 16 байт.
                  Могу предположить, что на 64х разрядных - до 32 байт на 1 указатель.
                  Ответить
                  • > На 32хразрядных платформах до 16 байт.
                    Можно пример?
                    Ответить
                    • http://www.rsdn.ru/article/cpp/fastdelegate.xml
                      Ну подробная статья на эту тему + реализазация костыля для испрвления этой ситуации.
                      Ответить
                      • Безумно знакомая статья, я её когда-то читал, чтобы понять что же это за указатели на методы и как с ними работать.

                        А из таблицы получается, что вопрос не соответствия длины указателя длине int является, помимо прочего ещё и вопросом компилятора.
                        Ответить
                        • Размеры типов int и прочих целых в стандарте не указаны.
                          Сказано лишь, что:
                          char<=short<=int<=long<=long long
                          Всё. Тоесть зависят размеры как от платформы, так и от компилятора.
                          Ответить
                          • Plain ints have the natural size suggested by the architecture of the execution environment

                            Я так понимаю это высказывание, что int -- всегда равен длине процессорного слова, которое, в общем случае, есть длина указателя на память.
                            Хотя, это, несомненно, домысливание стандарта.

                            И не найду я сейчас утверждения о независимости аппаратной long int (а может unsigned int)... Но оно точно где-то есть.
                            И при всём при этом они действительно должны вкладываться одни в другой. Достаточно жозенькие условия...
                            Ответить
                            • Что-бы там не было написано, но я пока не пользовался компилятором, что-бы int на 64битной платформе был = 64. Пока только с 32хбитными int видел. Хотя несомненно, наверное где-то с 64хбитными int компиляторы есть.
                              Ответить
                            • Это значит, что размер int определяется архитектурой, а про длинну процессорного слова где тут написано?
                              Ответить
                              • Не только архитектурой, но и к сожаленю компилятором.
                                int может быть как 64 бита, так и 32.
                                Ответить
                              • have the natural size

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

                                Про современные, входящие в строй, 64x битные машины общие -- не знаю. Думаю, стоит попробовать на них ставить писаный для них Unix и пользоваться компилятором оттуда, тогда всё будет срастаться со стандартом.

                                Это моё предположение.
                                Ответить
                                • Linux x86_64
                                  GCC 4.5.1
                                  sizeof(int) == 4
                                  Ответить
                                  • У вас open-source, я так понимаю. Там была тенденция "64-bit safe". То есть, компиляторы предоставляли всё то же, что и для 32-bit, а адресация может меняться на 64. Это позволяет создавать коды, которые компилируются и в 32, и в 64 bit архитектурах. Потому что вдали маячит 64, но большинство ещё пользует 32 (да собственно сейчас оно пока так и есть).
                                    Боюсь, вы держите в руках именно такой компилятор.
                                    Ответить
                                  • тогда как архитектура суггестит использовать 64битные регистры, FAIL
                                    Ответить
                        • указатель метода -- процедурный указатель и указатель на экземпляр, всего 2 шт.
                          Ответить
                          • Это в том простом случае, который мне как раз был нужен, действительно подходит так понимать. Но... В более сложных... Начинаются непонятки...
                            И в статье хорошо об этом написано.
                            Ответить
                • Указатель совпадает по размеру с типами `intptr_t` и `uintptr_t` - по определению.

                  Никакого совпадения по размеру с типами `size_t` и `ptrdiff_t` нет. Язык ничего подобного никогда не гарантировал. Да и нет никаких предпосылок для такого совпадения. Это какое-то странное заблуждение, родившееся в эпоху flat memory model.

                  Несложно вспомнить 16-битные сегментные платформы, на которых `size_t` был меньше указателя в некоторых моделях памяти. Платформы с "маленьким" `size_t` прекрасно существюит и сейчас (какие-то компиляторы С для IBM, кажется).
                  Ответить
                  • Возможно, Вы правы про `intptr_t` и `uintptr_t`. Нужно заглянуть в стандарт на досуге...
                    Ответить
                  • Впрочем соглашусь лишь с утверждением о size_t. ptrdiff_t - не может не совпадать по размеру с указателем.
                    Ответить
                    • Может. Стандарт языка четко и ясно говорит, что разность двух указателей (указыващих на элементы одного массива) может поместиться, а модет и не поместиться в тип `ptrdiff_t`. Если разность не помещается, то поведение программы не определено. Так образом конкретный компилятор имеет формальное право сделать тип `ptrdiff_t` сколь угодно маленьким.

                      Более того, тип `ptrdiff_t` по определению - тип знаковый. Чтобы он "всегда работал" для вычисления разности двух указателей, этот тип обязан быть как минимум на один бит длиннее, чем тип `size_t`. Вы видели хоть одну платформу, где `ptrdiff_t` длиннее, чем `size_t`? В рельности они практически всегда имеют один им тот же размер, что говорит о том, что вышеупомянутая проблема с `ptrdiff_t`- соверешенно реальна. Тип `ptrdiff_t` очень легко переполнить.
                      Ответить
                  • Однако, боюсь согласно современного стандарта, `size_t` и `ptrdiff_t` совпадают по размеру с указателем.
                    size_t действительно раньше был не совпадающем по размеру с указателем. Но сейчас стандарт новый и проживёт это ещё долго.
                    Ответить
                    • Так что смело, если не пользуешься машиной времени, можно использовать `size_t` и `ptrdiff_t`.

                      Или Вы предлагаете, что стоит писать кросплатформенный код, который можно отправлять на машине времени в прошлое без необходимости переписывать его?
                      Ответить
                      • Нет, нельзя. Прошлое здесь не при чем. Писать код по принципу "написана херня, но работать должно" - это классическое говнокодирование. Не надо заниматься говнокодированием.
                        Ответить
                    • Нет. Никаких изменений в современном стандарте на эту тему нет. Более того, сама идея сравнения размера типа `size_t` с размером указателя - это какая-то странная ерунда. Указатель как паравило имеет возможность адресации всего адресного пространства программы. Тип `size_t` по определению имеет возможность хранить размер самого большого объекта в программе. Это - две соврешенно разные, никак не связанные концепции.

                      В эпоху flat memory model откуда то появилось наивное заблуждение о том, что максимальный размер объекта должен быть равен полному объему адресного пространства программы - отсюда и это странное верование о том, что размер `size_t` должен быть равен размеру указателя. На самом же деле это не так.

                      Еще раз: целочисленные типы, равные по размеру указателю, это типы `intptr_t` и `unintptr_t`. Именно для этого они и были введены в стандарот C99. А `size_t` к размеру указателя никакого отношения не имеет вообще.
                      Ответить
                      • >Тип `size_t` по определению имеет возможность хранить размер самого большого объекта в программе.

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

                        Впрочем, соглашусь, что, когда последний раз читал стандарт, инфы о совпадении размера size_t c указателем не видел, но по всем сайтикам почему то кричат, особенно в рекламе анализатора 64х битного кода Viva64, что sizeof(size_t)==sizeof(void*).
                        Ответить
                      • >Еще раз: целочисленные типы, равные по размеру указателю, это типы `intptr_t` и `unintptr_t`.

                        Я полностью согласен. :)
                        Ответить
                      • Спасибо за хорошую дискуссию. :)
                        Ответить
                      • >`intptr_t` и `unintptr_t`
                        Только опечатка:
                        `intptr_t` и `uintptr_t`
                        Но это ничего не меняет.
                        Ответить
              • >Указатель всегда, кроме может быть критических случаев, имеет туже длину, что и int.

                Если вы искали способ открыть шкатулка Лемаршана, то вот он ^^
                Ответить
            • Простите, но в варианте с указателем вы создаете в функции локальный параметр-указатель, на который тоже тратится память. Причем еще не известно, на что потратится больше памяти - на ваш указатель или на копию значения типа `unsigned`. В общем случае - на указатель потратится не меньше, а может и больше (зависит от платформы и модели). Т.е. никакой экономии памяти тут нет и в помине. Наоборот, есть "перерасход".
              Ответить
              • >или на копию значения типа `unsigned`
                Там же вроде массив, а не 1 unsigned.
                Ответить
                • Я говорю именно про параметр `array_size`. Никакого массива там нет. Доступ ведется только к `*array_size`, т.е. Никакого смещения от `array_size` никогда и нигде не делается. Таким образом `array_size` - это просто одинокий размер массива, который по какой-то необъяснимой причине передали "по указателю". Никакого смысла в этом не было и никакой "экономии памяти" это не достигло. (Остается только объяснение через calling convention, как заметил gegMOPO4 в комментарии ниже).
                  Ответить
      • Передача размера "по указателю" требуется, если соответствующие функции будут использоваться из Фортрана. Суровые видать преподаватели у автора. Использование ключевого слова register тоже намекает на кондовость.

        Ещё один вариант -- все функции должны иметь одинаковую сигнатуру, а некоторые из них могут изменять размер.

        Понятно, что даже в этом случае первым же делом следует ради читаемости и эффективности разыменовать указатель в локальную переменную.
        Ответить
        • > если соответствующие функции будут использоваться из Фортрана
          Да, с таким аргументом я могу согласиться.

          Но, правильно замечено, что всё же:
          >первым же делом следует <...> разыменовать указатель в локальную переменную
          Ответить
    • Каммент порадовал, спасибо, Рома)
      Ответить
    • показать все, что скрытоvanished
      Ответить

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