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

Почему тут UB?

28.02.2020, 08:59. Показов 3144. Ответов 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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,822
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
Ответ Создать тему
Новые блоги и статьи
Программа для com-порта
Uhbif79 05.06.2026
Всем привет, давно хотел изучить Qt, начинал, бросал, потом снова начинал. И сейчас вот смог написать свою первую программу. До этого имел опыт программирования микроконтроллеров, писал прошивки на. . .
Транскрипция 55-минутного видео через Whisper: WhisperDesktop облажался, спас Google Colab[
anaschu 01.06.2026
Понадобилось получить текст из свежезагруженного видео на YouTube. Казалось бы, задача на пять минут. Заняла полтора часа. Делюсь опытом — может кому пригодится последовательность решений. . . .
21 мат мед. Планы на развитие модели здравоСохранения
anaschu 01.06.2026
AnyLogic: план развития симуляционной модели рабочего коллектива — динамический абсентеизм, реальные данные, три сценария сравнения Продолжаю серию постов о дискретно-событийной модели рабочего. . .
20. Мат мед. Абсентеизм как отдельный тип простоя
anaschu 29.05.2026
Апдейт модели: исправленные баги, абсентеизм и новые механизмы Продолжаю развивать ранее описанную модель рабочего коллектива на AnyLogic. За последние несколько дней был проведён серьёзный. . .
19. здоровье, усталость и психотип работника влияют на производительность предприятия, и наоборот, производительность на здоровье, усталось и психотип
anaschu 28.05.2026
Дискретно-событийная модель рабочего коллектива на AnyLogic: здоровье, выгорание, психотипы и микростимуляция Привет, коллеги. Хочу поделиться итогами нескольких недель работы над симуляционной. . .
"Прокси" для последовательного порта
Eddy_Em 28.05.2026
Эту штуку написал я достаточно давно. Но сейчас вот понадобилось настроить датчик грозы, но при этом не отключать его от "метеодемона". Соответственно, надо запустить этот "прокси": метеодемон будет. . .
Рефакторинг программы уравнивания.
Massaraksh7 26.05.2026
Пример по предыдущей записи в блоге. Но, надо заметить, что, во-первых, там оптимизация не только математики, но и работы с базой данных, и с графами, а во-вторых, это ещё не всё.
Использование TThread в Lazarus для математических вычислений.
Massaraksh7 25.05.2026
Производя рефакторинг своих программ на предмет ускорения их работы, обратил внимание на такой аспект, как сокращение времени матвычислений. Дело в том, что приходится работать с большими матрицами. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru