Casten macht Probleme

Gast #3221827
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?
Gast #3221838
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);
                                 ^
Gast #3221849
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);
                               ^
Gast #3221861
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.
Gast #3221865
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?
Gast #3221883
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);
#3221918
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.
Gast #3221968
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.
#3222034
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.
Moderator #3222096
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.
Gast #3226066
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);
Gast #3228135
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;'.
#3228193
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.
#3228231
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.
Gast #3228258
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
Gast #3228304
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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren