Кресты / Говнокод #25991 Ссылка на оригинал

0

  1. 1
  2. 2
  3. 3
  4. 4
char (&getArray())[11] {
  static char arr[] = "1234567890";
  return arr;
}

Как вернуть массив из функции в C/C++

На самом деле нет: возвращается ссылка

Запостил: Elvenfighter Elvenfighter, (Updated )

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

  • зачем заворачивать в лямблию, когда можно просто в струхтуру?
    Ответить
      • тут нет лямблии, тут есть во-первых статическое что-то, на что вероятно можно возвращать реф.
        А кроме того это что-то еще и само указывает на стат литерал, который вообщзе небось никуда не денеца
        Ответить
        • В секцию данных помещаются 11 символов:
          .ascii "1234567890\0"

          Функция возвращает ссылку на этот массив.

          На многих платформах эта секция ещё и не защищена от записи (в ту же секцию помещаются переменные с начальным значением), так что теоретически можно этот литерал испортить.
          Ответить
          • Конечно можно, стандарт разрешает

            В баре обсудим
            Ответить
            • Что ты, питух анскильный, можешь с ним обсудить? Ах да, оба питуха найдут о чём покукарекать.
              Можно не потому что стандарт разрешает (разрешение спрашивать нужно только анскильным питухам вроде тебя), а потому что это работает.
              Ответить
            • На всякий случай напоминаю тебе, что отвечать безграмотному уёбку, который косит под superkiller1997, не нужно
              Ответить
          • Испортить его можно разве что неопределенным поведением. В плюсах строковые литералы это массивы const char
            Ответить
            • Стал разбираться. Если написать static char arr[] = "1234567890"; то константа помещается в секцию данных и её можно изменить:
              #include <iostream>
              
              char (&getArray())[11] {
                static char arr[] = "1234567890";
                return arr;
              }
              
              int main() {
                 char * stroka = getArray();
                 std::cout << stroka << std::endl;
                 stroka[0] = '9';
                 std::cout << stroka << std::endl; // выведет 9234567890
                 std::cout << getArray() << std::endl; // выведет тоже 9234567890
                 return 0;
              }

              Этот код портит значение массива static char arr[], объявленного внутри функции. Причём в ассемблерном выхлопе никакого копирования нет, портится оригинал:
               	.data
              __ZZ8getArrayvE3arr:
              	.ascii "1234567890\0" ; это начальное значение
              	.text
              __Z8getArrayv:
              LFB1546:
              	push	ebp
              	mov 	ebp, esp
              	mov 	eax, OFFSET FLAT:__ZZ8getArrayvE3arr ; возвращаем в eax адрес статической переменной
              	pop	ebp
              	ret


              Если же написать сразу return "1234567890", то константа "1234567890\0" помещается в секцию .rdata, а не .data. Некоторые линкеры и некоторые ОС защищают такую секцию от записи, некоторые — нет. Но в любом случае с точки зрения C++ это уже будет const char, поэтому без const_cast изменить не дадут.
              Ответить
              • разбирали же уже
                >char arr[] = "1234567890";
                тут ты скопировал себе в стек чары

                >char *arr = "1234567890";
                тут ты положил в стеке указатель на строку, и компилятор имеет право сделать ее RO
                Ответить
                • char arr[] = "1234567890"; // тут скопировал в стек чары
                  static char arr2[] = "1234567890"; // а тут никакого копирования

                  Во втором случае начальное значение объявляется сразу на месте, на том самом, где расположена переменная.
                  Ответить
                  • во -втором месте ты скопировал в стат память. Однако поскоку стать память живет вечно, то разницы никакой нет

                    зы: в каком языке кроме Яебу строки мудакбельны?
                    Ответить
            • А теперь самый ад: gcc с ключом -fmerge-all-constants. Пишем код:
              #include <iostream>
              
              typedef char (& pituh)[11];
              
              char (&getArray())[11] {
                return const_cast<pituh>("1234567890");
              }
              
              int main() {
                 char * stroka = getArray();
                 std::cout << stroka << std::endl;
                 stroka[0] = '9';
                 std::cout << stroka << std::endl;
                 std::cout << "1234567890" << std::endl;
                 return 0;
              }
              На моей платформе оба литерала попали в секцию .rdata, защищённую от записи, поэтому на строке stroka[0] = '9'; программа упала.

              Исправляем: gcc -S, меняем секцию с .rdata на .data (представим себе, что у нас линкер использует платформу типа a.out, где нет .rdata), собираем... литерал чудесным образом испортился.
              Ответить
    • тут фунгкция возвращает указатель на строковой литерал, что не так?
      Ответить
      • Nein! Функция возвращает ссылку на массив. Для понимания:
        int (&getArray())[4] {
          static int arr[] = {1, 2, 3, 4};
          return arr;
        }
        
        int main() {
          int sum = 0;
          for (int e : getArray())
            sum += e;
          return sum; // вернет 9 (1 + 2 + 3 + 4)
        }
        Ответить
    • > зачем заворачивать в лямблию
      там лямбд нет

      > когда можно просто в струхтуру?
      std::array так и делает.
      Ответить
      • > там лямбд нет
        Они и не нужны. Их придумали анскильные питухи, не осилившие указатели на функции.

        > std::array
        Питух, убери от меня это говно. Питух не осилил вернуть массив из функции, и придумал это говно.
        Ответить
      • На всякий случай напоминаю тебе, что отвечать безграмотному уёбку, который косит под superkiller1997, не нужно
        Ответить
  • Для сравнения Free Pascal:
    type massiv = array[0..10]of char;
    
    function getArray: massiv;
    const arr: massiv = '1234567890';
    begin
      getArray := arr
    end;
    
    begin
        Writeln(getArray)
    end.
    Вызов функции getArray компилируется в такое:
    lea	esp,[esp-12] ; выделяем в стеке место под результат
    ...
    lea	eax,[ebp-12] ; передаём в функцию последним аргументом указатель на выделенный кусок памяти
    call	P$PROGRAM_$$_GETARRAY$$MASSIV
    lea	esi,[ebp-12] ; используем заполненный функцией блок данных


    С точки зрения исходника кажется, что функция способна возвращать массив или строку по значению. На самом же деле вызывающий код выделяет память под результат (в стеке рядом с локальными переменными), а потом передаёт в функцию указатель на этот блок памяти последним аргументом. С точки зрения ассемблерного выхлопа у функции становится на один аргумент больше. Похожий трюк (с невидимым в исходнике аргументом) происходит при вызове методов объектов, но там указатель на self передаётся первым аргументом.

    Эквивалентный код:
    type massiv = array[0..10]of char;
    
    procedure getArray(var x: massiv);
    const arr: massiv = '1234567890';
    begin
      x := arr
    end;
    
    var y: massiv;
    begin
        getArray(y);
        Writeln(y)
    end.
    Тут явная передача по ссылке, но тут нужно явно же заводить переменную под результат, а в случае функции результат был безымянным.
    Ответить
    • Так это просто кал конгвеншен такой

      В x86 часто бывает так, что инт ты вертаешь через регистр, а сложные агрегаты через создание их на стеке вызывающей стороны и передачи указателя

      Скажи, ты помнишь архиватор arj?
      Ответить
      • Помню. Я даже видел его форк arjz, который умел сжимать сильнее. А ещё я помню jar, но не который используется Жабой и является зипом по сути, а другой, который был очередной попыткой замены arj, но не стал популярным.
        Ответить
        • Я помню, что распаковывать надо было
          ``arj x``
          Ответить
        • Смотри, если ни ты, ни люр не соглашаетесь до завтра, встреча переносится из Москвы в Питер, а там как хотите. Заебался я вас пинговать.
          Ответить
      • >> а сложные агрегаты через создание их на стеке вызывающей стороны и передачи указателя

        Если завернуть в структуру, то сишка/кресты поступают так же, как Паскаль:
        #include <iostream>
        typedef struct pituh_tag {char data[11];} pituh_type;
        
        
        pituh_type getArray() {
          static pituh_type arr = {.data = "1234567890"};
          return arr;
        }
        
        int main() {
          unsigned sum = 0;
          for (auto e : getArray().data) {
            sum += e;
          }
          std::cout << sum << std::endl;
          return 0;
        }

        Ассемблерный выхлоп:
        subl	$52, %esp ; занимаем место в стеке
        ...
        leal	-36(%ebp), %eax ; передаём в функцию указатель на выделенный блок
        movl	%eax, (%esp) 
        call	__Z8getArrayv ; вызываем
        leal	-36(%ebp), %eax ; используем указатель на блок, который заполнила функция


        Если же я сделаю typedef char pituh_type[11], то компилятор ругнётся:
        error: 'getArray' declared as function returning an array
        Он согласится компилировать код, только если объявить pituh_type & getArray(), и в этом случае в eax/rax вернёт ссылку.

        Почему в сишке и в крестах так недолюбливают массивы, что их обязательно нужно завернуть в структуру, чтобы передать по значению?
        Ответить
        • > недолюбливают массивы,
          Керниган с Ричи не то что плюнули, насрали блядь в вечность. Столько боли и говна из за этого
          Ответить
          • Массивы в сишке на особом положении для того, чтобы элементы массивов можно было копировать выражением *p++ = *q++, в то время как в других языках программирования было p[i] = q[i]; i = i + 1. Полезная особенность, не правда ли?
            Ответить
    • пассалист инканус серет под себя
      array[0..10]

      '1234567890'

      оно вообще сконпелицца?
      Ответить
      • Блядь, откуда я взял ассемблерный код? В уме компилировал по-твоему?

        https://ideone.com/SNuZfm

        Короче, иди в свой «Сосач» или в свои «Одноклассники». Там твоя аудитория.
        Ответить
        • ты что?? Не отвечай этому уёбку никогда!!
          Ответить
        • Нахуй ты отвечаешь уебку?
          P.S. Не заметил, уже написали.
          Ответить
          • Хорошо, постараюсь.

            Кстати, для сомневающихся: в Турбо Паскале и в его потомках массивы of char можно инициализировать строковым литералом. Если литерал короче массива, хвост добивается байтами, равными нулю. Наоборот же (если массив короче литерала) не работает.
            Ответить
                • На "Говнокоде" паскаль знают только два человека: стертор и инканус. Никаких студентов здесь нет.
                  Ответить
                  • Я тоже знаю паскаль. Знал вернее. В 1999-м году.
                    Ответить
                  • Причём у Стертора откуда-то Турбо Паскаль шестой версии. Я такую версию запускал пару раз в жизни: первый раз она мне попалась лет 20 назад на диске с компиляторами; второй раз запустил сейчас, чтобы проверить инициализацию массивов.

                    Самая ватная версия, кстати: у TP6 даже была кустарная русификация IDE (мне, правда, такой мутант не попадался).

                    P.S. Ещё Тарас знает.
                    Ответить
                    • Тарас знал дельфи, и наверняка уже забыл
                      Ответить
                    • Ватник настолько туп, что до сих пор не просёк, в чем там дело. Неудивительно, что они наебнули Боинг и убили немца за паспорт с аквилой.
                      Ответить
                • Студенты давно пишут на ABC.NET, если только они девятнадцать раз на второй год не осталавилсь
                  Ответить
                  • ABC.NET анскилльный (см. секцию «Отсутствует»):
                    http://pascalabc.net/downloads/pabcnethelp/index.htm?page=Common/PABCNETvsDelphi.html

                    И инициализации массивов строковым литералом в нём тоже нет.

                    Плохо сделали, тупо.

                    Ещё и сайт на фреймах, так что я сначала неправильную ссылку скопировал. Какой анскилл )))
                    Ответить
                    • слушай, ну хоть так

                      если бы они не сделали паскаль абц, то на чем бы сейчас учили детей? на джаве? на питне?
                      ну ево нахуй

                      впрочем, и дотнет тоже не лучший вариант
                      Ответить
                      • Кое-что мне даже понравилось: локальные переменные блока, локальные переменные цикла, генерики, лямбды с замыканиями (увы, замыкания неявные), методы-расширения, множества на базе произвольных типов, кортежи, срезы, yield, анонимные классы.

                        Странно, что они добавили много интересного, но при этом много интересного убрали вроде записей с вариантами и вариантных типов. И вот даже такую мелочь убрали, как инициализация массивов строковым литералом. Нужно явно писать ('1', '2', '3', '4', '5', '6', '7', '8', '9', '0') вместо '1234567890'.
                        Ответить
              • Проверил несколько версий. Автопаддинг строковых литералов нулями появился в TP7. Во Фрипаскале и в Дельфи он тоже есть. В TP6 автопаддинга не было.
                Ответить
        • Ну blackwater на дамбассе ты же взял откуда-то, лишнехромосомный.
          В общем, сасай.
          ­  File  Edit  Search  Run  Compile  Debug  To
          ╔═[■]════════════════════════════ NONAME00.PA
          ║ Error 100: String length mismatch.
          ║{$X-}
          ║type TLA = packed array [0..2] of Char;
          ║const LA: TLA = 'AB';
          ║
          ║begin
          ║  Writeln('''', LA, '''');
          ║end.
          ║
          ║
          ║
          ║

          Ватники даже в пассаль не могут, дно пробито!
          Ответить
  • а если вместо строки будет {1,2,3,4}, то все равно будет работать?
    а если static убрать?
    Ответить
    • 1. будет работать
      2. скомпилируется с ворнингом "returning a reference to local", возможно будет рабтать, но это неточно.
      Ответить
      • 1. пушо static?
        2. пушо строковой литерал?
        а если сделать 1 и 2?
        Ответить
        • 1. Нет, потому что char -- целочисленный тип
          2. Нет, потому что UB
          3. Будет 1 и 2
          Ответить
          • >1. Нет, потому что char -- целочисленный тип
            и что? ну я создал массив челочисленных типов на стеке, и вернул на него ссылку.
            Массив закончился в месте с функцией, разве нет?

            >2. Нет, потому что UB
            почему нельзя вернуть ссылку на статическую переменную?
            Ответить
            • > Массив закончился в месте с функцией, разве нет?
              Согласно стандарту. Что компилятор сделает -- вопрос отдельный.

              > почему нельзя вернуть ссылку на статическую переменную?
              потому, что можно, АПВС?
              Ответить
              • Постой, я запутался)

                1. Вернуть массив по стандарту нельзя. Это UB.
                2. Но если он статический то можно. По стандарту.
                3. Если массив инициализрован строковым литералом, но возвращать его все равно UB. Но почти всегда это будет работать. Потому что литералы никуда не деваются. Но могут. Но не деваются.

                Верно?
                Ответить
                • > 1. Вернуть массив по стандарту нельзя. Это UB.
                  Нет, просто нельзя. Не скомпилируется: нет соответствующего синтаксиса. А в примере возвращается ссылка (читай текст под спойлером).

                  > 2. Но если он статический то можно. По стандарту.
                  В контексте №1, все так же нельзя. Но ссылку вернуть можно, и это не UB.

                  > 3. Если массив инициализрован строковым литералом, но возвращать его все равно UB. Но почти всегда это будет работать. Потому что литералы никуда не деваются. Но могут. Но не деваются.
                  Да.
                  Ответить
                  • >> 3. Если массив инициализрован строковым литералом, но возвращать его все равно UB. Но почти всегда это будет работать. Потому что литералы никуда не деваются. Но могут. Но не деваются.
                    >Да.
                    Шта?

                    char * foo()
                    {
                      //char a[] = { 1, 2, 3, 4 };
                      char a[] = "pehetut";
                      // char a[2]; a[0] = 0;
                      a[1] = '☺';
                      return a;
                    }
                    Какая разница, чем массив инициализирован, данные все равно скопируются в стек. UB.
                    Ответить
                    • А если убрать " a[1] = '☺';" то копулятор может же так соптмизировать и не скопировать?
                      Ответить
                      • Ну теоретически оптимизировать он может вообще до return 0; // UB
                        Ответить
                        • ладно, ты прав, это уб

                          а
                          char* a = "pehetut";

                          тоже уб, потому что никто не гарантирует жизни пехетута (кто такой пехетут, кстати?) хотя все это и делают
                          Ответить
                          • Петух такой, наверное.
                            Подождите-ка. Я всегда думал, что время жизни строкового литерала должна быть вся программа. Неужели по стандарту это не так?
                            char *s = "petutuh"; это указатель на статическую область памяти, а char s[] = "petuhuh"; это заполнение выделенного в стеке массива.
                            Ответить
                • >> 1. Вернуть массив по стандарту нельзя. Это UB.

                  Проверил, как ведёт себя gcc.

                  1. В C++ вернуть массив нельзя. Можно вернуть только ссылку на массив, явно указав & у типа результата функции.

                  2. В Си вернуть массив тоже нельзя, но в Си нет ссылок. Можно вернуть указатель на массив (что по сути будет указателем на указатель) либо указатель на начальный элемент массива либо завернуть массив в структуру.
                  Ответить
                  • 3. можно вернуть указатель на первый элемент массива

                    char* petih() {
                    char petuh[42];
                    return petuh;
                    }
                    Ответить
                    • zip> либо указатель на начальный элемент массива
                      guest> 3. можно вернуть указатель на первый элемент массива

                      В общем, мы имели в виду одно и то же. char * вернуть можно, а char [] почему-то нельзя, хотя в сишке они обычно синонимы (char petuh[42] можно засунуть в return, хотя return должен вернуть значение типа char *).
                      Ответить
                      • цинизм в том, что получить его можно

                        void raka_stertory(char[]) {
                        }

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

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

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

Где здесь C++, guest?!

    А не использовать ли нам bbcode?


    8