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

    0

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

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

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

    Запостил: Elvenfighter, 24 Октября 2019

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

    • Но, кстати, работает как и ожидалось: https://cppinsights.io/s/1f526251
      Ответить
    • зачем заворачивать в лямблию, когда можно просто в струхтуру?
      Ответить
      • Где тут лямблия?
        Ответить
        • тут нет лямблии, тут есть во-первых статическое что-то, на что вероятно можно возвращать реф.
          А кроме того это что-то еще и само указывает на стат литерал, который вообщзе небось никуда не денеца
          Ответить
          • В секцию данных помещаются 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)
          }
          Ответить
          • Массив в данном случае является строковым литералом, не?
            Ответить
          • Реально, питушок?

            Ты строковый литерал от массива отличаешь? Иди матчасть поучи, питух.
            Ответить
          • На всякий случай напоминаю тебе, что отвечать безграмотному уёбку, который косит под superkiller1997, не нужно
            Ответить
      • > зачем заворачивать в лямблию
        там лямбд нет

        > когда можно просто в струхтуру?
        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?
        Ответить
        • арджарджа
          Ответить
          • http://www.arjsoftware.com/
            лучший софт 2001 года
            совместим с 2000-м годом
            поддерживает WindowsME

            что вам еще надо?
            Ответить
            • >> Below are supplemental modules for ARJ, ARJ32, and JAR. They are intended for USA users only. They are not for export.

              Опять это экспортное законодательство...
              Ответить
            • https://www.yumpu.com/en/image/facebook/48850143.jpg
              Ответить
        • Помню. Я даже видел его форк 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 можно инициализировать строковым литералом. Если литерал короче массива, хвост добивается байтами, равными нулю. Наоборот же (если массив короче литерала) не работает.
              Ответить
              • брехливость-русни.jpg
                Ответить
              • Никого не ебёт турбо паскаль.
                Ответить
                • А как же студенты?
                  Ответить
                  • На "Говнокоде" паскаль знают только два человека: стертор и инканус. Никаких студентов здесь нет.
                    Ответить
                    • Я тоже знаю паскаль. Знал вернее. В 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[]) {
                          }

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

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

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