Powiedziano mi, że następujący kod ma niezdefiniowane zachowanie do C ++ 20:
int *p = (int*)malloc(sizeof(int));
*p = 10;
Czy to prawda?
Argumentem było to, że czas życia int
obiektu nie jest uruchamiany przed przypisaniem mu wartości ( P0593R6 ). Aby rozwiązać problem, new
należy użyć umieszczenia:
int *p = (int*)malloc(sizeof(int));
new (p) int;
*p = 10;
Czy naprawdę musimy wywoływać domyślny konstruktor, który jest trywialny, aby rozpocząć cykl życia obiektu?
Jednocześnie kod nie ma nieokreślonego zachowania w czystym C.Ale co jeśli przydzielę int
kod w C i użyję go w kodzie 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;
Czy nadal jest to niezdefiniowane zachowanie?
c++
malloc
undefined-behavior
c++20
anton_rh
źródło
źródło
int
? Nie. Dlastd::string
? Tak.int
, też tak. Po prostu nie spowoduje to problemów w praktyce, jeśli tego nie zrobisz. Bostd::string
to oczywiście spowoduje problemy.int *p = (int*)malloc(sizeof(int)); p = new(p) int;
? Kiedyś zdałem sobie sprawę, że nieprzypisanie wyniku umieszczenia nowego może również spowodować fatalne skutki (choć może to wyglądać trochę głupio).Odpowiedzi:
Tak. Technicznie rzecz biorąc, żadna część:
int *p = (int*)malloc(sizeof(int));
w rzeczywistości tworzy obiekt typu
int
, więc dereferencjap
jest UB, ponieważ nie maint
tam rzeczywistego .Czy musisz postępować zgodnie z modelem obiektowym C ++, aby uniknąć niezdefiniowanego zachowania przed C ++ 20? Tak. Czy jakikolwiek kompilator rzeczywiście spowoduje szkodę, jeśli tego nie zrobisz? Nie, że jestem świadomy.
Tak. Przed C ++ 20 nadal
int
nigdzie nie utworzyłeś obiektu, więc to jest UB.źródło
int
w przykładzie uzyskano przechowywanie odpowiedniego rozmiaru i wyrównania -int
tam zaczyna się żywotność obiektu.Tak, to był UB. Lista sposobów istnienia
int
puszki została wyliczona i żadna z nich nie ma zastosowania, chyba że uznasz, że malloc jest bezprzyczynowy.Powszechnie uważano to za lukę w standardzie, ale jedną o małym znaczeniu, ponieważ optymalizacje wykonane przez kompilatory C ++ wokół tego konkretnego bitu UB nie spowodowały problemów z tym przypadkiem użycia.
Jeśli chodzi o drugie pytanie, C ++ nie narzuca sposobu interakcji C ++ i C. Zatem cała interakcja z C to ... UB, czyli zachowanie niezdefiniowane w standardzie C ++.
źródło
extern "C"
w ogóle składnię.gcc -fno-strict-aliasing
lub domyślnie MSVC). Powiedzenie „zdefiniowana implementacja” wymagałoby od wszystkich implementacji C ++ zdefiniowania sposobu, w jaki współdziałają z niektórymi implementacjami C, więc sensowne jest pozostawienie w pełni implementacji, czy chcą coś takiego zrobić, czy nie.