Czy jest jakaś gwarancja wyrównania zwracanego adresu przez nową operację C++?

Większość doświadczonych programistów wie, że wyrównanie danych jest ważne dla wydajności programu. Widziałem, jak jakiś programista napisał program, który przydziela większy rozmiar bufora niż potrzebuje i używa wyrównanego wskaźnika jako begin. Zastanawiam się, czy w moim programie nie mam pojęcia, czy jest jakaś gwarancja wyrównania adresu zwracanego przez nową operację C++. Więc napisałem mały program do testowania

for(size_t i = 0; i < 100; ++i) {
    char *p = new char[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
    short *p = new short[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
for(size_t i = 0; i < 100; ++i) {
    float *p = new float[123];
    if(reinterpret_cast<size_t>(p) % 4) {
        cout << "*";
        system("pause");
    }
    cout << reinterpret_cast<void *>(p) << endl;
}
system("pause");

Kompilatorem którego używam jest Visual C++ Express 2008. Wygląda na to, że wszystkie adresy zwrócone przez nową operację są wyrównane. Ale nie jestem pewien. Więc moje pytanie brzmi: czy są jakieś Gwarancje? Jeśli mają gwarancję, nie muszę się dostosowywać, jeśli nie, muszę.

Author: Brian Tompsett - 汤莱恩, 2009-02-03

6 answers

Wyrównanie ma następującą gwarancję ze standardu (3.7.3.1/2):

Zwrócony wskaźnik powinien być odpowiednio wyrównany, aby można go było przekonwertować na wskaźnik dowolnego typu obiektu, a następnie służy do uzyskania dostępu do obiektu lub tablicy w miejsce przydzielone (do przechowywanie jest jawnie dealokowane przez wywołanie odpowiedniej funkcji dealokacji).

EDIT : Thanks to timday for highlighting a bug in GCC/glibc w przypadku braku gwarancji.

EDIT 2 : komentarz Bena podkreśla interesujący przypadek. Wymagania dotyczące procedur alokacji dotyczą wyłącznie procedur przewidzianych w normie. Jeśli aplikacja ma własną wersję, to nie ma takiej gwarancji na wynik.

 26
Author: Richard Corden,
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-05-23 11:54:29

To późna odpowiedź, ale tylko dla wyjaśnienia sytuacji na Linuksie - na systemach 64-bitowych pamięć jest zawsze wyrównana 16-bajtowo:

Http://www.gnu.org/software/libc/manual/html_node/Aligned-Memory-Blocks.html

Adres bloku zwracanego przez malloc lub realloc w systemie GNU jest zawsze wielokrotność ośmiu (lub szesnastu w systemach 64-bitowych).

Operator new wywołuje malloc wewnętrznie (patrz ./gcc/libstdc++-v3/libsupc++/new_op.cc) tak więc dotyczy to new jako cóż.

Implementacja malloc, która jest częścią glibc W zasadzie definiuje MALLOC_ALIGNMENT BYĆ 2*sizeof(size_t) i size_t to 32bit=4byte i 64bit=8byte odpowiednio w systemie x86-32 i x86-64.

$ cat ./glibc-2.14/malloc/malloc.c:
...
#ifndef INTERNAL_SIZE_T
#define INTERNAL_SIZE_T size_t
#endif
...
#define SIZE_SZ                (sizeof(INTERNAL_SIZE_T))
...
#ifndef MALLOC_ALIGNMENT
#define MALLOC_ALIGNMENT       (2 * SIZE_SZ)
#endif
 16
Author: user1059432,
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
2012-05-16 21:47:31

Nawiasem mówiąc ms documentation wspomina coś o malloc/new zwracających adresach, które są wyrównane 16-bajtowo, ale z eksperymentów tak nie jest. Zdarzyło mi się potrzebować wyrównania 16-bajtowego dla projektu (aby przyspieszyć kopie pamięci z ulepszonym zestawem instrukcji), w końcu uciekłem się do pisania własnego alokatora...

 8
Author: jheriko,
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
2009-02-03 12:11:37

C++17 zmienia wymagania dotyczące alokatora new, tak że wymagane jest zwrócenie wskaźnika, którego wyrównanie jest równe makrowi __STDCPP_DEFAULT_NEW_ALIGNMENT__ (które jest zdefiniowane przez implementację, a nie przez dodanie nagłówka).

Jest to ważne, ponieważ rozmiar ten może być większy niż alignof(std::max_align_t). Na przykład w Visual C++, Maksymalne wyrównanie regularne jest 8-bajtowe, ale domyślna new zawsze zwraca 16-bajtową wyrównaną pamięć.

Zwróć również uwagę, że jeśli nadpiszesz domyślną new z Twój własny alokator, jesteś zobowiązany do przestrzegania __STDCPP_DEFAULT_NEW_ALIGNMENT__.

 8
Author: Nicol Bolas,
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
2019-07-31 17:45:25

Operator new/new[] zwróci wskaźniki z wystarczającym wyrównaniem, tak aby działał dobrze z podstawowymi typami danych (double,float,itp.). Przynajmniej każdy rozsądny kompilator C+++runtime powinien to zrobić.

Jeśli masz specjalne wymagania wyrównania, takie jak dla SSE, to prawdopodobnie dobrym pomysłem jest użycie specjalnych funkcji aligned_malloc lub zrolowanie własnych.

 5
Author: mfazekas,
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
2009-02-03 10:28:44

Pracowałem nad systemem, w którym użyli wyrównania, aby uwolnić dziwny kawałek na własny użytek!

Użyli dziwnego bitu do zaimplementowania systemu pamięci wirtualnej.

Gdy wskaźnik miał ustawiony nieparzysty bit, używali go do oznaczenia, że wskazywał (minus nieparzysty bit) do informacji, aby uzyskać dane z bazy danych, a nie same dane.

Myślałem, że to szczególnie paskudny kawałek kodowania, który był daleko do sprytu dla własnego dobra!!

Tony

 4
Author: AnthonyLambert,
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
2009-02-03 11:12:29