Hi Leute,
bei mir türmen sich die Fragezeichen.
Ich verwende die Funktion hex2dez aus dem Bootloader Artikel
http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung
Diese Funktion wird so definiert:
static uint16_t hex2num (const uint8_t * ascii, uint8_t num)
wenn ich nun diese Funktioin verwenden möchte, bekomme ich immer
Warnings, dass die Funktion andere Parameter erwartet. Wie muss ich den
bei meinem Beispiel casten, damit ich keine Warings bekomme?
Beispiel1:
char txt[2] = "C9";
uint8_t dez = 0;
dez = (uint8_t)Hex2Dez(txt,2);
Warning: expected 'const uint8_t *' but argument is of type 'char *'
Okay macht ja Sinn, da ja der read only Zeiger ascii vom Typ uint8_t
ist. Aber wenn ich es anders versuche:
Beispiel 2:
char txt[2] = "C9";
uint8_t dez = 0;
dez = (uint8_t)Hex2Dez((uint8_t)txt,2);
Warning: expected 'const uint8_t *' but argument is of type 'unsigned
char'
Wieso hat er jetzt zu 'unsigned char' und nicht zu 'uint8_t' gecastet?!
Gut 'unsigned char' und 'uint8_t' ist ja eigentlich das selbe, aber
wieso bekomme ich dann wieder ein Warining, das die Funtkion einen
anderen Parametertyp erwartet?
Wäre einer so nett und könnte mich mal aufklären. Irgendwie muss es doch
gehen, ohne das der Compiler meckert oder?
> Warning: expected 'const uint8_t *' but argument is of type 'unsigned> char'>> Wieso hat er jetzt zu 'unsigned char' und nicht zu 'uint8_t' gecastet?!
Weil Du es hingeschrieben hast. Das ist aber nicht das Problem sondern
der fehlende Asterisk:
> dez = (uint8_t)Hex2Dez((uint8_t)txt,2);
^
Oh das ging ja schnell :)
Joachim Drechsel schrieb:> Das braucht 3 Byte, die terminierende 0 zählt auch mit.
ja stimmt, hatte ich ein meinem Beispiel nicht beachtet. sry
g457 schrieb:> Weil Du es hingeschrieben hast. Das ist aber nicht das Problem sondern> der fehlende Asterisk:
Was ist denn ein "Asterisk"? Wie müsste denn die Code Zeile richtig
lauten?
> Was ist denn ein "Asterisk"? Wie müsste denn die Code Zeile richtig> lauten?
Die Funktion erwartet einen Pointer auf konstante uint8_t:
>> static uint16_t hex2num (const uint8_t * ascii, uint8_t num)
^
Du machst aus 'txt' einen einzelnen uint8_t:
> dez = (uint8_t)Hex2Dez((uint8_t)txt,2);
^^^^^^^
Korrekt wäre besagter Pointer:
dez = (uint8_t)Hex2Dez((uint8_t*)txt, 2);
^
g457 schrieb:> Korrekt wäre besagter Pointer:> dez = (uint8_t)Hex2Dez((uint8_t*)txt, 2);> ^
ich erstelle also beim casten noch einen konstanten pointer vom typ
uint8_t, der auf txt[0] zeigt. und dieser pointer wird dann der Funktion
übergeben?
> ich erstelle also beim casten noch einen konstanten pointer vom typ> uint8_t, der auf txt[0]
Du hast Recht, einen Pointer auf Konstante (n.b. das ist was anderes als
ein konstanter Pointer), das hab ich unterschlagen:
dez = (uint8_t)Hex2Dez((const uint8_t*)txt, 2);
Das Rumcasten bei der Nutzung kannst Du dir übrigens ersparen wenn Du es
bereits bei der Deklaration von txt adäquat anpasst.
g457 schrieb:> Das Rumcasten bei der Nutzung kannst Du dir übrigens ersparen wenn Du es> bereits bei der Deklaration von txt adäquat anpasst.
bei der Deklaration von txt anpassen? oha wie geht das denn? txt ist
doch ein String und kein Pointer. Könntest du mir mal ein Beispiel
geben?
> txt ist doch ein String und kein Pointer.
Strings sind(tm) Pointer. Letztenendes kommt es drauf welchen Zeigertyp
man wie und wie oft braucht.
uint8_t* txt = (uint8_t*)"C9";
[..]
dez = (uint8_t)Hex2Dez(txt, 2);
g457 schrieb:> Strings sind(tm) Pointer.
Nein.
Das sieht man schon daran, daß ein Pointer auf heute üblichen Kisten 4
Byte breit ist, ein String aber auch mehrere Megabyte groß sein kann.
Ergo: Pointer und Strings sind unterschiedliche Dinge.
>> Strings sind(tm) Pointer.>> Nein.
Doch, im Rahmen der Fragestellung ist das korrekt. Für die Allgemeinheit
beachte man das nicht nur zufällig beigefügt "(tm)".
g457 schrieb:> uint8_t* txt = (uint8_t*)"C9";
Achtung, das ist was anderes als das ursprüngliche Beispiel!
Das ursprüngliche Beispiel sähe so aus:
1
constuint8_ttxt[]="C9";
Der Ausdruck txt ist konstant, vom Typ (const uint8_t *) und zeigt auf
die Adresse des ersten Elements des Arrays.
Du hast genau ein Datenobjekt, nämlich txt (3 bytes groß).
Jetzt das andere Beispiel:
1
uint8_t*txt="C9";
txt ist jetzt eine "echte" Variable (ein lvalue) vom typ (uint8_t *) und
wird mit der Adresse eines ansonsten unbenannten Arrays ("C9")
initialisiert.
Du hast jetzt zwei Datenobjekte, zum einen txt, das 2 bytes* groß ist
(da es einen Adresszeiger enthält) und zum anderen ein unbenanntes
Array, das 3 bytes groß ist und die Zeichen 'C', '9' und '\0' enthält.
Grüße
Stefan
* das gilt für die AVR-CPU mit 16 bit-Adressen. Anderswo sind Pointer
auch mal etwas größer.
g457 schrieb:> Doch, im Rahmen der Fragestellung ist das korrekt.
Es ist nie korrekt.
> Für die Allgemeinheit beachte man das nicht nur zufällig beigefügt "(tm)".
Du kannst auch "schnurz" anfügen, es ändert nichts.
Stefan Rand schrieb:> g457 schrieb:>> Strings sind(tm) Pointer.>> Nein.
...
> Ergo: Pointer und Strings sind unterschiedliche Dinge.
Ein C-String ist ein Char-Array. Ein Array ist eine andere Schreibweise
für Pointer. Doch, ein Array ist eigentlich nichts anderes als ein
Pointer.
Stefan Rand schrieb:> Das sieht man schon daran, daß ein Pointer auf heute üblichen Kisten 4> Byte breit ist, ein String aber auch mehrere Megabyte groß sein kann.
Eine Adresse ist 4-Byte groß, wenn der Adressbus 4-Byte groß ist. Ist er
größer oder kleiner, so können das z.B. 2-Byte oder auch 8-Byte sein.
Bei meiner Kiste sind es z.B. 8-Byte, was auch bei einer 64-Bit-Kiste
nicht anders zu erwarten ist.
Die Funktion malloc() kennt ja jeder. Wenn man hier mehrere Megabytes
sich reserviert, wie groß ist dann der zurückgegebene Pointer? - Wenn
ich jetzt so frage, dann habe ich die Frage falsch gestellt. Ich müsste
nämlich fragen, wie viel Speicher ist hinter diesem zurückgegeben
Pointer.
Ergo, eigentlich sind Pointer und Arrays sich gar nicht so
unterschiedlich. Man kann sogar behaupten, sie sind das gleiche.
mar IO schrieb:> Stefan Rand schrieb:>> g457 schrieb:>>> Strings sind(tm) Pointer.>>>> Nein.> ...>> Ergo: Pointer und Strings sind unterschiedliche Dinge.>> Ein C-String ist ein Char-Array. Ein Array ist eine andere Schreibweise> für Pointer. Doch, ein Array ist eigentlich nichts anderes als ein> Pointer.
Dummschwatz.
Ein Array hat Speicherplatz seiner Größe für Inhalte hinterlegt, ein
Pointer hat keinen Speicherplatz für Inhalte hinterlegt.
> Stefan Rand schrieb:>> Das sieht man schon daran, daß ein Pointer auf heute üblichen Kisten 4>> Byte breit ist, ein String aber auch mehrere Megabyte groß sein kann.>> Eine Adresse ist 4-Byte groß, wenn der Adressbus 4-Byte groß ist. Ist er> größer oder kleiner, so können das z.B. 2-Byte oder auch 8-Byte sein.> Bei meiner Kiste sind es z.B. 8-Byte, was auch bei einer 64-Bit-Kiste> nicht anders zu erwarten ist.
Ja und?
> Die Funktion malloc() kennt ja jeder. Wenn man hier mehrere Megabytes> sich reserviert, wie groß ist dann der zurückgegebene Pointer? - Wenn
Eben. Die Zeigerbreite. Nicht die Megabyte. Das Array ist aber Megabyte
groß.
Also: Zeiger ist kein Array.
Mit deiner Denkweise handelst du dir genau die "einfachen" Bugs ein, die
wir hier ständig sehen.
g457 schrieb:> Das Rumcasten bei der Nutzung kannst Du dir übrigens ersparen wenn Du es> bereits bei der Deklaration von txt adäquat anpasst.
Noch besser: Die Funktion hex2dez umdeklarieren, dass das erste Argument
vom Typ (const char *) ist. Dieses Argument ist ein Textstring, ein
Textstring besteht aus Zeichen, und ein Zeichen nun einmal in char und
kein uint8_t. Man ist damit auch kompatibel zu den Funktionen der
Standardbibliothek, bei denen Zeichen und Strings ebenfalls auf
char-Basis deklariert sind.
Zum Thema Array != Pointer gibt es übrigens hier im Forum schon genügend
Threads. Einfach mal die Suchfunktion benutzen.
Leider werden die Begriffe "Pointer" und "Adresse" regelmäßig in einen
Topf geworfen, obwohl es zwei paar Schuhe sind.
1
constchartxt[3]="C9";
2
uint8_tdez=0;
3
dez=(uint8_t)Hex2Dez(txt,2);
In diesem Fall steht "txt" als Platzhalter für die Adresse, an der der
String "C9" im Speicher steht. Es ist aber kein Pointer.
An die Funktion Hex2Dez wird in diesem Beispiel kein Pointer übergeben,
sondern eine Adresse.
Ein Pointer ist eine Speicherstelle, die eine Adresse aufnehmen kann.
Das würde so aussehen:
1
constchartxt[3]="C9";
2
constchar*pointer;
3
uint8_tdez=0;
4
// Den Pointer mit der Adresse des Strings beschreiben
Adresse und Zeiger sind ohnehin zwei gefährliche Begriffe in C...
Man kann sich das aber recht einfach klarmachen:
1
charv[10]="hallo";
2
char*s="Welt";
Beides sind Initialisierer, allerdings für zwei völlig verschiedene
Dinge. Die erste Zeile initialisiert sechs Zeichen in dem Vektor 'v'.
Die zweite Zeile initialisiert einen Zeiger 's'.
Dementsprechend deklarieren folgende Zeilen zwei verschiedene Dinge:
1
charv[10];
2
char*s;
Das bemerkenswerte daran ist lediglich, dass der Vektor 'v' implizit in
einen Zeiger auf sein erstes Element zerfällt, wenn man ihn als r-value
benutzt.
'char v[10];' ist viel eher sowas wie 'struct st_t st;' als 'char *s;'.
Sven P. schrieb:> 'char v[10];' ist viel eher sowas wie 'struct st_t st;' als 'char *s;'.
'char v[10];' ist eher wie ein konstanter Zeiger, wo noch 10 Char's
auf'm Stack reserviert werden 'char * const s = alloca(sizeof(char) *
10);'.
mar IO schrieb:> Sven P. schrieb:>> 'char v[10];' ist viel eher sowas wie 'struct st_t st;' als 'char *s;'.>> 'char v[10];' ist eher wie ein konstanter Zeiger,
Nein bitte nicht. Sagt das keinem Anfänger. Diese ganzen 'ein Array ist
eigentlich fast wie ein Zeiger' Aussagen sind allesamt höchst gefährlich
und verwirren Anfänger mehr als notwendig
1
char v[6] = "Hallo";
2
3
4
v
5
+---+---+---+---+---+---+
6
| H | a | l | l | o | \0|
7
+---+---+---+---+---+---+
8
9
10
11
char* c = "Hallo";
12
13
14
c
15
+------+ +---+---+---+---+---+---+
16
| o----------->| H | a | l | l | o | \0|
17
+------+ +---+---+---+---+---+---+
2 komplett verschiedene Dinge.
lediglich in der Verwendung degeneriert der Name eines Arrays zu seiner
Startadresse und nicht wie bei anderen Variablentypen zu ihrem Inhalt.
Bronco hat schon recht: Gerade für einen Anfänger ist es am allerbesten
die Begriffe "Adresse" und "Pointer" streng auseinanderzuhalten. Auch
wenn sich diese strenge Sprechweise im Laufe der Zeit immer mehr
verwischt.
EIne Adresse ist ein Wert, der in einer Variablen vom Typ Pointer
gespeichert werden kann.
Genauso wie ein Integer ein Wert ist, der in einer Integer-Variablen
gespeichert werden kann. Nur dass wir für 'INteger-Variablen' keinen
eigenen Begriff haben, weil aus dem Zusammenhang immer klar ist, was
gemeint ist. Das ist zwar bei Pointer im Grunde auch so, trotzdem
verwirrt es die Anfänger regelmässig, weswegen man die Begriffe Adresse
und Pointer(-Variable) besser von Anfang an streng trennt.
warning: initialization discards 'const' qualifier from pointer
2
> target type
>> Hier ist wohl gemeint>
1
constchar*c="Hallo";
Ja richtig.
Ist der Eile geschuldet, in der ich getippt habe.
> Ab GCC 4.0 wird kein -fwritable-strings, das nicht dem C-Standard> entsprach, mehr unterstützt.
Endlich!
Genau so soll es sein.
Diese Unsitte, für Character-Literale eine Ausnahme im Standard zu
machen, ist mir sowieso schon immer sauer aufgestossen.
mar IO schrieb:> Sven P. schrieb:>> 'char v[10];' ist viel eher sowas wie 'struct st_t st;' als 'char *s;'.>> 'char v[10];' ist eher wie ein konstanter Zeiger, wo noch 10 Char's> auf'm Stack reserviert werden 'char * const s = alloca(sizeof(char) *> 10);'.
Vorsicht damit...
Sven P. schrieb:> Vorsicht damit...>> char *s = "Hallo";> char v[10] = "Welt";>> printf("%p <--> %p\n", s, &s);> printf("%p <--> %p\n", v, &v);>> 0x4006c4 <--> 0x7fff8429f978> 0x7fff8429f980 <--> 0x7fff8429f980
Wobei v und &v unterschiedlichen Typs sind: v ist ein char* während &v
vom Typ char(*)[10] ist.