2010-02-17 9 views
6

Wykonuję procedurę kopiowania bloku pamięci i zajmuję się blokami surowej pamięci w wydajnych porcjach. Moje pytanie nie dotyczy specjalnej procedury kopiowania, którą robię, ale w jaki sposób poprawnie sprawdzić wyrównanie surowego wskaźnika w C.Zatwierdzona składnia dla manipulowania przy użyciu surowego wskaźnika

Mam surową wskazówkę pamięci, załóżmy, że jest już rzutowany jako niezerowy znak * . W mojej architekturze mogę bardzo wydajnie kopiować pamięć w 64-bajtowych fragmentach, KIEDY ZOSTAŁO ZOSTAŁO UDZIAŁ W FORMIE 64 BYTE. Więc (standardowa) sztuczka polega na tym, że zrobię prostą kopię 0-63 bajtów "ręcznie" na głowie i/lub ogonie, aby przekształcić kopię z dowolnego znaku * o dowolnej długości na wskaźnik wyrównany do 64 bajtów z pewną liczbą wielokrotną o długości 64 bajtów.

Teraz pytanie brzmi: jak legalnie "zbadać" wskaźnik, aby określić (i manipulować) jego wyrównaniem? Oczywistym sposobem jest rzucić go do liczby całkowitej i po prostu badać bitów:

char *pointer=something. 
int p=(int)pointer; 
char *alignedPointer=(char *)((p+63)&~63); 

Uwaga tu widzę, że alignedPointer nie wskazują na ten sam pamięci jako wskaźnik ... to jest „zaokrąglona w górę” wskaźnik, który mogę nazwać moją wydajną procedurą kopiowania, i poradzę sobie z każdym innym bajtem na początku ręcznie.

Ale kompilatory (uzasadnione) zwariują podczas rzutowania wskaźnika na liczbę całkowitą. Ale jak inaczej mogę badać i manipulować dolnymi bitami wskaźnika w C: LEGAL C? Idealnie, aby przy różnych kompilatorach nie otrzymywałem żadnych błędów ani ostrzeżeń.

+0

Powinno być ok, o ile 'int' ma taki sam rozmiar jak typ wskaźnika. –

+0

Możesz również rzucić okiem na http://stackoverflow.com/questions/1898153/how-to-determine-if-memory-is-aligned-testing-for-alignment-not-aligning/1898194 –

+0

Ah, ale zakłada się, że wskaźniki są przechowywane w formacie binarnym z MSB do LSB. Co my mamy? * niezdefiniowane zachowanie! * (powiedziane w taki sam sposób, w jaki zabójcza broń mówi * odporność dyplomatyczna! *) To, że działa w rzeczywistym świecie, nie czyni go mniej nieokreślonym. ;-) –

Odpowiedz

7

Dla całkowitych typów, które są wystarczająco duże, aby pomieścić wskaźniki, C99 stdint.h posiada:

Dla danych długościach są:

które już długo przed C99.

Jeśli twoja platforma ich nie posiada, możesz zmaksymalizować przenośność swojego kodu, ciągle używając tych nazw i tworząc dla nich odpowiednie typedef.

+0

+1 pokonaj mnie do tego. –

+0

ah, ale w jaki sposób uzyskać * wyrównany * wskaźnik od niezaopasowanego? – JustJeff

0

Zamiast int, wypróbuj typ danych o gwarantowanym rozmiarze wskaźnika (INT_PTR na Win32/64). Może kompilator nie będzie zbytnio wkurzał. :) Lub użyj unii, jeśli kompatybilność 64-bitowa nie jest ważna.

1

Nie sądzę, że w przeszłości ludzie byli tak niechętni, by robić własne bicie, ale może obecny "nie dotykaj tego" nastrój byłby korzystny dla kogoś tworzącego jakąś standardową bibliotekę do dopasowywania wskaźniki. Brak jakiejś oficjalnej api, nie masz wyboru poza AND i OR na swój sposób.

+1

+1 W przeszłości nie było czegoś takiego jak * niezdefiniowane zachowanie *. Jeśli twój kompilator zrobił to, co chciałeś, to wystarczyło. –

0

Współczynniki wyrzutu do iz liczb całkowitych są poprawne, ale wyniki są zdefiniowane przez implementację. Patrz rozdział 6.3.2.3 normy. Wydaje się, że intencją jest, aby rezultaty były oczekiwane przez każdego, kto zna ten system, i faktycznie wydaje się, że jest to rutynowo w praktyce.

Jeśli architektura, o której mowa, może efektywnie manipulować wskaźnikami i liczbami całkowitymi zamiennie, a problem polega tylko na tym, czy będzie działać na wszystkich kompilatorach tego systemu, to odpowiedź brzmi, że prawdopodobnie tak będzie.

(Oczywiście, gdybym pisał ten kod, uważałbym, że jest w porządku, dopóki nie zostanie udowodnione inaczej.) Moje doświadczenie było takie, że kompilatory dla danego systemu zachowują się w bardzo podobny sposób na tym poziomie; język po prostu sugeruje szczególne podejście, że wszyscy wtedy podjąć.)

„Prawdopodobnie działa” nie jest bardzo dobre ogólne porady chociaż, więc moja propozycja byłaby po prostu napisać kod, który działa, otaczają go wystarczająco odpowiedni #ifdef s, że tylko znany kompilator skompiluje go i odracza w innych przypadkach od memcpy.

#ifdef rzadko jest idealny, ale jest dość lekki w porównaniu do innych możliwości. A jeśli potrzebne jest zachowanie zdefiniowane przez implementację lub sztuczki specyficzne dla kompilatora, to i tak opcje są dość ograniczone.

+0

"język asembler sugeruje tylko konkretne podejście" - zacznę używać tego zwrotu w debatach dotyczących oprogramowania, niezależnie od zastosowania. – notJim