Переименование переменной дает разные результаты

#c #struct

#c #структура

Вопрос:

Я написал простую функцию, которая находит самого старого пользователя в массиве структур. В структуре хранится информация о возрасте и имени.

 #include <iostream>

struct Person
{
    int age;
    char name[16];
};

char* oldest(Person* arr, int len)
{
    int max = 0;
    char* ptr = nullptr;
    Person elem;
    for (int i = 0; i < len; i  )
    {
        elem = arr[i];
        if (max < elem.age)
        {
            max = elem.age;
            ptr = arr[i].name;
        }
    }
    return ptr;
}

int main()
{
    Person list[3] = {
        {20, "Alice"},
        {70, "Bob"},
        {25, "James"}
    };
    std::cout << oldest(list, 3) << 'n';
}
 

Это дает правильный результат (а именно я вижу Bob на экране), но когда я использую elem вместо arr[i] в строке ptr = arr[i].name; (что не что иное, как присвоение другого имени arr[i] , верно ??) программа внезапно начинает выдавать какие-то странные результаты (непечатаемые символы). Я понятия не имею, почему он так себя ведет.
Для справки, это код, который не работает:

 char* oldest(Person* arr, int len)
{
    int max = 0;
    char* ptr = nullptr;
    Person elem;
    for (int i = 0; i < len; i  )
    {
        elem = arr[i];
        if (max < elem.age)
        {
            max = elem.age;
            ptr = elem.name;
        }
    }
    return ptr;
}

 

Комментарии:

1. пожалуйста, опубликуйте код, в котором есть проблема, а не тот, который в порядке.

2. @largest_prime_is_463035818 ок

3. Person elem; это «копия», которую вы хотите /*const*/Personamp; elem = arr[i]; использовать в качестве псевдонима.

4. elem = arr[i] присваивается elem как копия arr[i] . But elem является локальной переменной с автоматической продолжительностью хранения, поэтому она (и ее содержимое) перестают существовать при возврате функции. Если ptr указывает на (первый символ) elem.name , вызывающий получает висячую ссылку, то есть указатель на что-то, чего больше не существует. Разыменование этого указателя вызывающим объектом (например, доступ к массиву, который больше не существует) приводит к неопределенному поведению. Для сравнения, arr передается вызывающей стороной, поэтому все еще существует после возврата функции.

Ответ №1:

ptr = elem.name; присваивает ptr адрес первому элементу elem.name (поскольку архив elem.name автоматически преобразуется в указатель на его первый элемент). elem.name конечно, это внутренний массив elem и elem объект с автоматическим сроком хранения, что означает, что он создается автоматически в блоке, в котором он определен, и уничтожается при завершении выполнения этого блока. Итак, когда функция возвращает, elem перестает существовать в модели вычислений C , и указатель на ее часть становится недействительным.

… это не что иное, как присвоение другого имени arr[i], верно??

Нет, в заявлении elem = arr[i]; содержится копия arr[i] elem В. Это не создает elem альтернативного имени для arr[i] . Эта копия перестает существовать, когда функция возвращается.

Если бы вы удалили Person elem; объявление и внутри цикла использовали Person amp;elem = arr[i]; вместо elem = arr[i]; , это определило elem бы как ссылку на arr[i] . Тогда это было бы фактически альтернативным именем для arr[i] , и ptr = elem.name; было бы установлено ptr , чтобы указывать на первый элемент arr[i].name .

Ответ №2:

(это не что иное, как присвоение другого имени arr[i], верно??)

Не правильно. elem это отдельный объект. Это не имя arr[i] .

но когда я использую elem вместо arr[i] в строке ptr = arr[i].name; … программа внезапно начинает выдавать какие-то странные результаты

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