Chociaż prawdą jest, że zachowanie jest dobrze zdefiniowane - to nie prawda, że kompilatory mogą „Optymalizacja dla const” w tym sensie, że masz na myśli.
Oznacza to, że kompilator nie może zakładać, że tylko dlatego, że parametrem jest a const T* ptr
, wskazana przez pamięć ptr
nie zostanie zmieniona za pomocą innego wskaźnika. Wskaźniki nawet nie muszą być równe. const
Jest obowiązkiem, a nie gwarancja - obowiązek przez Ciebie (= funkcja) nie dokonywać zmian przez ten wskaźnik.
Aby faktycznie mieć tę gwarancję, musisz oznaczyć wskaźnik restrict
słowem kluczowym. Zatem jeśli skompilujesz te dwie funkcje:
int foo(const int* x, int* y) {
int result = *x;
(*y)++;
return result + *x;
}
int bar(const int* x, int* restrict y) {
int result = *x;
(*y)++;
return result + *x;
}
foo()
funkcja musi czytać dwukrotnie z x
, podczas gdy bar()
tylko musi ją przeczytać raz:
foo:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, DWORD PTR [rdi] # second read
ret
bar:
mov eax, DWORD PTR [rdi]
add DWORD PTR [rsi], 1
add eax, eax # no second read
ret
Zobacz to na żywo GodBolt.
restrict
jest tylko słowem kluczowym w C (od C99); niestety do tej pory nie został wprowadzony do C ++ (z tego słabszego powodu, że bardziej skomplikowane jest wprowadzenie go w C ++). Jednak wiele kompilatorów trochę to obsługuje __restrict
.
Konkluzja: Kompilator musi obsługiwać Twój „ezoteryczny” przypadek użycia podczas kompilacji f()
i nie będzie miał z tym żadnego problemu.
Zobacz ten post dotyczący przypadków użycia dla restrict
.
const
nie jest „obowiązkiem użytkownika (= funkcji), aby nie dokonywać zmian za pomocą tego wskaźnika”. Standard C pozwala na usunięcie funkcjiconst
za pomocą rzutowania, a następnie zmodyfikowanie obiektu w wyniku. Zasadniczoconst
jest jedynie poradą i wygodą dla programisty, aby pomóc uniknąć przypadkowej modyfikacji obiektu.memcpy
istrcpy
są zadeklarowane zrestrict
argumentami, podczas gdymemmove
nie jest - tylko ten ostatni pozwala na nakładanie się bloków pamięci.Jest to dobrze zdefiniowane (w C ++, już nie jestem pewien w C), z
const
kwalifikatorem i bez niego .Pierwszą rzeczą, której należy szukać, jest ścisła zasada aliasingu 1 . Jeśli
src
idst
wskazuje na ten sam obiekt:char*
ichar const*
nie są kompatybilne.char*
ichar const*
są podobne.Jeśli chodzi o
const
kwalifikator, możesz argumentować, że skorodst == src
twoja funkcja skutecznie modyfikuje to, na cosrc
wskazuje,src
nie powinna być kwalifikowana jakoconst
. To nie takconst
działa. Należy wziąć pod uwagę dwa przypadki:const
jako takichar const data[42];
, modyfikacja (bezpośrednio lub pośrednio) prowadzi do niezdefiniowanego zachowania.const
zdefiniowane jest odniesienie lub wskaźnik do obiektu, tak jak wchar const* pdata = data;
, można zmodyfikować obiekt leżący u jego podstaw, pod warunkiem, że nie został on zdefiniowany jakoconst
2 (patrz 1.). Zatem następujące elementy są dobrze zdefiniowane:1) Jaka jest ścisła zasada aliasingu?
2) Czy jest
const_cast
bezpieczny?źródło
char*
ichar const*
nie są kompatybilne._Generic((char *) 0, const char *: 1, default: 0))
ocenia na zero.const
zdefiniowaniu odwołania lub wskaźnika do obiektu” jest niepoprawne. Masz na myśli to, że gdy zdefiniowane jest odwołanie lub wskaźnik do typuconst
kwalifikowanego , nie oznacza to, że obiekt, na który ma wskazywać, nie może być modyfikowany (na różne sposoby). (Jeśli wskaźnik wskazuje na obiekt, oznacza to, że obiekt rzeczywiście jest z definicji, więc zachowanie próby modyfikacji nie jest zdefiniowane.)const
const
language-lawyer
. Dokładność to wartość, którą cenię, ale zdaję sobie również sprawę, że wiąże się z większą złożonością. Tutaj zdecydowałem się na prostotę i zrozumiałe zdania, ponieważ uważam, że to właśnie chciał OP. Jeśli uważasz inaczej, proszę odpowiedz, będę jednym z pierwszych, który zagłosuje. W każdym razie dziękuję za komentarz.Jest to dobrze zdefiniowane w C. Ścisłe reguły aliasingu nie mają zastosowania do
char
typu ani do dwóch wskaźników tego samego typu.Nie jestem pewien, co masz na myśli przez „optymalizuj dla
const
”. Mój kompilator (GCC 8.3.0 x86-64) generuje dokładnie ten sam kod dla obu przypadków. Jeśli dodaszrestrict
specyfikator do wskaźników, wygenerowany kod będzie nieco lepszy, ale to nie zadziała w twoim przypadku, ponieważ wskaźniki są takie same.(C11 § 6.5 7)
W takim przypadku (bez
restrict
) zawsze otrzymasz121
wynik.źródło