Використовує malloc для невизначеної поведінки int до C ++ 20


96

Мені сказали, що наступний код має невизначену поведінку до С ++ 20:

int *p = (int*)malloc(sizeof(int));
*p = 10;

Це правда?

Аргументом було те, що час життя intоб’єкта не починається до присвоєння йому значення ( P0593R6 ). Щоб вирішити проблему, newслід використовувати розміщення:

int *p = (int*)malloc(sizeof(int));
new (p) int;
*p = 10;

Чи справді нам доводиться викликати конструктор за замовчуванням, який є тривіальним, щоб почати життя об’єкта?

У той же час, код не має невизначеної поведінки в чистому C. Але, що, якщо я виділю intкод в C і використаю його в коді C ++?

// C source code:
int *alloc_int(void)
{
    int *p = (int*)malloc(sizeof(int));
    *p = 10;
    return p;
}

// C++ source code:
extern "C" int *alloc_int(void);

auto p = alloc_int();
*p = 20;

Це все ще невизначена поведінка?


8
Для int? Ні для std::string? Так.
Елджей,

8
@Eljay Для int, також так. Просто це не викличе проблем на практиці, якщо ви цього не зробите. Бо std::stringце, очевидно, спричинить проблеми.
Баррі,

Pre C ++ 20 ви можете додати розташування новим. Тоді це було б добре сформовано, і це, мабуть, нічого не коштувало б.
Франсуа Андріє

8
Які нові правила в C ++ 20 змінюють це?
Кевін,

4
Чи не повинно бути int *p = (int*)malloc(sizeof(int)); p = new(p) int;? Одного разу я зрозумів, що не присвоєння результату розміщення нового може також спричинити смертельні наслідки (хоча це може виглядати дещо безглуздо).
Шефф

Відповіді:


62

Це правда?

Так. Технічно кажучи, жодна частина:

int *p = (int*)malloc(sizeof(int));

насправді створює об'єкт типу int, тому розмежування посилань pє UB, оскільки фактичного intтам немає.

Чи справді нам доводиться викликати конструктор за замовчуванням, який є тривіальним, щоб почати час життя об’єкта?

Чи є у вас повинні в об'єктної моделі C ++ , щоб уникнути невизначений поведінка пре-C ++ 20? Так. Чи справді будь-який компілятор заподіює шкоду тим, що ви цього не робите? Не те, що я знаю.

[...] Це все ще невизначена поведінка?

Так. До версії C ++ 20, ви все ще насправді ніде не створювали intоб’єкт, тож це UB.


Коментарі не призначені для розширеного обговорення; цю розмову переміщено до чату .
Makyen

Чому мови в timsong-cpp.github.io/cppwp/n3337/basic.life#1.1 недостатньо, щоб вона не була UB? Врешті-решт, intу прикладі було отримано сховище належного розміру та вирівнювання - intтам починається час життя об’єкта.
авакар

41

Так, це був УБ. Перелік способів існування intможе бути перерахований, і жоден там не застосовується, якщо ви не вважаєте, що malloc є акаузальним.

Це широко вважалося вадою стандарту, але мало важливим, оскільки оптимізація, проведена компіляторами C ++ навколо цього конкретного біта UB, не викликала проблем у цьому випадку використання.

Що стосується другого питання, C ++ не вимагає, як взаємодіють C ++ і C. Отже, вся взаємодія з C - це ... UB, тобто поведінка, не визначена стандартом C ++.


5
Чи можете ви розширити перелічений перелік способів існування int? Пам'ятаю, я задавав подібне запитання про тривалість життя примітивних типів, і мені сказали, що примітив може "існувати", просто кажучи, що він існує, оскільки специфікація не говорила інакше. Здається, я, можливо, пропустив корисний розділ специфікації! Я хотів би знати, який розділ мені слід було переглянути!
Корт Аммон

7
@CortAmmon Перелічений перелік способів існування об’єкта (будь-якого типу) в C ++ 20 знаходиться в [intro.object] : (1) за визначенням (2) new-вираз (3) неявно за новими правилами у P0593 (4) зміна активного члена спілки (5) тимчасовим. (3) є новим у C ++ 20, (4) було новим у C ++ 17.
Барі

3
Чи справді взаємодія C / C ++ - UB? Це мало б більше сенсу бути визначеним реалізацією, а не невизначеним, інакше було б дивно навіть мати extern "C"синтаксис взагалі.
Руслан

4
@Ruslan: Реалізації можуть вільно визначати будь-яку поведінку, яку ISO C ++ залишає невизначеною. (Наприклад gcc -fno-strict-aliasing, або MSVC за замовчуванням). Вимовляючи "визначена реалізація", потрібно, щоб усі реалізації С ++ визначали певний спосіб взаємодії з деякою реалізацією С, тому має сенс повністю залишити до реалізації, хочуть вони робити щось подібне чи ні.
Пітер Кордес,

4
@PeterCordes: Мені цікаво, чому так багато людей не визнають такої різниці між IDB і UB, і приймають якесь вигадливе уявлення про те, що неможливість стандарту вимагати, щоб усі реалізації обробляли конструкцію, значущим чином передбачає судження про те, що ніяких реалізацій цього очікувати не слід, а реалізації, які цього не роблять, як наслідок не повинні розглядатися як неповноцінні.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.