Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.54/13: Рейтинг темы: голосов - 13, средняя оценка - 4.54
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712

Почему тут UB?

28.02.2020, 08:59. Показов 3215. Ответов 40
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую всех. Есть такой код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
 
typedef struct {
  int a; int b;
} X;
 
int main()
{
  X* x = (X*)malloc(sizeof(X));
  if (!x) return 1;
 
  x->a = 111;
  x->b = 222;
 
  printf("x->a = %d\n", x->a);
  printf("x->a = %d\n", x->b);
 
  free(x);
  return 0;
}
Говорят, что он корректен в С, но в С++ ведет к неопределенному поведению. Почему он корректен в С понятно, но объясните, почему в С++ это UB?
0
Лучшие ответы (1)
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
28.02.2020, 08:59
Ответы с готовыми решениями:

Почему тут ошибка
Всем привет! Не могу понять, почему возникает ошибка: class Vector { int values; public Vector(int nDim) {

Почему тут разный вывод?
#include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; int main() { for(int i = 0, j = 20; i &lt; 20, j &gt; 10; i++, j--) { printf(&quot;%d...

Можете объяснить, почему тут NPE
Добрый день В программировании новичок. Программа, которая должна вывести после сортировки массива индекс элементов до...

40
248 / 70 / 9
Регистрация: 22.07.2018
Сообщений: 321
28.02.2020, 18:36
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Vourhey Посмотреть сообщение
Уже есть proposal для исправления этого. И в нем описаны причины в том числе. Почитай:
P0593R5
Последняя ревизия пропозала это P0593R6 и оно уже смёржено в драфт C++20.
Цитата Сообщение от Vourhey Посмотреть сообщение
Хотя я не встречал живьем компилятора, в котором бы это приводило к UB.
Это приводило к UB абсолютно в любом компиляторе. Лол.
Или пока нет сегфолта то UB это не UB?
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
28.02.2020, 21:39
Цитата Сообщение от Azazel-San Посмотреть сообщение
Потому, что malloc не начинает жизнь объекта:
UB-то в чём заключается, для pod-типа?

Добавлено через 33 минуты
Кстати, если делать как в том видео, вызывать new для памяти выделенной по malloc, то там объект будет проинициализирован нулями, конструктором по-умолчанию (в майкросовфте, во всяком случае)
C++
1
2
3
4
5
6
7
8
9
10
        struct ssss
        {
            int x;
        };
 
        ssss* p = (ssss*)malloc(sizeof(ssss));
        std::cout << p->x << std::endl;
 
        ssss* p2 = new (malloc(sizeof(ssss))) ssss();
        std::cout << p2->x << std::endl;
Вот это точно UB - никто не просил ничего заполнять

Добавлено через 11 минут
Хотя, если без скобок, то нормально
C++
1
ssss* p2 = new (malloc(sizeof(ssss))) ssss;
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
28.02.2020, 22:04
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
никто не просил ничего заполнять
Вы попросили.

Скобки - это value initialization. Никакого UB.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
28.02.2020, 22:50
Цитата Сообщение от DrOffset Посмотреть сообщение
Вы попросили.
Скобки - это value initialization. Никакого UB.
У меня там класс, в котором я никаких скобок не вызываю. Собственно именно для того, чтоб ничего не инициализировалось.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
28.02.2020, 23:01
Цитата Сообщение от Azazel-San Посмотреть сообщение
Потому, что malloc не начинает жизнь объекта:
ну и что?

Цитата Сообщение от Vourhey Посмотреть сообщение
Имеющий глаза да увидит. А слепой сытому не товарищ.
ты в состоянии объяснить в чем проблема?

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
вызывать new для памяти выделенной по malloc
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
ssss* p2 = new (malloc(sizeof(ssss))) ssss;
на мой взгляд: слегка отдаёт наркоманией.

из серии: как на ровном месте создать себе проблему,
и потом начать её героически преодолевать.

DrOffset,
можешь объяснить: в чем выражаеццо UB?
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
28.02.2020, 23:07
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
У меня там класс, в котором я никаких скобок не вызываю.
Наличие в классе конструктора на это никак не влияет. Выражение инициализации имеет значение. Со времен С++03 вот этот код:
C++
1
ssss s = ssss();
Инициализирует поле ssss нулем.
Также как и этот код
C++
1
ssss s = {};
Ссылочку мою внимательно прочитайте, пожалуйста.

Цитата Сообщение от hoggy Посмотреть сообщение
можешь объяснить: в чем выражаеццо UB?
Не хочу.
Поберегу нервы свои.
2
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 11:07
Цитата Сообщение от DrOffset Посмотреть сообщение
Наличие в классе конструктора на это никак не влияет. Выражение инициализации имеет значение. Со времен С++03 вот этот код:
Где ты там увидел у меня оператор = ?
В общем, вот код
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdlib.h>
#include <stdio.h>
#include <cstring>
#include <chrono>
#include <memory>
template <typename T, typename... TT>
T *init(void *p, TT&&... args) noexcept(std::is_nothrow_constructible_v<T, TT...>)
{
    return new (p) T(std::forward<TT>(args)...);
}
 
int main()
{
    struct test
    {
        //test() {;}
        //test() = default;
        size_t m_sz;
        char m_data[1024 * 1024 * 1024];
    };
 
    //static_assert(std::is_trivially_constructible_v<test>);
    static_assert(std::is_trivially_destructible_v<test>);
 
    auto *raw = malloc(sizeof(test));
    if (!raw)
        return -1;
 
    const auto tm = std::chrono::steady_clock::now();
 
    size_t ln = 0;
    for (size_t i = 0; i < 1'000'000; ++i)
    { 
        auto* p = new (raw) test; // ();
        //auto* p = init<test>(raw);
        p->m_sz = 0;
        p->m_data[0] = 0;
        p->m_data[1] = 1;
        ln = strlen(p->m_data);
        std::destroy_at(p);
    }
 
    const auto dt = std::chrono::steady_clock::now() - tm;
    printf("%lu, dt: %ld\n", ln, std::chrono::duration_cast<std::chrono::microseconds>(dt).count());
    free(raw);
    return 0;
}

При добавлении скобок в оператор new, я не смог дождаться окончания его работы. Без них отрабатывет ~1ms. Т.е. простое добавление скобок может увеличить сложность алгоритма, без всяких усилий мс твоей стороны.
Т.е. нет возможности определить поведение своего класса на момент декларации. Если добавляешь конструктор test() {;}, всё работает нормально, но он перестаёт быть is_trivially_constructible_v.
И это никак не отследить- никаких ворнингов, ничего. PVS тоже молчит.
И скобки для new, это полбеды. Беда в том, что они могут быть вызваны неявно, в шаблоне - случай с init().
Непонятно, нахрена компилятор генерит конструктор по-умолчанию для класса, где это не требуется?

Добавлено через 14 минут
И, главное, что меня интересует - как от таких просёров застраховаться в compile-time?
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 13:23
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Где ты там увидел у меня оператор = ?
При чем тут оператор= ?
Надо в правую часть смотреть.
И в ссылку, которую я дал.



Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Т.е. нет возможности определить поведение своего класса на момент декларации. Если добавляешь конструктор test() {;}, всё работает нормально, но он перестаёт быть is_trivially_constructible_v.
И это никак не отследить- никаких ворнингов, ничего. PVS тоже молчит.
И скобки для new, это полбеды. Беда в том, что они могут быть вызваны неявно, в шаблоне - случай с init().
Непонятно, нахрена компилятор генерит конструктор по-умолчанию для класса, где это не требуется?
Все молчат, потому что это правила языка. Если ты пишешь код по правилам языка, то закономерно не получаешь ни ворнингов, ни криков PVS.
1
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 14:03
Цитата Сообщение от DrOffset Посмотреть сообщение
Все молчат, потому что это правила языка. Если ты пишешь код по правилам языка, то закономерно не получаешь ни ворнингов, ни криков PVS.
Интересно, откуда это правило пошло? В си такого вроде не было.
Потому что выглядит оно, как откровенный просёр - как то сложно интуитивно понять, почему вот такой код приведёт к генерации memset
C++
1
2
3
4
5
6
7
    struct test
    {
        //test() {;}
        test() = default;
        size_t m_sz = 0;
        char m_data[1024 * 1024 * 1024];
    };
А вот такой - нет
C++
1
2
3
4
5
6
7
    struct test
    {
        test() {;}
        //test() = default;
        size_t m_sz = 0;
        char m_data[1024 * 1024 * 1024];
    };
Класс здесь даже не является pod-типом.
Это источник ошибок, вернее проблем с производительностью.
Сижу, башку ломаю, как их избежать. Единственное, что пока придумал - запретить делать test() = default;
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 14:17
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Сижу, башку ломаю, как их избежать.
Чего избежать-то? Инициализации? Ну так не делай ее.

Добавлено через 5 минут
C++
1
2
3
4
5
6
7
8
9
10
template <typename T>
auto init(void * p) -> std::enable_if_t<std::is_trivially_constructible_v<T>, T *>
{
    return new (p) T;
}
template <typename T>
auto init(void * p) -> std::enable_if_t<!std::is_trivially_constructible_v<T>, T *>
{
    return new (p) T{};
}
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 14:17
Цитата Сообщение от DrOffset Посмотреть сообщение
Чего избежать-то? Инициализации? Ну так не делай ее.
А где я здесь делаю инициализацию?
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 14:18
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
А где я здесь делаю инициализацию?
Вот здесь:
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
C++
1
2
3
4
5
template <typename T, typename... TT>
T *init(void *p, TT&&... args) noexcept(std::is_nothrow_constructible_v<T, TT...>)
{
    return new (p) T(std::forward<TT>(args)...); // <------ Вот здесь
}
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 14:18
Цитата Сообщение от DrOffset Посмотреть сообщение
auto init(void * p) -> std::enable_if_t<!std::is_trivially_cons tructible_v<T>, T *>
{
Так это ещё и в emlpace и в make_shared и т.д. стреляет
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 14:22
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Так это ещё и в emlpace и в make_shared и т.д. стреляет
Если знаешь семантику, то ничего не стреляет.
Хочешь засовывать такой огромный тип в контейнер или в обобщенный инициализатор - думай о том, что он будет его инициализировать весь. Если это не подходит, значит обобщенный инициализатор тоже не подходит, т.е. у нас частный случай.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 14:28
Цитата Сообщение от DrOffset Посмотреть сообщение
Хочешь засовывать такой огромный тип в контейнер или в обобщенный инициализатор - думай о том, что он будет его инициализировать весь. Если это не подходит, значит обобщенный инициализатор тоже не подходит, т.е. у нас частный случай.
Какая разница огромный он или нет? Много маленьких будут лучше себя вести? Подозреваю, даже хуже - здесь хоть memset вызывается, а для маленьких - каждый байт будет по-отдельности выставляться в разных кусках памяти.
И что-то непохоже это на частный случай.
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 14:43
Цитата Сообщение от oleg-m1973 Посмотреть сообщение
Много маленьких будут лучше себя вести?
Хочешь сказать, что ты столько лет программируешь и не знал про это?
Что если ты засунешь, например, POD структуру в vector, то он забъет все ее объекты нулями?

Цитата Сообщение от oleg-m1973 Посмотреть сообщение
И что-то непохоже это на частный случай.
Частный случай - это когда ты хочешь избежать инициализации.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 14:50
Цитата Сообщение от DrOffset Посмотреть сообщение
Хочешь сказать, что ты столько лет программируешь и не знал про это?
Не, не знал.
Не так часто пользуешься pod-типами, да ещё и инициализируешь их со скобками, чтоб заметить такие проблемы.
Ну и инициализация вне конструкторов и default не так давно появились. Соответственно, не уверен, что раньше эта проблема проявлялась для не-pod.
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 14:53
oleg-m1973, этому правилу по меньшей мере 25 лет. В С++98 была несколько иная формулировка, но смысл такой же.
В С++11 все чуть усложнилось - это да.
0
6772 / 4565 / 1844
Регистрация: 07.05.2019
Сообщений: 13,726
29.02.2020, 15:06
Цитата Сообщение от DrOffset Посмотреть сообщение
oleg-m1973, этому правилу по меньшей мере 25 лет. В С++98 была несколько иная формулировка, но смысл такой же.
В С++11 все чуть усложнилось - это да.
Такие вещи должен компилятор отслеживать - поставить скобки при иналициализации легко, но мало кто будет ожидать, что поведение изменится если у класса нет явно определённого конструктора по умолчанию. С другой стороны - поведение тоже поменяется, если просто заменить пустые конструкторы на слово default (кстати, а дефолтные конструкторы копирования тоже заполняют массивы нулями?)

И я вот тоже подозреваю, что это какой-то старый просёр, который уже поздно исправлять.
0
19505 / 10108 / 2463
Регистрация: 30.01.2014
Сообщений: 17,829
29.02.2020, 15:12
oleg-m1973, то, что это не согласуется с твоими представлениями, не означает, что это "просёр".
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
29.02.2020, 15:12

Почему тут нужен амперсанд (&) ?
Вопрос по foreach , а точнее, по амперсанду (&amp;) в foreach: Есть цикл: $nums = ; foreach($nums as $v) $v = $v * 10; ...

Почему не записывает в textbox? В чем тут ошибка?
private void button3_Click(object sender, EventArgs e) { string strSQL; using (cn = new...

Не могу понять почему не работает и где тут ошибка
Доброго всем времени суток! Нужна ваша помощь скачал с инета форму которая определяет сколько людей онлайн на сайте установил по...

Гибридное видео в ноутах... почему у многих тут проблемы???
Железо: ноут Samsung NP-R730 (JB02RU) это если верить заводской наклейке по инфе но офсайте в этой конкретной конфигурации встоит только...

Где тут ошибка и почему, ну ни как не могу разобраться
/* Программа: Параметры vararg и перегрузка. */ class VarArgs3 { static void vaTest(int ... v) { ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru