Forum: Mikrocontroller und Digitale Elektronik negative Speicheradresse


von Chris H. (xkris)


Lesenswert?

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
signed int Zahl,Zahl1;
4
float Zahl2;
5
void main(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.

von Haku (Gast)


Lesenswert?

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...

von Haku (Gast)


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?


von Knilch (Gast)


Lesenswert?

@ 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.

von Michael (Gast)


Lesenswert?

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

von Benedikt K. (benedikt)


Lesenswert?

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...

von I_ H. (i_h)


Lesenswert?

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:
1
printf("\n%ld, %ld", "abcdefgh");


EDIT: zu langsam...

von Karl H. (kbuchegg)


Lesenswert?

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.

von I_ H. (i_h)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

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

von Benedikt K. (benedikt)


Lesenswert?

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.

von Michael (Gast)


Lesenswert?

>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

von Karl H. (kbuchegg)


Lesenswert?

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
void foo( int i )
2
{
3
  // hier i ansehen
4
}
5
6
void bar()
7
{
8
  signed short int j = 0xFF;
9
  unsigned short int k = 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)

von yalu (Gast)


Lesenswert?

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.).

von Chris H. (xkris)


Lesenswert?

Ja, ok, der interpretiert das als vorzeichenbehaftetes integer und dann 
kommt halt, zumindst in meinem Fall, was negatives raus

Danke

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.