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 ?

Author: templatetypedef, 2011-03-09

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 implementacja memcpy 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.
 65
Author: templatetypedef,
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.

 20
Author: user1998586,
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 do memmove(3) obsługuje NULL 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));
}

Przykłady :

- 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))
 0
Author: VonC,
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