Проблемы с указателями и структурами (C++)

Может кто-нибудь сказать мне, что не так со следующим кодом?

#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++ и все еще пытаюсь понять эти концепции.

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

Компилятор просто говорит, что я использовал недопустимую инструкцию.

Указатели должны указывать на что-то, прежде чем вы их используете. key0 в p ни на что не указывает.

Yksisarvinen 03.05.2024 12:19

Обычно хорошей идеей является предоставить дословное сообщение компилятора/среды выполнения с точной строкой/обстоятельством соответственно, особенно если вы новичок: это может означать не то, что вы думаете.

Abstraction 03.05.2024 12:20

Странно то, что компилятор на самом деле ничего мне не сказал, он просто ничего не вернул.

deomanu01 03.05.2024 12:29

Кроме того, вам не нужно типизировать структуры в C++. Просто используйте point в качестве типа.

iwarv 03.05.2024 12:39

Странно то, что компилятор мне фактически ничего не сказал. -- В этом нет ничего странного. Вы несете ответственность за то, чтобы указатели куда-то указывали. C++ — это не тот язык, который требует много ручного управления.

PaulMcKenzie 03.05.2024 12:39

Компилятор просто говорит, что я использовал недопустимую инструкцию. -- Ошибку выдает не компилятор. Ошибка выдается при запуске вашей программы. Успешная компиляция (и компоновка) означает, что ваша программа была успешно собрана, и ваша программа была успешно собрана. Запуск успешно построенной программы – это нечто совершенно иное.

PaulMcKenzie 03.05.2024 12:43

Почему вы делаете typedef struct point на C++?

Eljay 03.05.2024 14:48

@deomanu01 «Странно то, что компилятор на самом деле ничего мне не сказал» - это потому, что компилятор заботится только о правильности синтаксиса кода, а не о логике кода. Ваш код «синтаксически» правильный, но «логически» неправильный. Логические ошибки не проявляются до времени выполнения. Вы получаете ошибку времени выполнения ОС при попытке доступа к памяти, которой вы не владеете.

Remy Lebeau 03.05.2024 17:00

В C++ typedef struct не нужен. В качестве типа можно использовать имя struct.

Thomas Matthews 04.05.2024 01:51

Вы выделяете переменные из динамической памяти, когда в этом нет необходимости.

Thomas Matthews 04.05.2024 01:52
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
10
142
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ваша проблема в том, что вы никогда не инициализируете ключ 0, поэтому он просто указывает на случайную память.

Вам необходимо инициализировать указатели, чтобы они указывали на выделенные вами объекты.

Ответ принят как подходящий

Эта строка:

pnt * p = new pnt;

Выделяет структуру pnt, но не инициализирует ее содержимое. Адрес выделенной структуры присваивается p.

В этой строке:

p->key = 1;

Вы инициализируете поле key.

Но на данный момент поле key0 все еще не инициализировано.

Но в последней строке:

p->key0->key = 2;

Вы пытаетесь разыменовать неинициализированное поле key0 (с помощью key0->key). Это вызывает неопределенное поведение.

Чтобы избежать этого, вам необходимо инициализировать key0, либо выделив другую структуру pnt и используя ее адрес, либо используя адрес существующего объекта pnt.

Боковые примечания:

  1. В C++ обычно лучше избегать использования raw new.
    Для начала требуется delete (без него вы получите утечку памяти - как в вашем коде).
    Кроме того, есть альтернативы получше, например использование умных указателей или, в других случаях, использование стандартных контейнеров.

  2. Лучше избегать using namespace std;. См. здесь.

  3. В C++ нет необходимости использовать typedef для создания типа структуры (это распространено в C). Вы можете просто использовать:

    struct pnt{
        ...
        pnt * key0;
    };
    

Что касается примечаний: умные указатели не являются панацеей (хотя я подозреваю, что они здесь будут уместны). Более того, вся эта история с typedef struct и struct point* пахнет C; в C++ struct и class достаточно без typedef, и вы можете использовать имена напрямую, не предваряя их ключевым словом struct.

James Kanze 03.05.2024 13:19

@JamesKanze Я согласен, что умные указатели - это не волшебные пули, но они могут творить немало чудес, избегая утечек памяти ;-). По поводу typedef - добавил примечание.

wohlstad 03.05.2024 13:24

@wohlstad Настоящая проблема — неинициализированный указатель. В его маленьком примере ему повезло, и он сразу же вылетел. Инициализация указателя либо с помощью интеллектуального указателя, либо путем добавления = nullptr к его объявлению могла бы решить эту проблему — формально это все еще неопределенное поведение, но на практике это приведет к немедленному сбою, из-за которого отладчик может получить ошибку. действительный обратный след. Неинициализированный указатель может и часто приводит к таким вещам, как перезапись стека, что приводит к сбою намного позже, и отладчик не может выполнить обратную трассировку. Веселье.

James Kanze 03.05.2024 17:50

Другие вопросы по теме