LINUX.ORG.RU

Массив в классе и странности

 , ,


0

2

Всем привет. Давно не писал на C++, захотелось вспомнить этот язык. Накатал следующий класс.

plane.hpp

class Plane
{
    public:
        Plane(int planetype);
        float *bodyx;
};

plane.cpp

#include "plane.hpp"
float bx[][6] = {{0, -5, -6, 0, 6, 5}, {1, 1, 1}};

Plane::Plane(int planetype)
{
    cout << sizeof(bx[planetype]) << " " << sizeof(bx[planetype][0]) << " " << sizeof(bx[planetype]) / sizeof(bx[planetype][0]) << endl;
    for(unsigned int k=0; k<sizeof(bx[planetype])/sizeof(bx[planetype][0]); k++)
    {
        bodyx[k] = bx[planetype][k];
        cout << bodyx[k] << " ";
    }
    cout << endl;
}

main.cpp

#include "plane.hpp"
int main(int argc, char* args[])
{
    Plane* player = new Plane(0);
    cout << sizeof(player->bodyx) << " " <<  sizeof(player->bodyx[0]) << " " << sizeof(player->bodyx) / sizeof(player->bodyx[0]) << endl;
    for(unsigned int k=0; k<sizeof(player->bodyx)/sizeof(player->bodyx[0]); k++)
    {
        cout << player->bodyx[k] << " ";
    }
    cout << endl;
}

Вывод:

24 4 6
0 -5 -6 0 6 5 
8 4 2
0 -5 
Ошибка сегментирования (стек памяти сброшен на диск)

То есть в конструкторе в массив благополучно перешли все 6 значений. А куда же по дороге делись 4 из них и что я делаю не так?

Ты не выделяешь память под bodyx в конструкторе.

Если говоришь, что давно не писал на C++, то увидишь, что инструменты стали лучше — добавили различные санитайзеры, например:

clang++ -g -fsanitize=address -fsanitize=leak -fsanitize=undefined  test.cpp

Хорошо бьет по рукам, и указывает где именно ошибка или утечка (но во время выполнения программы).

anonymous
()

Выкинь нафиг указатели и никогда так не пиши!

Ты в десяти строках запутался. Представь что будет если их миллион.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от invy

Выкинь нафиг указатели и никогда так не пиши!

Человек просто вспоминает основы языка. Нравится тебе это или нет, но указатели — это основа основ C++, вне зависимости от того, что ты там вычитал в своей засаленной хипстерской методичке, умник.

anonymous
()

че-то какая-то дичь, ты если работаешь с динамической памятью,так и работай с ней , что это такое

float bx[][6] = {{0, -5, -6, 0, 6, 5}, {1, 1, 1}};
еще и инициализация странная, почему у тебя второй элемент состоит из 3х значений если в описании 6 ? у тебя уже автоматом выделилось памяти с гавном под 12 элементов,а ты инициализировал только 9. ты sizeof(bx) проверял?

jo_b1ack ★★★★★
()
Ответ на: комментарий от invy

Так никто больше не пишет.

Всех программистов C++ уже опросил? А то, сдается мне, что ты всего лишь имел в виду свое село и близлежащие территории.

anonymous
()

Ты пишешь в неинициированный указатель. Результат такой операции — UB, чего ты и наблодаешь. Делать надо было либо так:

#include <iostream>
#include <cstdint>

#include "plane.hpp"

static float bx[][6] = {{0, -5, -6, 0, 6, 5}, {1, 1, 1}};

using namespace std;

Plane::Plane(int planetype)
{
    cout << sizeof(bx[planetype]) << " " << sizeof(bx[planetype][0])
         << " " << sizeof(bx[planetype]) / sizeof(bx[planetype][0]) << endl;
         
    bodyx = new float[2 * 6];
         
    size_t limit = sizeof(bx[planetype])/sizeof(bx[planetype][0]);
    for(size_t k = 0; k < limit; k++)
    {
        bodyx[k] = bx[planetype][k];
        cout << bodyx[k] << " ";
    }
    cout << endl;
}

Plane::~Plane()
  { delete[] bodyx; }

Либо так:

#include <iostream>
#include <cstdint>

#include "plane.hpp"

static float bx[][6] = {{0, -5, -6, 0, 6, 5}, {1, 1, 1}};

using namespace std;

Plane::Plane(int planetype)
{
    cout << sizeof(bx[planetype]) << " " << sizeof(bx[planetype][0])
         << " " << sizeof(bx[planetype]) / sizeof(bx[planetype][0]) << endl;
         
    bodyx = bx[planetype];
         
    size_t limit = sizeof(bx[planetype])/sizeof(bx[planetype][0]);
    for(size_t k = 0; k < limit; k++)
    {
        cout << bodyx[k] << " ";
    }
    cout << endl;
}

XMs ★★★★★
()
Последнее исправление: XMs (всего исправлений: 1)
Ответ на: комментарий от anonymous

Всех программистов C++ уже опросил? А то, сдается мне, что ты всего лишь имел в виду свое село и близлежащие территории.

Скорее всего еще не всех.

Давай опрашивать.

Лично я тоже считаю, что указатели нужно использовать только в самых крайних случаях. Ибо ошибка при работе с указателями очень легко может привести к segmentation fault и подобным (как, собственно, и произошло). C++ тем и хорош, что в 99% процентах позволяет указатели не использовать. Например, в примере ТС достаточно std::vector.

Пусть ТС это знает.

Аноним, ты считай статистику, прибавляй единичку каждый раз, когда такое услышишь, и отнимай единичку, когда скажут противоположное. А я скажу страшные слова. Пореже использовать указатели в C++ - это best practice.

Kroz ★★★★★
()
Последнее исправление: Kroz (всего исправлений: 1)
std::vector<std::vector<float>> bx = {{0, -5, -6, 0, 6, 5}, {1, 1, 1}};

class Plane
{
    public:
        Plane(int planetype) :
          bodyx(bx[planetype])
        {
        };
        std::vector<float> bodyx;
};


Как-то так.

invy ★★★★★
()
Ответ на: комментарий от invy

Ещё лучше в конструкторе предавать const std::vector<float>&

invy ★★★★★
()

float *bodyx
sizeof(bx[planetype][0])

Ну вот, зачем?

crutch_master ★★★★★
()
Ответ на: комментарий от jo_b1ack

почему у тебя второй элемент состоит из 3х значений если в описании 6 ?

Потому что, в зависимости от аргумента для конструктора, я в bodyx закидываю нужное число значений, хоть 6, хоть 26. Строчку целиком. Получается, нужно либо выбирать набором if ... else, либо сделать массив с «неровным краем». Я придумал только 2 таких варианта.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от Kroz

C++ тем и хорош, что в 99% процентах позволяет указатели не использовать.

Да мне от плюсов в этом примере нужны были только классы. Если бы я хотел максимально отодвинуться от реализации, так я бы взял Python, например, и не задавал глупых вопросов :)

Например, в примере ТС достаточно std::vector.

Вариант, кстати. По-моему, не писал с векторами до этого.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от XMs

bodyx = new float[2 * 6];

Вот как раз new я вчера и не попробовал, кажется, единственный вариант. Только new float[limit], что ли, потому что нужна одна строчка из bx.

bodyx = bx[planetype];

А вот так пробовал, и получается неправильно:

24 4 6
0 -5 -6 0 6 5 
8 4 2
0 -5 
Хотя сегфолта в конце не происходит.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

new float[limit]

Хм, и с new получается та же ерунда: сначала 24 байта, потом 8.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

Массив с «неровным краем» это либо ты хранишь количество элементов где-то снаружи, либо используешь вектор. У массива из 6 элементов всегда будет размер 6 сколько бы ты значений туда не пытался положить.

xpahos ★★★★★
()

Я посмотрел твой код внимательно. Ты не выделил память для использования bodyx[k]

Поэтому строчка bodyx[k] = bx[planetype][k]; будет приводить к падению программы.

Выше XMs тебе правильно ответил. Про использование new в конструкторах можно почитать в книге «Стивен Прата. Язык программирования C++. 6-ое издание» в 12 главе.

neon1ks ★★
()
Ответ на: комментарий от xpahos

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

Ну, 6 я указываю, пока у меня максимум 6 элементов в «строчке». Было бы 26 - указал бы 26, и так далее. Или я неправильно запомнил это дело?

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от jo_b1ack

у тебя уже автоматом выделилось памяти с гавном под 12 элементов,а ты инициализировал только 9.

Последние 3 будут инициализированными нулями, раз для них нет инициализатора. Здесь нет ошибки.

xaizek ★★★★★
()
Ответ на: комментарий от al_exquemelin

Я сейчас посмотрел внимательнее, и мне стало вот что непонятно: зачем ты делишь sizeof(player->bodyx) ( == sizeof(float*)) на sizeof(player->bodyx[0]) ( == sizeof(float))? Указатель ничего не знает про размер выделенной памяти, sizeof(float*) == sizeof(intptr_t).

Какую задачу ты в принципе пытаешься решить? Мне кажется, так будет проще

XMs ★★★★★
()
Последнее исправление: XMs (всего исправлений: 1)
Ответ на: комментарий от XMs

И правда) Вот ещё одна классическая проблема. Извращенный вариант. Обычно эту тему рассматривают, когда массив передают в функцию.

neon1ks ★★
()
Ответ на: комментарий от XMs

sizeof(player->bodyx) ( == sizeof(float*)) на sizeof(player->bodyx[0]) ( == sizeof(float))

Ах вот оно что. Я подозревал, что ошибка при второй печати, теперь дошло.

Какую задачу ты в принципе пытаешься решить?

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

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

Да мне от плюсов в этом примере нужны были только классы.

ИМХО, если писать на C++, то минимум что нужно использовать это:
- Классы
- Standard Template Library (STL) - хотябы std::vector
- std::string - упрощает работу со строками
- потоки - cin/cout/cerr/clog, fstream, stringstream и т. п. - упрощает ввод/вывод.
- исключения - они просто везде в библиотеках зашиты, у тебя в программе они будут даже если ты о них не думаешь.
- тип auto и "Range-based for" - вспомни про них когда надоест писать монструозные конструкции с итераторами при работе с std::vector и другими контейнерами.

А когда свыкнешься с вышеупомянутым, обязательно к просмотру Jon Kalb Exception-Safe Code часть 1, часть 2.

Kroz ★★★★★
()
Ответ на: комментарий от al_exquemelin

Если уж ты решил, что тебе нужно все преаллоцировать на старте и больше не изменять размер, то используй std::array. У него хотя бы size есть.

xpahos ★★★★★
()
Ответ на: комментарий от anonymous

Тот редкий случай когда соглашаюсь и неистово плюсую анонима. Мне недавно один семнадцатый с++ программист заявил что функции бывают только в С, а в С++ только классы. Это глупо утверждать что поинтеры зло... Тупость, недалекость и незнание инструмента - вот истинный корень всего плохого кода...

БТВ поинтерные сегфолты в С++ теперь называются динамические ассерты...

Jetty ★★★★★
()
Ответ на: комментарий от Jetty

БТВ поинтерные сегфолты в С++ теперь называются динамические ассерты

Што?

anonymous
()
Ответ на: комментарий от anonymous

потоки - cin/cout/cerr/clog, fstream, stringstream и т. п. - упрощает ввод/вывод.

Перетолстил.

Если нужно просто вывести строку и пару чисел, потоки намного проще чем printf. Если форматирование, тут, да, можно поспорить.

Kroz ★★★★★
()
Ответ на: комментарий от XMs

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

std::vector<double> v;
double* a = &v[0];

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от Jetty

Тупость, недалекость и незнание инструмента - вот истинный корень всего плохого кода...

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

БТВ поинтерные сегфолты в С++ теперь называются динамические ассерты...

«Будьте добры помедленнее, я записываю» xD

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

тем более что пишут, можно делать так при необходимости

Так делать точно не надо, иначе можешь получить очень труднообнаруживаемый сегфолт в рандомном месте. В C++ голые указатели в принципе мало когда оказываются нужны, почти всегда есть способ обойтись без них и получить во много раз меньше проблем

XMs ★★★★★
()
using bx_t = float;
static bx_t bx[][6] = {{0, -5, -6, 0, 6, 5}, {1, 1, 1}};

class Plane {
public:
  Plane(int planetype);
  bx_t *data() noexcept;
  vector<bx_t> bodyx;
};

Plane::Plane(int planetype) {
  size_t k = sizeof(bx[planetype]) / sizeof(bx_t);
  bodyx = vector<bx_t>(bx[planetype], bx[planetype] + k);
}

bx_t *Plane::data() noexcept { return bodyx.data(); }

как-то так

anonymous
()
Ответ на: комментарий от invy

Выкинь нафиг указатели и никогда так не пиши!

Ты в десяти строках запутался. Представь что будет если их миллион.

Тогда он опять запутается в десяти строках, всё нормально.

Virtuos86 ★★★★★
()
Ответ на: комментарий от XMs

Так делать точно не надо, иначе можешь получить очень труднообнаруживаемый сегфолт в рандомном месте.

А что мне остаётся, если внешней функции нужны const Sint16 *, а у меня вместо них два вектора? Придётся как-то переходить от одного к другому.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от XMs

И array можно будет напрямую подсунуть в качестве аргумента, без преобразования? Ладно, посмотрю, чем одно от другого отличается.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

Придётся как-то переходить от одного к другому.

Пишешь нормально, а далее пишешь оператор const Sint16 * и ты сможешь передавать свой тип куда угодно, где требуется const Sint16 *.

захотелось вспомнить этот язык

Ты вспомнил явно не тот язык.

onlyd2
()
Ответ на: комментарий от jo_b1ack

еще и инициализация странная, почему у тебя второй элемент состоит из 3х значений если в описании 6 ? у тебя уже автоматом выделилось памяти с гавном под 12 элементов,а ты инициализировал только 9. ты sizeof(bx) проверял?

Эксперты в треде? Сходи и почитай букварь по языку, о котором пытаешься что-то говорить.

ты инициализировал только 9

Нет. Сообщаю новость - в С/С++ инициализатором является инициализатор. Он всегда полностью инициализирует объект. Что ты туда передаёшь - это лишь дополнение.

(float[6]){1, 1, 1} - полноценный объект, (float[6]){} - тоже, (float[6]){1} - тоже.

onlyd2
()
Ответ на: комментарий от onlyd2

Ты вспомнил явно не тот язык.

Это я учил не этот язык, а по стандарту 98-го (вроде) года. Или ты к тому, что с нынешним количеством библиотек, надстроек и обёрток проще сразу учить какой-нибудь Rust (или что там ещё выросло за последние годы)?

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

Это я учил не этот язык, а по стандарту 98-го (вроде) года.

Про это. Нужно всё забыть и учить заново.

Или ты к тому, что с нынешним количеством библиотек, надстроек и обёрток проще сразу учить какой-нибудь Rust (или что там ещё выросло за последние годы)?

Нет. Ничего не выросло, а писать лучше на актуальном языке.

Rust

Это скриптуха С++ в принципе заменить не может.

onlyd2
()
Ответ на: комментарий от onlyd2

Это скриптуха С++ в принципе заменить не может.

Вообще пишут, что он компилируемый, но без ООП в привычном виде. Ну да ладно, я всё равно не знаком с ним.

al_exquemelin ★★★
() автор топика
Ответ на: комментарий от al_exquemelin

Вообще пишут, что он компилируемый

Компилируется сейчас вся скриптуха. Даже пхп.

onlyd2
()
Ответ на: комментарий от al_exquemelin

У его величества острая реакция на ключевое слово rust. Его шизофрению лучше игнорировать. А так rust не позволил бы допустить все ошибки выше. Но рекомендовать его изучать сложно, слишком высокий порог входа, намного выше чем в с++.

anonymous
()
Ответ на: комментарий от anonymous

А так rust не позволил бы допустить все ошибки выше.

Враньё. Пхп позволил бы не допустить ошибки.

слишком высокий порог входа, намного выше чем в с++.

Да, в пхп так же. Правда почему-то адептов дохрена. Но так всегда бывает, когда порог входа большой, а каждый первый уже вошел.

onlyd2
()
Ответ на: комментарий от Jetty

Мне недавно один семнадцатый с++ программист

Это мб студент был, которому только в этом семестре плюсы показали?=)

Dudraug ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.