Czy wykonanie memcpy(0,0,0) jest bezpieczne?
Nie znam się na standardzie C, więc proszę o cierpliwość.
Chciałbym wiedzieć, czy norma gwarantuje, że {[0] } jest Bezpieczna.
Jedynym ograniczeniem, jakie znalazłem, jest to, że jeśli obszary pamięci nakładają się na siebie, to zachowanie jest niezdefiniowane...
Ale czy możemy uznać, że obszary pamięci pokrywają się tutaj ?
3 answers
Mam wersję roboczą standardu C (ISO/IEC 9899: 1999)i ma kilka fajnych rzeczy do powiedzenia na temat tego połączenia. Na początek wymienia (§7.21.1/2) w odniesieniu do memcpy
, że
Gdzie argument zadeklarowany jako
size_t
N określa długość tablicy dla Funkcja, n może mieć wartość zero w wywołaniu tej funkcji. Chyba że wyraźnie zaznaczono w przeciwnym razie w opisie konkretnej funkcji w tej podkluczy, argumenty wskaźnikowe na wezwanie takie nadal mają ważne wartości, jak opisano w 7.1.4. Na taki telefon, a funkcja lokalizująca znak nie znajduje żadnego wystąpienia, funkcja porównująca dwa sekwencje znaków zwracają zero, a funkcja kopiująca znaki kopiuje zero postaci.
Wskazane tu odniesienie wskazuje na to:
Jeśli argument funkcji ma nieprawidłową wartość (np. wartość poza domeną funkcji, lub wskaźnik poza przestrzenią adresową program, lub wskaźnik null , lub wskaźnik do pamięci niemodyfikowalnej, gdy odpowiedni parametr nie jest const-qualified) lub typu (po promocji) nie oczekiwanego przez funkcję przy zmiennej liczbie argumentów, zachowanie jest niezdefiniowane .
Więc wygląda na to, że zgodnie ze specyfikacją C, wywołanie
memcpy(0, 0, 0)
Powoduje nieokreślone zachowanie, ponieważ wskaźniki null są uważane za " nieprawidłowe wartości."
To powiedziawszy, byłbym całkowicie zdumiony, gdyby jakikolwiek rzeczywista implementacjamemcpy
złamała się, jeśli to zrobiłeś, ponieważ większość intuicyjnych implementacji, o których myślę, nie zrobiłaby nic, gdybyś powiedział, aby skopiować zero bajtów.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-09 08:24:36
Dla Zabawy, Uwagi do wydania gcc-4.9 wskazują, że jego optymalizator korzysta z tych reguł i na przykład może usunąć warunkowe w
int copy (int* dest, int* src, size_t nbytes) {
memmove (dest, src, nbytes);
if (src != NULL)
return *src;
return 0;
}
, który następnie daje nieoczekiwane wyniki, gdy copy(0,0,0)
jest wywołany (patrz https://gcc.gnu.org/gcc-4.9/porting_to.html).
Jestem nieco ambiwalentny co do zachowania gcc-4.9; zachowanie może być zgodne ze standardami, ale możliwość wywołania memmove(0,0,0) jest czasami użytecznym rozszerzeniem tych standardów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-09 11:01:55
Możesz również rozważyć użycie memmove
widziane w Git 2.14.x (Q3 2017)
Zobacz commit 168e635 (16 lipca 2017) i commit 1773664, / align = "left" / f331ab9, 15 lipca 2017) przez René Scharfe (rscharfe
).
(dodany przez Junio C Hamano -- gitster
-- in commit 32f9025, 11 Aug 2017)
Używa makra pomocniczego MOVE_ARRAY
który oblicza wielkość
na podstawie podanej liczby elementów dla nas i podpór NULL
wskaźnik, gdy liczba ta jest równa zero.
Surowy memmove(3)
połączenia z NULL
mogą
powoduje, że kompilator (zbyt chętnie) optymalizuje się później NULL
sprawdza.
MOVE_ARRAY
dodaje bezpieczny i wygodny pomocnik do przenoszenia potencjalnie nakładających się zakresów wpisów tablicy.
W przeciwieństwie domemmove(3)
obsługujeNULL
wskaźniki iff 0 elementów do przeniesienia.
#define MOVE_ARRAY(dst, src, n) move_array((dst), (src), (n), sizeof(*(dst)) + \
BUILD_ASSERT_OR_ZERO(sizeof(*(dst)) == sizeof(*(src))))
static inline void move_array(void *dst, const void *src, size_t n, size_t size)
{
if (n)
memmove(dst, src, st_mult(size, n));
}
- memmove(dst, src, (n) * sizeof(*dst));
+ MOVE_ARRAY(dst, src, n);
Używa makra BUILD_ASSERT_OR_ZERO
która zakłada zależność od czasu kompilacji, jako wyrażenie (przy czym @cond
jest warunkiem kompilacji, który musi być prawdziwy).
Kompilacja nie powiedzie się, jeśli warunek nie jest prawdziwy lub kompilator nie może go ocenić.
#define BUILD_ASSERT_OR_ZERO(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
Przykład:
#define foo_to_char(foo) \
((char *)(foo) \
+ BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-12 08:30:45