Может кто-нибудь сказать мне, что не так со следующим кодом?
#include <iostream>
using namespace std;
typedef struct point{
int key;
struct point * key0;
} pnt;
int main() {
pnt * p = new pnt;
p->key = 1;
p->key0->key = 2;
}
Я новичок в C++ и все еще пытаюсь понять эти концепции.
Я ожидал, что смогу установить произвольное значение для определенной мной структуры. Я просто экспериментировал, если понимаю, как это работает.
Компилятор просто говорит, что я использовал недопустимую инструкцию.
Обычно хорошей идеей является предоставить дословное сообщение компилятора/среды выполнения с точной строкой/обстоятельством соответственно, особенно если вы новичок: это может означать не то, что вы думаете.
Странно то, что компилятор на самом деле ничего мне не сказал, он просто ничего не вернул.
Кроме того, вам не нужно типизировать структуры в C++. Просто используйте point
в качестве типа.
Странно то, что компилятор мне фактически ничего не сказал. -- В этом нет ничего странного. Вы несете ответственность за то, чтобы указатели куда-то указывали. C++ — это не тот язык, который требует много ручного управления.
Компилятор просто говорит, что я использовал недопустимую инструкцию. -- Ошибку выдает не компилятор. Ошибка выдается при запуске вашей программы. Успешная компиляция (и компоновка) означает, что ваша программа была успешно собрана, и ваша программа была успешно собрана. Запуск успешно построенной программы – это нечто совершенно иное.
Почему вы делаете typedef struct point
на C++?
@deomanu01 «Странно то, что компилятор на самом деле ничего мне не сказал» - это потому, что компилятор заботится только о правильности синтаксиса кода, а не о логике кода. Ваш код «синтаксически» правильный, но «логически» неправильный. Логические ошибки не проявляются до времени выполнения. Вы получаете ошибку времени выполнения ОС при попытке доступа к памяти, которой вы не владеете.
В C++ typedef struct
не нужен. В качестве типа можно использовать имя struct
.
Вы выделяете переменные из динамической памяти, когда в этом нет необходимости.
Ваша проблема в том, что вы никогда не инициализируете ключ 0, поэтому он просто указывает на случайную память.
Вам необходимо инициализировать указатели, чтобы они указывали на выделенные вами объекты.
Эта строка:
pnt * p = new pnt;
Выделяет структуру pnt
, но не инициализирует ее содержимое. Адрес выделенной структуры присваивается p
.
В этой строке:
p->key = 1;
Вы инициализируете поле key
.
Но на данный момент поле key0
все еще не инициализировано.
Но в последней строке:
p->key0->key = 2;
Вы пытаетесь разыменовать неинициализированное поле key0
(с помощью key0->key
). Это вызывает неопределенное поведение.
Чтобы избежать этого, вам необходимо инициализировать key0
, либо выделив другую структуру pnt
и используя ее адрес, либо используя адрес существующего объекта pnt
.
Боковые примечания:
В C++ обычно лучше избегать использования raw new
.
Для начала требуется delete
(без него вы получите утечку памяти - как в вашем коде).
Кроме того, есть альтернативы получше, например использование умных указателей или, в других случаях, использование стандартных контейнеров.
Лучше избегать using namespace std;
. См. здесь.
В C++ нет необходимости использовать typedef
для создания типа структуры (это распространено в C). Вы можете просто использовать:
struct pnt{
...
pnt * key0;
};
Что касается примечаний: умные указатели не являются панацеей (хотя я подозреваю, что они здесь будут уместны). Более того, вся эта история с typedef struct
и struct point*
пахнет C; в C++ struct
и class
достаточно без typedef, и вы можете использовать имена напрямую, не предваряя их ключевым словом struct
.
@JamesKanze Я согласен, что умные указатели - это не волшебные пули, но они могут творить немало чудес, избегая утечек памяти ;-). По поводу typedef
- добавил примечание.
@wohlstad Настоящая проблема — неинициализированный указатель. В его маленьком примере ему повезло, и он сразу же вылетел. Инициализация указателя либо с помощью интеллектуального указателя, либо путем добавления = nullptr
к его объявлению могла бы решить эту проблему — формально это все еще неопределенное поведение, но на практике это приведет к немедленному сбою, из-за которого отладчик может получить ошибку. действительный обратный след. Неинициализированный указатель может и часто приводит к таким вещам, как перезапись стека, что приводит к сбою намного позже, и отладчик не может выполнить обратную трассировку. Веселье.
Указатели должны указывать на что-то, прежде чем вы их используете.
key0
вp
ни на что не указывает.