Hallo,
ich beschäftige mich grad ein wenig mit der Keil Entwicklungsumgebung
und habe folgendes "Problem".
1
#include<stdio.h>
2
#include<XC167.h>
3
signedintZahl,Zahl1;
4
floatZahl2;
5
voidmain(void)
6
{
7
8
9
ASC0_RXFCON=0x0102;
10
ASC0_TXFCON=0x0102;
11
ALTSEL0P3|=0x0400;
12
P3=(P3&~0x0400)|0x0400;
13
DP3=(DP3&~0x0400)|0x0400;
14
ASC0_TIC_IR=1;
15
ASC0_BG=0x0081;
16
ASC0_CON=0x8011;// load ASC0 control register
17
PSW_IEN=1;
18
19
printf("zahlen eingeben:\n");
20
scanf("%d%d",&Zahl,&Zahl1);
21
printf("%x\n%x\n",&Zahl,&Zahl1);
22
printf("%d\n%d\n",&Zahl,&Zahl1);
23
24
25
while(1){}
26
27
28
}
Ich lese also 2 integer ein und möchte mir anschliessend die
Speicheradreesen anzeigen lassen, einmal als Hex und einmal dezimal.
Als Ergebnis erhalte ich:
zahlen eingeben:
12
14
8246
8244
-32186
-32188
dabei sind 8246 und 8244 die Adressen in hexadezimaler Form und die
beiden nagativen Zahlen sollen wohl die Adressen in Dezimalform
darstellen.
Wieso sind sie negativ? Wenn ich einen anderen C-Compiler nehme und eine
Windows Konsolen-Anwendung programmiere funktioniert das Ganze
problemlos.
Dashier:
printf("%x\n%x\n",&Zahl,&Zahl1);
Zeigt die Adressen als vorzeichenlose Zahlen an
Dashier:
printf("%d\n%d\n",&Zahl,&Zahl1);
hingegen bewertet sie mit Vorzeichen. D.h., der Wertebereich ist
halbiert, die eine Hälfte ist dann halt negativ. Hängt letztlich mit der
Repräsentation von Integern im Prozessor zusammen...
Nachtrag:
Unter Windows z.B. funktioniert es vermutlich deshalb, weil der Integer
da 32 Bit lang ist, da passt die Adresse dann ganz rein, ohne in den
"negativen" (Zweierkomplement) Bereich zu laufen.
Du musst Dir die Formatspezifizierer von printf mal genauer ansehen.
%d gibt einen signed int aus (als vorzeichenbehaftete Dezimalzahl)
%u gibt einen unsigned int aus (als vorzeichenlose Dezimalzahl)
%x gibt einen unsigned int aus (als vorzeichenlose Hexadezimalzahl)
%X wie %x, nur werden ABCDEF statt abcdef verwendet
@ Kristian Herr (xkris)
Auch als C Programmierer sollte man sich mit der Zahlenrepresentation
vertraut machen. Was ist der Unterschied zwischen 0x41, 'A', 65, und
dergleichen. Sonst macht alles wenig Sinn.
Es ist ja nun fast alles gesagt worden, aber trotzdem noch ein kleiner
Hinweis: printf ist recht intelligent. Wenn Du Zahl1 und Zahl2 als
"signed int" definierst, dann wird es auch so ausgedruckt.
Es wird also reichen Zahl1 und Zahl2 als "unsigned int" zu deklarieren.
Am printf-Formatstring brauchst Du dann nichts zu ändern.
Grüsse
Michael
Michael wrote:
> Es ist ja nun fast alles gesagt worden, aber trotzdem noch ein kleiner> Hinweis: printf ist recht intelligent. Wenn Du Zahl1 und Zahl2 als> "signed int" definierst, dann wird es auch so ausgedruckt.>> Es wird also reichen Zahl1 und Zahl2 als "unsigned int" zu deklarieren.> Am printf-Formatstring brauchst Du dann nichts zu ändern.
Das ist absolut falsch. printf kannst du alles übergeben, das hat keine
Ahnung was du übergibst.
Schau dir mal den Code genauer an: Er übergibt printf keinen signed
int...
Das glaube ich weniger, du meinst wohl cout. Schau dir mal den printf
Prototyp an, und wie variable Parameterzahlen in C realisiert werden.
Ohne den format-string weis prinft nichtmal wieviele Parameter es
bekommt, geschweige denn welchen Typ die haben.
Sowas hier funktioniert zB. völlig problemlos:
Michael wrote:
> Es ist ja nun fast alles gesagt worden, aber trotzdem noch ein kleiner> Hinweis: printf ist recht intelligent. Wenn Du Zahl1 und Zahl2 als> "signed int" definierst, dann wird es auch so ausgedruckt.>> Es wird also reichen Zahl1 und Zahl2 als "unsigned int" zu deklarieren.> Am printf-Formatstring brauchst Du dann nichts zu ändern.
Äh. Nein
printf bekommt 2 Bytes pro signed/unsigned über die
Argumentschnittstelle. Ob diese 2 Bytes signed oder unsigned sind,
kann man denen nicht ansehen. Alles was printf hat, ist der
Formatstring. Dem entnimmt es wie diese beiden Bytes pro Argument zu
interpretieren sind.
Der einzige der hier noch mitmischen könnte, ist der Compiler.
Der könnte im Prinzip prüfen, ob die Datentypen der Argumente
mit den im Formatstring angegebenen Codes übereinstimmen oder
zumindest plausibel sind. Aber gerade bei signed/unsigned halten
sich Compiler meist raus. Wenn eine derartige Überprüfung stattfindet
dann meist nur auf grobe Schnitzer, wie ein %d bei einem double
Argument.
Der GCC spuckt für den code oben nur eine Warnung aus, wenn -Wall
gesetzt ist. Das ist auch richtig so, denn man könnte printf ja auch
völlig anders implementieren.
Asche über mein Haupt. Habe zu schnell gelesen. Er übergibt ja die
Adressen und nicht die Werte.
Wenn man Werte übergibt, scheint der Compiler aber auf die Deklaration
zu achten. Ich hatte das mal bei mir, dass ich short int Variablen
ausgegeben habe (16Bit) und die kamen durchs printf immer mit
Vorzeichen. Sobald ich die Deklaration auf "unsigned short int" geändert
hatte, war das Vorzeichen weg.
So macht es zumindest GCC 4.2 bei mir unter Linux.
Grüsse
Michael
Michael wrote:
> Wenn man Werte übergibt, scheint der Compiler aber auf die Deklaration> zu achten.
Nein.
> Ich hatte das mal bei mir, dass ich short int Variablen> ausgegeben habe (16Bit) und die kamen durchs printf immer mit> Vorzeichen. Sobald ich die Deklaration auf "unsigned short int" geändert> hatte, war das Vorzeichen weg.>> So macht es zumindest GCC 4.2 bei mir unter Linux.
Das ist was anderes. Alle Zahlen werden als int übergeben wenn sie
kleiner sind. Auf einem PC also meist 32bit. short int ist aber nur
16bit, daher passt sowohl signed short int als auch unsigned short int
in ein int. Somit bleibt das Vorzeichen erhalten.
>Das ist was anderes. Alle Zahlen werden als int übergeben wenn sie>kleiner sind. Auf einem PC also meist 32bit. short int ist aber nur>16bit, daher passt sowohl signed short int als auch unsigned short int>in ein int. Somit bleibt das Vorzeichen erhalten.
Das habe ich jetzt nicht verstanden. Kannst Du das noch mal erklären?
Also hier nochmal, was ich bei meinem Programm sehe:
Wenn ich Zahl als "short int" deklariere, und diese dann z.B. den Wert
0xFF zuordne, dann gibt mir
printf ("%2X", Zahl);
als Ergebnis 0xFFFF (also -1).
Wenn ich nur die Deklaration ändere auf "unsigned short int", dann
erhalte ich mit dem gleichen printf als Ergebnis 0xFF (also wirklich der
unsigned Wert).
(Ich hoffe, ich habe das jetzt korrekt aus dem Gedächnis wiedergegeben.)
Das heisst also, dass die Deklaration einen Unterschied zu machen
scheint. Wie es in Assembler umgesetzt wurde, habe ich mir leider nicht
angeschaut.
Wie gesagt, das war unter GCC Linux, ich glaube GCC 4.2 (kann aber auch
4.3 gewesen sein).
Schöne Grüsse
Michael
Michael wrote:
> Das habe ich jetzt nicht verstanden. Kannst Du das noch mal erklären?
Im Grunde hat das was du beobachtet hast, nichts mit printf an sich
zu tun, sondern mit der impliziten Konvertierung auf int.
Du würdest dasselbe in diesem Fall beobachten
1
voidfoo(inti)
2
{
3
// hier i ansehen
4
}
5
6
voidbar()
7
{
8
signedshortintj=0xFF;
9
unsignedshortintk=0xFF;
10
11
foo(j);
12
foo(k);
13
}
in foo siehst du verschiedene Werte für die Aufrufe für j und
für k.
> (Ich hoffe, ich habe das jetzt korrekt aus dem Gedächnis wiedergegeben.)
Ich vertrau dir mal, dass ein signed short int auf deinem System
tatsächlich nur 1 Byte gross ist. Spielt aber auch nicht wirklich
eine Rolle. Der entscheidende Punkt ist: was passiert, wenn ein
kleinerer Datentyp (short int) zu einem größeren Datentyp (int)
hochgecastet wird. Bei einem signed Datentyp muss klarerweise
eine Sign-Extension gemacht werden (0xFF -> 0xFFFF), was bei
einem unsigned Typ unterbleibt (0xFF -> 0x00FF)
Noch ein wichtiger Aspekt:
Einen Zeiger als int oder unsigned int auszugeben ist eigentlich
unschön, wenig portabel und funktioniert nur dann, wenn int und Zeiger
die gleiche Speichergröße haben.
Besser ist es, im Formatstring %p anzugeben, denn dieses ist speziell
für Zeiger vorgesehen und gibt die Adresse in einem auf der jeweiligen
Architektur sinnvollen Format aus (meist als 32- oder 64-Bit-Hexzahl,
auf 8086 und 80286 als Segment:Offset usw.).