Forum: Compiler & IDEs Casten macht Probleme


von Hauke (Gast)


Lesenswert?

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?

von g457 (Gast)


Lesenswert?

> 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);
                                 ^

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Hauke schrieb:
> char txt[2] = "C9";

Achtung: Das braucht 3 Byte, die terminierende 0 zählt auch mit.

von Hauke (Gast)


Lesenswert?

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?

von g457 (Gast)


Lesenswert?

> 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);
                               ^

von Hauke (Gast)


Lesenswert?

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?

von g457 (Gast)


Lesenswert?

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

von Hauke (Gast)


Lesenswert?

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?

von g457 (Gast)


Lesenswert?

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

von Hauke (Gast)


Lesenswert?

g457 schrieb:
> uint8_t* txt = (uint8_t*)"C9";
> [..]
> dez = (uint8_t)Hex2Dez(txt, 2);

ah cool :)

vielen dank für die hilfe!

von Stefan R. (srand)


Lesenswert?

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.

von g457 (Gast)


Lesenswert?

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

von Stefan W. (dl6dx)


Lesenswert?

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
const uint8_t txt[] = "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.

von Stefan R. (srand)


Lesenswert?

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.

von mar IO (Gast)


Lesenswert?

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.

von Stefan R. (srand)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

von Bronco (Gast)


Lesenswert?

Leider werden die Begriffe "Pointer" und "Adresse" regelmäßig in einen 
Topf geworfen, obwohl es zwei paar Schuhe sind.
1
const char txt[3] = "C9";
2
uint8_t dez = 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
const char txt[3] = "C9";
2
const char* pointer;
3
uint8_t dez = 0;
4
// Den Pointer mit der Adresse des Strings beschreiben
5
pointer = txt;
6
// Einen Pointer übergeben
7
dez = (uint8_t)Hex2Dez(pointer,2);

von Sven P. (Gast)


Lesenswert?

Adresse und Zeiger sind ohnehin zwei gefährliche Begriffe in C...

Man kann sich das aber recht einfach klarmachen:
1
char v[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
char v[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;'.

von mar IO (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> char* c = "Hallo";
1
warning: initialization discards 'const' qualifier from pointer target type

Hier ist wohl gemeint
1
const char *c = "Hallo";

Ab GCC 4.0 wird kein -fwritable-strings, das nicht dem C-Standard 
entsprach, mehr unterstützt.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:
> Karl Heinz Buchegger schrieb:
>
>> char* c = "Hallo";
>
>
1
warning: initialization discards 'const' qualifier from pointer 
2
> target type
>
> Hier ist wohl gemeint
>
1
const char *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.

von Sven P. (Gast)


Lesenswert?

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...
1
  char *s = "Hallo";
2
  char v[10] = "Welt";
3
4
  printf("%p  <-->  %p\n", s, &s);
5
  printf("%p  <-->  %p\n", v, &v);
1
kamajii:~/temp $ c test.c
2
kamajii:~/temp $ ./a.out 
3
0x4006c4  <-->  0x7fff8429f978
4
0x7fff8429f980  <-->  0x7fff8429f980

von mar IO (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Nein bitte nicht. Sagt das keinem Anfänger.

Ja, ich weiß. Für mich ist ein Array einfach eine Aneinanderreihung des 
selben Datentyps... Die Sachen mit Initialisierung usw. ziehe ich gar 
nicht in Betracht, sondern nur die Verwendung. Darum auch die Rede von 
"... eigentlich andere Schreibweise von ...".

Und mit Verwendung meine ich eigentlich das:

http://openbook.galileocomputing.de/c_von_a_bis_z/012_c_zeiger_012.htm#mjeac6320125efac0480883292b05b7c46

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

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.