PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 1 int (4 Byte) als 4 char (1 Byte) darstellen?



shutdown
27-07-2007, 01:05
Hallo,

ich bin gerade über eine relativ interessante Spielerei gestoplert, mit der man die CPUID-Instruktion von "modernen" Prozessoren auslesen kann.
Folgender Codeschnipsel soll in den int-Variablen b,c und d jeweils 4 Byte des Vendorstrings der eingesetzten CPU repräsentieren - in meinem Fall "Genu" "ineI" "ntel".
Da der Datentyp aber int ist, erscheint die Ausgabe hier als 3 Zahlen:


#include <stdio.h>

#define cpuid(func,ax,bx,cx,dx)\
__asm__ __volatile__ ("cpuid":\
"=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));

int main()
{
int a,b,c,d;

cpuid(0,a,b,c,d);

printf("%d, %d, %d, %d\n",a,b,c,d);

return 0;
}

Wie kriege ich es hin, die 3 4-Byte-langen int-Variablen in insgesamt 12 1-Byte-lange char-Variablen zu konvertieren?
Ich müsste die int also in vier Teile zersägen und zu chars machen - aber wie?

Ich finde solche Spielereien im Speicher sehr interessant, nur leider konnte ich nirgendwo ein HowTo finden, das nur im entferntesten solche Spielereien behandelt. Vielleicht ist es auch für einen Anfänger absolut nicht geeignet, aber es interessiert mich eben... ;)

Peter

SebastianKN
27-07-2007, 05:18
vielleicht kannst dir ja die Adresse von zB a (also dem Integer a) holen und mit memcpy das erste Byte in ein char schreiben. Das zweite Byte in ein anderen char usw. Hab aber keine Ahnung ob das auch nur ansatzweise funktioniert :)

Pingu
27-07-2007, 05:51
Du kennst die Funktionen zur Bitmanipulation? Sollte man eigentlich wenn man direkt in Assembler irgendwo im Speicher herum schreibt.
printf("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
(a >> 24) & 0xFF, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
(b >> 24) & 0xFF, (b >> 16) & 0xFF, (b >> 8) & 0xFF, b & 0xFF,
(c >> 24) & 0xFF, (c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF,
(d >> 24) & 0xFF, (d >> 16) & 0xFF, (d >> 8) & 0xFF, d & 0xFF);

shutdown
27-07-2007, 12:08
Du kennst die Funktionen zur Bitmanipulation?
Nein, höchstens vom Hörensagen :rolleyes:


Sollte man eigentlich wenn man direkt in Assembler irgendwo im Speicher herum schreibt.
Da kann ich dir nur zustimmen - da ich das Beispiel aber nur kopiert habe und auch nur ein bisschen mit der Sache herumspiele und eigentlich auch noch nicht allzuviel Ahnung von C und insbesondere Assembler habe, habe ich mir gedacht, das es sich hier um eine gute Gelegenheit für learning-by-doing handelt.

Ach ja: Ich weiss, dass ich mit Assembler-Code vorsichtig sein sollte und werde da ohne mich etwas besser auszukennen auch nicht selber rumpfuschen :D

Auf jeden Fall vielen Dank, das war genau was ich gesucht habe - und schon wieder was gelernt :D
Ich werde mal Google zu den Funktionen befragen, damit ich mein oberflächliches Unwissen in nutzbares Wissen umformen kann!

Der Vollständigkeit wegen:

#include <stdio.h>

#define cpuid(func,ax,bx,cx,dx)\
__asm__ __volatile__ ("cpuid":\
"=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func));

int main()
{
int a,b,c,d;

cpuid(0,a,b,c,d);

printf("%c%c%c%c%c%c%c%c%c%c%c%c\n",
b & 0xFF, (b >> 8) & 0xFF, (b >> 16) & 0xFF, (b >> 24) & 0xFF,
d & 0xFF, (d >> 8) & 0xFF, (d >> 16) & 0xFF, (d >> 24) & 0xFF,
c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, (c >> 24) & 0xFF);

return 0;
}


a enthält in meinem Fall keine relevanten Informationen (Anzahl der CPUID-Instruktionen der CPU), die Reihenfolge b, d, c stimmt auch, so ists vorgesehen.
Ausgabe auf meinem Core2Duo:

$ ./cpuid
GenuineIntel
Perfekt :D

Peter

RHBaum
27-07-2007, 13:03
naja, ueber perfomance reden wir mal nich ^^

und warum einfach, wenns auch umstaendlich geht gell ^^

deine ints repraesentieren ja eigentlich schon ne zeichenkette von 4 zeichen, nur eben nicht 0 terminiert, und als int "getarnt"

"Tarnungen" in der Form erstellt man und loesst man auch gleich auf mittels casts ! die kosten nix ....
eleganter als casts sind unions, technisch isses aber dasselbe ...

dein problem ist nur noch, du musst deine 3 ints nur hintereinander bekommen ... und ein \0 zeichenende anzuhaengen

Das kann man durch kopieren in einen buffer realisieren .... bei gleichzeitiger umdefinition
char buffer[16];
memset(buffer,0,16);
memcopy(buffer,&a,4);
memcopy(&buffer[4],&b,4);
memcopy(&buffer[8],&c,4);

...

und schon hast in buffer deinen String ....

Eleganter und performanter bekommt man es, wenn man die Ints von haus aus hintereinander legt .... und den union als cast verwendet ....

struct X
{
int a;
int b;
int c;
int d;
};

union Y
{
X x;
char cstring[16];
};

Y data;
// nullen
memset(&data,0,sizeof(Y));
// werte beschreiben, oder noch besser gleich in die variablen des structs schreiben lassen , wenn das geht

cpuid(0,data.x.a,data.x.b,data.x.c,data.x.d); // keine ahnung ob das geht

// und ueber data.cstring kommt man dann gleich an die cstring form ran ...

printf("%c\n",data.cstring);


Ciao ...

Pingu
27-07-2007, 13:27
Naja, die Speicherkopiererreien sind in solchen einfachen Fällen etwas zuviel des guten.
Unions sind wirklich elegant an der Stelle. Allerdings haben Unions gegebenenfalls einen Nachteil: man muss sicher sein, dass die Reihenfolge stimmt (ich sage nur: little endian vs. big endian).

panzi
27-07-2007, 16:07
Warum so kompliziert?


typedef union {
char bytes[4];
int i;
} int_and_bytes_t;

// ...

int_and_bytes_t x;

x.bytes[0] = 'a';
x.bytes[1] = 'b';
x.bytes[2] = 'c';
x.bytes[3] = 'd';

// und dann hat man die int repräsentation in x.i
// aber zu beachten ist das mit little und big endian
// also eventuell noch mit htonl/ntohl entsprechend die byteorder umordnen

quinte17
28-07-2007, 09:38
oder einfach per pointer:


#include <stdio.h>

int main(){
int zahl = 3999;
char *text;

text = (char*)&zahl;

printf("%c %c %c %c\n",text[0],text[1],text[2],text[3]);

return 0;
}


greetz

shutdown
29-07-2007, 22:23
Ich habe nun ein bisschen mit den verschiedenen Lösungen von euch gespielt und habe noch eine Frage:


char vendor[13];
memset(vendor,0,13);
memcpy(&vendor[0],&b,4);
memcpy(&vendor[4],&d,4);
memcpy(&vendor[8],&c,4);
vendor[13]='\0';
und

char vendor[13];
memset(vendor,0,13);
memcpy(vendor,&b,4);
memcpy(vendor+4,&d,4);
memcpy(vendor+8,&c,4);
vendor[13]='\0';

Funktionieren für mich beide - alles andere, u.a. auch "memcpy(vendor[4],&d,4);", wie von RHBaum vorgeschlagen, funktionieren NICHT.

Kann es sein, dass die Version von RHBaum an dieser Stelle nur einen Tippfehler hat oder stimmt da was nicht?

Und: Wofür genau benötigt man den memset-Befehl, wenn man die Länge des Strings schon bei der Definition des char-Arrays angibt?

Peter

jeebee
29-07-2007, 22:35
[...]
Das kann man durch kopieren in einen buffer realisieren .... bei gleichzeitiger umdefinition
char buffer[16];
memset(buffer,0,16);
memcopy(buffer,&a,4);
memcopy(&buffer[4],&b,4);
memcopy(&buffer[8],&c,4);
[...]
Da sind doch eindeutig die & davor, also so wie deine 1. Lösung!
afaik ist auch &buffer[0] == buffer

jan61
31-07-2007, 12:17
Ich habe nun ein bisschen mit den verschiedenen Lösungen von euch gespielt und habe noch eine Frage:


char vendor[13];
memset(vendor,0,13);
memcpy(&vendor[0],&b,4);
memcpy(&vendor[4],&d,4);
memcpy(&vendor[8],&c,4);
vendor[13]='\0';und

char vendor[13];
memset(vendor,0,13);
memcpy(vendor,&b,4);
memcpy(vendor+4,&d,4);
memcpy(vendor+8,&c,4);
vendor[13]='\0';Funktionieren für mich beide - alles andere, u.a. auch "memcpy(vendor[4],&d,4);", wie von RHBaum vorgeschlagen, funktionieren NICHT.

Kann es sein, dass die Version von RHBaum an dieser Stelle nur einen Tippfehler hat oder stimmt da was nicht?

Und: Wofür genau benötigt man den memset-Befehl, wenn man die Länge des Strings schon bei der Definition des char-Arrays angibt?

Peter

memset setzt den Inhalt der Variablen (bzw. korrekt den Speicherinhalt ab der Stelle, an der die Variable vendor beginnt) auf Null. Ist in diesem Fall eigentlich überflüssig, da durch die 3 memcpy-Befehle bereits die Stellen 0 ... 11 von vendor gesetzt werden und mit dem Befehl vendor[13]='\0' die abschließende Null in die Variable gesetzt wird. Allerdings an dieser Stelle an eine ungültige Adresse, es müsste vendor[12] heißen, ein mit 13 Stellen deklariertes char-Array hat nur die Stellen 0 ... 12.

Man sollte memset immer dann benutzen, wenn man nicht 100% sicher ist, dass eine deklarierte Variable im Programm korrekt beschrieben wird. In vielen Fällen wird durch den Compiler die Variable initialisiert, aber das ist nicht im C-Standard definiert, kann also ins Auge gehen.

Zu der Variante "memcpy(vendor[4],&d,4);": memcpy erwartet Adressen, vendor[4] ist keine, sondern der Inhalt der 5. Stelle von vendor. Damit wird sonstwohin kopiert, aber nicht in die 5. Stelle von vendor. Korrekt ist &vendor[4] oder vendor+4.

Jan

P.S.: Die beiden Code-Varianten sind übrigens identisch, sie benutzen nur eine andere Art des Zugriffs auf die Stellen von vendor. &vendor[4] ist die Adresse des 5. Elements des char-Arrays vendor, vendor+4 ist die Adresse von vendor + 4 (zeigt also auf eine Adresse mit einem Offset von 4 zu der Adresse von vendor). Beide Angaben landen beim 5. Element, die 2. allerdings nur, weil der Compiler ein char-Array an hintereinanderliegenden Stellen im Speicher positioniert.