Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 07.05.2023, 17:29   #1
Женя32
Форумчанин
 
Регистрация: 12.09.2008
Сообщений: 621
Счастье Ссылка на массив из функции, вывод значений элементов массива

Здравствуйте!
Подскажите, пожалуйста, почему так получается?: Хочу вывести значения элементов массива при помощи ссылки на него, но у меня выводится какое-то не понятное значение.
1 -858993460 -858993460

Подскажите, пожалуйста, что ни так я делаю?
Код:
#include <conio.h>
#include <iostream>

// все пространство имен std
using namespace std;

int* a()
{
    int b[3] = { 1, 2, 3 };
    return b;
}

// Главная функция. С данной функции происходит вход в программу
int main()
{
    setlocale(LC_ALL, "Russian");

    int* ar;

    ar = a();

    cout << ar[0] << ' ' << ar[1] << ' ' << ar[2];
    
    _getch(); // ожидание нажатия клавиши. Модуль conio.h
    return 0; // условие завершение программы
}
Женя32 вне форума Ответить с цитированием
Старый 07.05.2023, 17:51   #2
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,285
По умолчанию

Массив объявлен локально и будет размещен на стеке, так что адрес массива будет валидным до конца функции. Нужно динамически выделять память, чтобы возвращать адрес, или работать с более высокоуровневыми классами (например, вектором):
Код:
int* a()
{
    int *b = new int[3];
    b[0] = 1;
    b[1] = 2;
    b[2] = 3;
    return b;
}
...
    int* ar;
    ar = a();
    cout << ar[0] << ' ' << ar[1] << ' ' << ar[2];
    delete[] ar;
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )
BDA вне форума Ответить с цитированием
Старый 07.05.2023, 18:03   #3
Женя32
Форумчанин
 
Регистрация: 12.09.2008
Сообщений: 621
По умолчанию

Цитата:
Сообщение от BDA Посмотреть сообщение
... адрес массива будет валидным до конца функции. ...
Почему тогда у автора этого видео все работает?
https://youtu.be/bRJLPFmC_Ls?list=PL...hGTOOTy&t=1547
Женя32 вне форума Ответить с цитированием
Старый 07.05.2023, 18:21   #4
BDA
МегаМодератор
СуперМодератор
 
Аватар для BDA
 
Регистрация: 09.11.2010
Сообщений: 7,285
По умолчанию

"Чудом сработало" != "корректная программа". Данные из стека никуда не пропали, поэтому и выдало ожидаемое, но так делать нельзя. Похоже, автор недостаточно компетентен, чтобы учить других. Студия выдает предупреждение "Warning C4172 returning address of local variable or temporary: b" на данный код, но у автора окно с ошибками и предупреждениями за кадром. Плохой пример:
Код:
#include <conio.h>
#include <iostream>

using namespace std;

int* a()
{
    int b[3] = { 1, 2, 3 };
    return b;
}

int* c()
{
    int d[3] = { 4, 5, 6 };
    return d;
}

int main()
{
    setlocale(LC_ALL, "Russian");

    int* ar;

    ar = a();
    c();

    cout << ar[0] << ' ' << ar[1] << ' ' << ar[2];

    _getch();
    return 0;
}
Вы ожидаете 1 2 3, а получите 4 5 6 (в лучшем случае), так как данные в стеке будут изменены.
Пишите язык программирования - это форум программистов, а не экстрасенсов. (<= это подпись )

Последний раз редактировалось BDA; 07.05.2023 в 18:27.
BDA вне форума Ответить с цитированием
Старый 07.05.2023, 18:28   #5
macomics
Участник клуба
 
Регистрация: 17.04.2022
Сообщений: 1,833
По умолчанию

Код:
$ g++ -ggdb -o ./main ./main.cpp
./main.cpp: В функции «int* a()»:
./main.cpp:10:12: предупреждение: возвращен адрес локальной переменной «b» [-Wreturn-local-addr]
   10 |     return b;
      |            ^
./main.cpp:9:9: замечание: объявлено здесь
    9 |     int b[3] = { 1, 2, 3 };
      |         ^

$ gdb -q ./main
Reading symbols from ./main...
(gdb) start
Temporary breakpoint 1 at 0x11e4: file ./main.cpp, line 16.
Starting program: /home/macomics/main 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Temporary breakpoint 1, main () at ./main.cpp:16
16          setlocale(LC_ALL, "Russian");
(gdb) next
20          ar = a();
(gdb) step
a () at ./main.cpp:8
8       {
(gdb) qv
                                                                                                                       RAX=0x0000000000000000
                                                                                                                       RCX=0x0000000000000000
                                                                                                                       RDX=0x0000000000000001
                                                                                                                       RBX=0x00007FFFFFFFD948
=> 0x55555555519d <_Z1av+8>:    mov    rax,QWORD PTR fs:0x28                                                           ESP=0x00007FFFFFFFD7F0
   0x5555555551a6 <_Z1av+17>:   mov    QWORD PTR [rbp-0x8],rax                                                         RBP=0x00007FFFFFFFD810
   0x5555555551aa <_Z1av+21>:   xor    eax,eax                                                                         RSI=0x000055555556B270
   0x5555555551ac <_Z1av+23>:   mov    DWORD PTR [rbp-0x14],0x1                                                        RDI=0x0000000000000000
   0x5555555551b3 <_Z1av+30>:   mov    DWORD PTR [rbp-0x10],0x2                                                         R8=0x00007FFFF7C67A80
   0x5555555551ba <_Z1av+37>:   mov    DWORD PTR [rbp-0xc],0x3                                                          R9=0x0000000000000000
   0x5555555551c1 <_Z1av+44>:   mov    eax,0x0                                                                         R10=0x0000000000000000
   0x5555555551c6 <_Z1av+49>:   mov    rdx,QWORD PTR [rbp-0x8]                                                         R11=0x0000000000000287
   0x5555555551ca <_Z1av+53>:   sub    rdx,QWORD PTR fs:0x28                                                           R12=0x0000000000000000
   0x5555555551d3 <_Z1av+62>:   je     0x5555555551da <_Z1av+69>                                                       R13=0x00007FFFFFFFD958
   0x5555555551d5 <_Z1av+64>:   call   0x555555555050 <__stack_chk_fail@plt>                                           R14=0x0000555555557DA0
   0x5555555551da <_Z1av+69>:   leave                                                                                  R15=0x00007FFFF7FFD000
   0x5555555551db <_Z1av+70>:   ret                                                                                    RIP=0x000055555555519D



0x00005555555551fd in main () at ./main.cpp:20                                                                         RAX=0x0000000000000000
20          ar = a();                                                                                                  RCX=0x0000000000000000
                                                                                                                       RDX=0x0000000000000000
                                                                                                                       RBX=0x00007FFFFFFFD948
=> 0x5555555551fd <main()+33>:  mov    QWORD PTR [rbp-0x8],rax                                                         ESP=0x00007FFFFFFFD820
   0x555555555201 <main()+37>:  mov    rax,QWORD PTR [rbp-0x8]                                                         RBP=0x00007FFFFFFFD830
   0x555555555205 <main()+41>:  mov    eax,DWORD PTR [rax]                                                             RSI=0x000055555556B270
   0x555555555207 <main()+43>:  mov    esi,eax                                                                         RDI=0x0000000000000000
   0x555555555209 <main()+45>:  lea    rax,[rip+0x2e70]        # 0x555555558080 <_ZSt4cout@GLIBCXX_3.4>                 R8=0x00007FFFF7C67A80
   0x555555555210 <main()+52>:  mov    rdi,rax                                                                          R9=0x0000000000000000
   0x555555555213 <main()+55>:  call   0x555555555090 <_ZNSolsEi@plt>                                                  R10=0x0000000000000000
   0x555555555218 <main()+60>:  mov    esi,0x20                                                                        R11=0x0000000000000287
   0x55555555521d <main()+65>:  mov    rdi,rax                                                                         R12=0x0000000000000000
   0x555555555220 <main()+68>:  call   0x555555555060 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@plt>      R13=0x00007FFFFFFFD958
   0x555555555225 <main()+73>:  mov    rdx,rax                                                                         R14=0x0000555555557DA0
   0x555555555228 <main()+76>:  mov    rax,QWORD PTR [rbp-0x8]                                                         R15=0x00007FFFF7FFD000
   0x55555555522c <main()+80>:  add    rax,0x4                                                                         RIP=0x00005555555551FD
   0x555555555230 <main()+84>:  mov    eax,DWORD PTR [rax]                                                             RFL=0x0000000000000246
   0x555555555232 <main()+86>:  mov    esi,eax                                                                         CF=0 SF=0 AF=0 ZF=0 IP
   0x555555555234 <main()+88>:  mov    rdi,rdx                                                                         PF=1 OF=0 TF=0 DF=0 1
   0x555555555237 <main()+91>:  call   0x555555555090 <_ZNSolsEi@plt>                                                  ST0=0.0000000000
   0x55555555523c <main()+96>:  mov    esi,0x20                                                                        ST1=0.0000000000
   0x555555555241 <main()+101>: mov    rdi,rax                                                                         ST2=0.0000000000
   0x555555555244 <main()+104>: call   0x555555555060 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_c@plt>      ST3=0.0000000000
   0x555555555249 <main()+109>: mov    rdx,rax                                                                         ST4=0.0000000000
   0x55555555524c <main()+112>: mov    rax,QWORD PTR [rbp-0x8]                                                         ST5=0.0000000000
   0x555555555250 <main()+116>: add    rax,0x8                                                                         ST6=0.0000000000
   0x555555555254 <main()+120>: mov    eax,DWORD PTR [rax]                                                             ST7=0.0000000000
   0x555555555256 <main()+122>: mov    esi,eax                                                          0x00007FFFFFFFD820=0x0000000000000000
                                                                                                        0x00007FFFFFFFD828=0x0000000000000000
                                                                                                        0x00007FFFFFFFD830=0x0000000000000001
(gdb)
Как видите GNU C++ возвращает 0 в такой ситуации и получаем SIGSEGV. Segmentation fault.

Не надо так делать. Стоит действительно выделять память динамически и копировать данные в нее, а не возвращать адреса локальных переменных. Об этом компилятор кстати предупреждает, хотя программа и создаётся.
macomics вне форума Ответить с цитированием
Старый 07.05.2023, 18:46   #6
Женя32
Форумчанин
 
Регистрация: 12.09.2008
Сообщений: 621
По умолчанию

Цитата:
Сообщение от BDA Посмотреть сообщение
Вы ожидаете 1 2 3, а получите 4 5 6,...
Нет, я получаю не 4, 5, 6, а не понятно что, какое-то число.
13276532
Код:
 cout << ar[0] << ar[1] << ar[2];
Женя32 вне форума Ответить с цитированием
Старый 07.05.2023, 18:52   #7
macomics
Участник клуба
 
Регистрация: 17.04.2022
Сообщений: 1,833
По умолчанию

Цитата:
Сообщение от Женя32 Посмотреть сообщение
Нет, я получаю не 4, 5, 6, а не понятно что, какое-то число.
13276532
Вам уже ответили. Потому что данные в стеке затираются. Вы получаете значения, записанные на место некогда существовавшей локальной переменной b. А это может быть что угодно.
macomics вне форума Ответить с цитированием
Старый 07.05.2023, 20:21   #8
Женя32
Форумчанин
 
Регистрация: 12.09.2008
Сообщений: 621
По умолчанию

Цитата:
Сообщение от macomics Посмотреть сообщение
Потому что данные в стеке затираются. Вы получаете значения, записанные на место некогда существовавшей локальной переменной b. А это может быть что угодно.
Если я правильно после того, как срабатывает return, функция удаляется из памяти в мести со всем тем, что в ней содержится, будь то переменная или массив. То есть, функция и все ее переменные и т.д. существуют от момента ее вызова и до того, как она возвратит результат.
Я верно понимаю?
Женя32 вне форума Ответить с цитированием
Старый 07.05.2023, 20:40   #9
macomics
Участник клуба
 
Регистрация: 17.04.2022
Сообщений: 1,833
По умолчанию

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

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

Это просто в двух словах. Подробнее читайте при работу процессора и ЯП ассемблер.

Последний раз редактировалось macomics; 07.05.2023 в 20:45.
macomics вне форума Ответить с цитированием
Старый 08.05.2023, 09:47   #10
Алексей1153
фрилансер
Форумчанин
 
Регистрация: 11.10.2019
Сообщений: 942
По умолчанию

глянул этот "урок" - косяк на косяке
Алексей1153 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Проверьте решение задачи: Дан массив. Получить новый массив, выбросив из исходного массива все члены со значением max(а1 ,а2 , ... , аn). Определить число элементов нового массива. ParkerVans Паскаль, Turbo Pascal, PascalABC.NET 5 23.10.2017 19:03
Дан массив m*n. Составить массив из наименьших элементов каждой строки данного массива. Найти сумму наибольшего и наименьшего элемента массива. Miriam_ Паскаль, Turbo Pascal, PascalABC.NET 2 23.04.2017 11:18
в массиве В из 30 элементов определить диапазон значений элементов массива Zhentos Паскаль, Turbo Pascal, PascalABC.NET 9 15.06.2011 00:10
Вывод элементов массива.Изменение элементов массива. Vesnushka18 Помощь студентам 6 09.06.2011 13:05
Одномерный массив. Вывод номеров отрицательных элементов массива. RomashkaGT Помощь студентам 3 25.02.2011 08:44