NAS

C++ Bitshift

Legenbaer

Cadet 4th Year
Registriert
Juni 2006
Beiträge
108
Hallo Leute!

Ich bin gerade auf eine Sache gestoßen, die ich mir nicht so recht erklären kann. Ich habe folgendes Programm:

Code:
unsigned char		mask = 255;
unsigned char		ucArr[8];
unsigned char*		ucPtr1;
unsigned __int64	ui64;

for(int i=0; i<8; i++)
{
	unsigned char val = rand() % 256;
	ui64	 = (ui64 << 8) | (val);
	ucArr[i] = val;
}
	
ucPtr1	= (unsigned char*)(&ui64);

//PRINT VALUES
for(int i=0; i<8; i++)
{
	unsigned char cVal = (ui64 >> (7-i)*8) & mask;
	cout << (int)cVal << " | " << (int)ucArr[i] << " | " << (int)ucPtr1[i] << endl;
}

//PRINT ADRESSEN
for(int i=0; i<8; i++)
{
	cout << (void*)&ucArr[i] << " | " << (void*)&ucPtr1[i] << endl;
}

return 0;

Wie man sehen kann habe ich eine 64 Bit Variable welche ich mit Werten von 0-255 fülle und diese dann um 8 Bit shifte. Wenn ich das richtig verstehe, werden die werte nach links geshiftet. Diese 64 Bit Variable kann ich aber auch als ein Array von 8 Bit werten verstehen. Nun die Frage: Warum werden die Werte in umgekehrter Reihenfolge ausgegeben, wenn ich die Variable caste? Meine Vermutung war, dass der Speicher dann rückwärts ausgelesen wird aber das kann nicht sein, denn die Adressen sind aufsteigend, wie auch beim normalen Array.

Wer kann diese Knobelei lösen? :freak:

MfG
 
Jupp, du hast höchstwahrscheinlich ein Little-Endian-System.

Was du da machst, ist die Repräsentation eines Typen zu untersuchen. Das geht über das was C spezifiziert hinaus und die Ergebnisse sind in aller Regel plattform-/implementations-spezifisch (und damit auch nicht portal).
 
ein intel prozessor speichert integer-variablen im speicher in "umgekehrter" reihenfolge.. die konstante 0x11223344 wird als 0x44 0x33 0x22 0x11 im speicher abgelegt. das nennt sich little endian.
 
Ob du dich auf einem big-endian oder little-endian System befindest, kannst du übrigens mit einem Konstrukt wie diesem hier abfragen. :)


Code:
typedef unsigned char byte;
typedef unsigned short int word;

bool isBigEndian()
{
	assert( sizeof( word ) == 2 * sizeof( byte ) );

	const byte Swaptest[] = { 1, 0 };
	const word* const pSwaptest = reinterpret_cast < const word* > ( Swaptest );

	if ( *pSwaptest == static_cast < word > ( Swaptest[ 0 ] ) )
	{
		return false;
	}

	return true;
}
 
Zuletzt bearbeitet:
Groovy! Danke Luete...das war mir bisher nicht geläufig. Gut zu wissen das es so ist, dass sollte mich davor schützen Fehler zu machen die schwer zu finden sind. ;)
 
antred schrieb:
Ob du dich auf einem big-endian oder little-endian System befindest, kannst du übrigens mit einem Konstrukt wie diesem hier abfragen.
Oder du fragst boost:

#include <boost/detail/endian.hpp>

Dann ist entweder das Makro BOOST_BIG_ENDIAN oder BOOST_LITTLE_ENDIAN gesetzt.
 
Kling gut, aber ist das wirklich für den öffentlichen Gebrauch gedacht? Mein Verständnis war, daß es sich bei allem, was in boost "detail" heißt, um Implementierungsdetails handelt, die sich freilich auch von einer Version zur nächsten ändern können.
 
Ja, warum denn nicht? Welche Byte-Reihenfolge verwendet wird, hängt doch von der Computerarchitektur ab, nicht von der Implementierung. Und mit dem Flag bekommst du heraus, welche Reihenfolge die Architektur verwendet. mMn ist das sehr praktisch.
 
Kling gut, aber ist das wirklich für den öffentlichen Gebrauch gedacht?
Eigentlich nicht, aber es ist Teil von Boost eigener limits.hpp-Implementierung und die Chancen, dass die die irgendwann mal über Bord werfen, ist eher gering.

Was die Verfügbarkeit in zukünftigen Versionen angeht: Boost hat für quasi gar nichts Garantien. Durfte ich selber schon in einer öffentlichen API (spirit) sehr schmerzhaft erfahren.

Was man auch machen kann, um Endians zu ermitteln:
PHP:
#include <netinet/in.h> 
bool isBigEndian()
{
 return 1 == htonl(1);
}
Nachtrag: Mir ist allerdings reichlich schleierhaft, warum man so etwas zur Laufzeit ermitteln wollen würde.
 
Zuletzt bearbeitet:
e-Laurin schrieb:
Ja, warum denn nicht? Welche Byte-Reihenfolge verwendet wird, hängt doch von der Computerarchitektur ab, nicht von der Implementierung. Und mit dem Flag bekommst du heraus, welche Reihenfolge die Architektur verwendet. mMn ist das sehr praktisch.


Was ich damit meinte ist, daß die Autoren von boost in einem Release plötzlich entscheiden könnten, daß sie das BOOST_BIG_ENDIAN-Symbol nicht mehr benötigen, und es damit einfach nicht mehr existiert. Ist ja schließlich bloß ein Implementierungsdetail und nicht Teil des public Interfaces.
Ergänzung ()

ghorst schrieb:
Was man auch machen kann, um Endians zu ermitteln:
PHP:
#include <netinet/in.h> 
bool isBigEndian()
{
 return 1 == htonl(1);
}
Nachtrag: Mir ist allerdings reichlich schleierhaft, warum man so etwas zur Laufzeit ermitteln wollen würde.


Aus dem gleichen Grund, aus dem es diese htonl()-Funktion gibt. :) Anwendunsbeispiel ... bei uns der Firma gibt es Anwendungen, die z.B. über ein serielles Protokoll namens USS Daten austauschen. USS ist laut Spezifizierung big endian. Wenn man sein Programm so schreiben will, daß es sowohl auf einer BE als auch auf einer LE Platform funktioniert, schafft man sich eben eine EndianConverter-Klasse, die wenn die Platform schon BE ist, nichts tut, und wenn sie LE ist, die ihr gelieferten Daten umdreht.


P.S. Oder lag deine Betonung auf "Laufzeit" im Gegensatz zu "zur Kompilierzeit"?
 
Zuletzt bearbeitet:
Endianess spielt eigentlich nur dass eine Rolle wenn du Integer-Variablen über *(int *)bla aus dem Speicher ausliest oder speicherst. Viele Programme die mit Netzwerkdaten oder sonstigen benutzerdefinierten Daten arbeiten haben eigene Routinen, um die aus der Datei auszulesen oder sie zu schreiben. Dort ist üblicherweise das Dateiformat auf Little- oder Big Endian genormt und die Programmroutinen lesen bzw. schreiben so wie Legenbaer es in einem ersten Post getan hat (also über einzelne Bytes und Bitshift+OR).
 
@antred Die Betonung lag auf Laufzeit und ermitteln. htonl macht das auch nicht zur Laufzeit, sondern die socket-Implementierung regelt das im Normalfall über Makros und ersetzt entsprechend htonl(X) durch X auf Big-Endian und durch irgendeine swapbytes-Implementierung auf Little-Endian.

Das man bei externen Daten natürlich auf die Endians achten muss, sollte eigentlich klar sein, genau wie klar sein sollte, dass man den Code, der plattformspezifisch ausliest/schreibt, möglichst hübsch wegkapseln sollte. Dafür sind htonX bzw ntohX geradezu Paradebeispiele.
 
Zurück
Oben