Morgen liebe Leute, ich habe mal ein Verständnisfrage.
Und zwar geht es um Funktionen, die einen String bearbeiten - hier
erstmal die Aufrufe:
Ich rufe die Funktion auf und übergebe einen Textstring:
1
lass_string_blinken("TEST1");
Diese Funktion wird aufgerufen und bekommt den String:
1
voidlass_string_blinken(*string)
2
{
3
...wennAN-ZEIT...
4
{
5
sende_string_zum_lcd(string);
6
}
7
8
...wennAUS-ZEIT...
9
{
10
loesche_Zeile(entsprechnende_Zeile);
11
}
12
}
Und diese unktion gibt den String aus:
1
voidsende_string_zum_lcd(char*string)
2
{
3
while(*string!=0)
4
{
5
lcd_send_alphanum(*string++);
6
}
7
}
So...mein Frage ist nun, wie ist das mit den Pointern? So, wie es jetzt
ist, funktioniert es einwandfrei. Aber es war mehr try-and-error. Ich
habe jetzt schon recht lang versucht, mir diese Pointer-Arithmetik
anzulesen, aber ich tue mich damit sehr schwer...ich raff einfach nicht,
wann man Adressen un wann man Inhalte übergibt.
Im ersten Funktionsaufruf übergebe ich doch quasi automatisch die
Anfangsadresse meines Strings "TEST1" an die Funktion zum Blinkenlassen.
Diese nimmt die Adresse als *string, also den Inhalt an der Adresse auf,
oder sehe ich das falsch?
Von hier wird die Funktion zum Senden der einzelnen Zeichen aufgerufen,
aber wieso übergebe ich den Parameter nun einfach als normale Variable,
wo die Funktion zum Senden diese wieder als Pointer übernimmt?
Kann mir da einer vielleicht etwas Licht ins dunkel bringen? Mit den
Zeigern tue ich mich leider nach wie vor etwas schwer...
Danke schonmal im Voraus!
Dietmar Hersch schrieb:> So...mein Frage ist nun, wie ist das mit den Pointern?
Gaaaanz einfach.
> Im ersten Funktionsaufruf übergebe ich doch quasi automatisch die> Anfangsadresse meines Strings "TEST1" an die Funktion zum Blinkenlassen.
Es ist gaz einfach.
In C wird an eine Funktion immer per Value übergeben.
D.h. die Funktion bekommt den Wert selber
machst du
1
voidfoo(inti)
2
{
3
i=3;
4
}
5
6
intmain()
7
{
8
intj=8;
9
10
foo(j);
11
}
dann wird beim Aufruf der Funktion der Wert von j ermittelt (= 8) und
dieser Wert an die Funktion übergeben. Die Funktion selbst erzeugt sich
eine neue Variable namens i, in der dieser Wert abgelegt wird und somit
innerhalb der Funktion zur Verfügung steht. Damit ist auch klar: wird i
innerhalb der Funktion verändert, so hat dies keine Auswirkungen auf j.
Die beiden sind in keiner Weise miteinander assoziiert.
Das ist der Standardmechanismus mit dem in C Argumente an Funktionen
übergeben werden.
Mit einer Ausnahme:
Bei Arrays ist das anders. Arrays werden an Funktionen übergeben, indem
die Startadresse des Arrays an die Funktion übergeben wird.
1
voidfoo(int*a)
2
{
3
a[0]=5;
4
...
5
}
6
7
intmain()
8
{
9
intj[5];
10
11
foo(j);
12
}
Dadurch, dass die Funktion die Startadresse des Arrays j selber hat,
kann die Funktion über diese Startadresse das Arrays selber verändern.
C-Strings sind auch nichts anderes als Arrays. Arrays von einzelnen
Charactern, die zusammen genommen den Text ergeben.
> Diese nimmt die Adresse als *string, also den Inhalt an der Adresse auf,> oder sehe ich das falsch?
Sie nimmt ihn in string auf, nicht *string. *strig wäre schon das
Zeichen, auf das dieser Pointer zeigt.
> Von hier wird die Funktion zum Senden der einzelnen Zeichen aufgerufen,> aber wieso übergebe ich den Parameter nun einfach als normale Variable,
weil string eine Pointer Variable ist und du den Pointer in string hast
> wo die Funktion zum Senden diese wieder als Pointer übernimmt?
Passt doch
string ist die Pointer Variable. Also übergibst du auch den Pointer.
> Kann mir da einer vielleicht etwas Licht ins dunkel bringen? Mit den
in
char * string
ist string die Variable, die die Adresse enthält! *string ist das
Zeichen an dieser Adresse! Bei der Deklaration gehört der * logisch
gesehen eher zum Datentyp als zur Variablen.
int *j;
j ist ein Zeiger_auf_int.
Und nicht *j ist ein Zeiger auf int.
Syntaxmässig hat man sich aber entschieden, dass der * an den Namen der
Variablen bindet, so dass
int * j, i;
zwar j als Pointer deklariert, aber nicht i. Und daher schreiben viele
Programmierer den * beim Variablennamen um da nicht hineinzufallen.
int *j, *i;
C++ Programmierer tendieren des öfteren dazu, den * beim Datentyp zu
lassen und diese Falle dadurch zu umgehen, dass sie ganz einfach nicht
mehrere Variablen in einer Deklaration machen.
int* j;
int i;
damit ist dann auch alles klar.
(Die C Sichtweise war: In
double *k;
erhält man einen double, wenn man auf k die Operation * anwendet.
double l = *k;
Daher kommt diese etwas seltsame Semantik in Deklarationen, in der
mehrere Variablen unterschiedliche Datentypen in ein und derselben
Deklaration haben können.
double *k, l, *m, *n, o;
k, m und n sind Pointer
l und o sind normale double Variablen.
)
Literatur und üben!
Dietmar Hersch schrieb:> lass_string_blinken ("TEST1");
Was übergebe ich hier an die nächste Funktion? Adresse oder Inhalt?
Adresse, oder?
Dietmar Hersch schrieb:> void lass_string_blinken (*string)
Diese Funktion nimmt die Adresse auf, das Argument ist nun aber *string,
also ist der Inhalt gemeint? Oder hat das damit garnichts zu tun?
Dietmar Hersch schrieb:> sende_string_zum_lcd (string);
Hier ist es ja nur wieder die Variable, allerdings war es vorher ja noch
*string...diesmal übergebe ich aber nur string - was ist string an
dieser Stelle? Adresse oder ist hier der Inhalt drin? Ich müsste ja
eigentlich weiterhin die Adresse übergeben...
Dietmar Hersch schrieb:> void sende_string_zum_lcd (char *string)> {> while (*string != 0)> {> lcd_send_alphanum (*string++);
Und hier ist ja dann ganz klar der Inhalt gemeint! Weil es wird ja
überprüft, ob der Inhalt nicht die '\0' ist und solange Zeichen für
Zeichen übermittelt - also garantiert der Inhalt.
Trotzdem ist mir das ein kleines Rätsel - ich weiß nie genau, wann der
Inhalt und wann die Adresse benutzt wird. Und ich weiß nicht, wieviel
ich mittlerweile schon gelesen hab darüber, aber das gibt bei mir immer
nur ne Hirnverknotung und irgendwann ist Schluss - ich denke mal, wenn
man es einmal verstanden hat, dann isses bestimmt logisch, aber.....
Manchmal wird doch auch mit &variable eine Adresse vergeben, passiert
dies bei einem String automatisch?
Vielen Dank auf jeden Fall schonmal für deinen Beitrag!
Dietmar Hersch schrieb:> Dietmar Hersch schrieb:>> lass_string_blinken ("TEST1");>> Was übergebe ich hier an die nächste Funktion? Adresse oder Inhalt?> Adresse, oder?
Adresse
> Dietmar Hersch schrieb:>> void lass_string_blinken (*string)>> Diese Funktion nimmt die Adresse auf, das Argument ist nun aber *string,> also ist der Inhalt gemeint? Oder hat das damit garnichts zu tun?
Das hat gar nichts damit zu tun.
Die korrekte Funktionssignatur wäre
void lass_string_blinken (char *string)
und das sagt lediglich aus, dass string eine Variable ist, nämlich eine
Variable vom Datentyp: Pointer auf Character.
>> sende_string_zum_lcd (string);>> Hier ist es ja nur wieder die Variable
Das ist ja auch ein Funktionsaufruf.
Die Funktion send_string_zum_lcd wird aufgerufen und ihr wird der Inhalt
der Variablen 'string' (welcher eine Adresse ist, weil string ja vom
Datentyp 'Pointer auf Character' ist) übergeben.
, allerdings war es vorher ja noch
> *string...
void lass_string_blinken (char *string)
hier ist es ein Funktionsheader. Und hier muss festgelegt werden,
welchen Datentyp string hat. Nämlich 'Pointer auf Character'
>> void sende_string_zum_lcd (char *string)>> {>> while (*string != 0)>> {>> lcd_send_alphanum (*string++);>> Und hier ist ja dann ganz klar der Inhalt gemeint!
Richtig.
*string ist das Zeichen, das man erhält, wenn man unter der Adresse im
Speicher nachsieht, die in string steht.
Du musst unterscheiden zwischen der Schreibweise bei der Definition
einer Variablen (dort wird der Datentyp festgelegt) und der
tatsächlichen Verwendung.
Dietmar Hersch schrieb:> Dietmar Hersch schrieb:>> lass_string_blinken ("TEST1");>> Was übergebe ich hier an die nächste Funktion? Adresse oder Inhalt?> Adresse, oder?
Japp, Du übergibt die Adresse des ersten Zeichens der Zeichenfolge.
> Dietmar Hersch schrieb:>> void lass_string_blinken (*string)>> Diese Funktion nimmt die Adresse auf, das Argument ist nun aber *string,> also ist der Inhalt gemeint? Oder hat das damit garnichts zu tun?
Das ist eine reine Deklaration zur Festlegung der im Funktionsbody
benutzten lokalen Variablen, diese definiert die Variable "string" als
einen "Pointer auf den Typ ???" ... da müsste der Compiler eigentlich
meckern ;)
> Dietmar Hersch schrieb:>> sende_string_zum_lcd (string);>> Hier ist es ja nur wieder die Variable, allerdings war es vorher ja noch> *string...diesmal übergebe ich aber nur string - was ist string an> dieser Stelle? Adresse oder ist hier der Inhalt drin? Ich müsste ja> eigentlich weiterhin die Adresse übergeben...
Weiterhin die Adresse, die Deklaration von "sende_string_zum_lcd()"
erwartet ja einen solchen.
> Dietmar Hersch schrieb:>> void sende_string_zum_lcd (char *string)>> {>> while (*string != 0)>> {>> lcd_send_alphanum (*string++);>> Und hier ist ja dann ganz klar der Inhalt gemeint! Weil es wird ja> überprüft, ob der Inhalt nicht die '\0' ist und solange Zeichen für> Zeichen übermittelt - also garantiert der Inhalt.
Japp, diesmal dient der "*" dazu den Pointer zu dereferenzieren, sprich
den Wert an der Adresse zu ermitteln.
> Trotzdem ist mir das ein kleines Rätsel - ich weiß nie genau, wann der> Inhalt und wann die Adresse benutzt wird. Und ich weiß nicht, wieviel> ich mittlerweile schon gelesen hab darüber, aber das gibt bei mir immer> nur ne Hirnverknotung und irgendwann ist Schluss - ich denke mal, wenn> man es einmal verstanden hat, dann isses bestimmt logisch, aber.....
Ist eigentlich ganz einfach und nur ne Gewohnheitssache.
Willst Du einen Pointer übergeben musst Du dies mit einem "*" in der
Deklaration dem Compiler verständlich machen, egal ob dies nun eine
Variablen- oder Funktionsdeklaration ist (diese erzeugt intern ja auch
nichts anderes als lokale Variablen).
Willst Du auf den Wert unter einem Pointer zugreifen musst Du explizit
ein "*" voranstellen.
> Manchmal wird doch auch mit &variable eine Adresse vergeben, passiert> dies bei einem String automatisch?
Es wird immer der Datentyp der Variablen genutzt - solang nicht mit
expliziten Typecasts gearbeitet wird. Und da Deine Variable "string" als
"char *" definiert wurde wird beim übergeben immer dieser Typ
angenommen.
Peter Stegemann schrieb:> Sammle ein paar Wochen Erfahrung mit Assembler
Ich sammle gerade Erfahrungen mit C :-) Erstmal eins verstehen -
Assembler wäre natürlich noch besser.
Eine andere Sache noch, wo wir gerade dabei sind...
Ich habe einen float, der besteht aus 4 Bytes - wenn ich nun die vier
einzelnen Bytes dieses Floats haben will, dann kann ich das doch auch
über die Adresse machen, oder? Müsste doch dann gehen.
z.B.:
1
floatwert=-35.7736
2
uint8_tbuffer[4];
3
4
float_to_uint8(wert,&buffer)
5
6
voidfloat_to_uint8(floatfloatwert,uint8_t*buffer)
7
{
8
uint8_ti=3;
9
10
for(i=0;i<4;i++)
11
{
12
buffer[i]=(uint8_t)floatwert++;
13
}
14
}
Stimmt das so in etwa, oder wie würde man das machen?
Dietmar Hersch schrieb:> Eine andere Sache noch, wo wir gerade dabei sind...>> Ich habe einen float, der besteht aus 4 Bytes - wenn ich nun die vier> einzelnen Bytes dieses Floats haben will, dann kann ich das doch auch> über die Adresse machen, oder?
Kann man, ja.
> float wert = -35.7736> uint8_t buffer[4];>> float_to_uint8 (wert, &buffer)
Noch mal.
Arrays werden immer übergeben, indem die Startadresse des Arrays
übergeben wird. Der & ist hier an dieser Stelle überflüssig, weil buffer
ein Array ist!
> void float_to_uint8 (float floatwert, uint8_t *buffer)> {> uint8_t i = 3;>> for (i = 0; i < 4; i++)> {> buffer[i] = (uint8_t) floatwert++;> }> }
Aber nicht so.
floatwert ist ja noch wie vor ein float Wert.
Durch Nennung des Variablennamens erhältst du den Wert in der Variablen
... welcher entsprechend dem Datentyp ein float ist.
Du würdest dich sehr wundern, wenn in
1
NettoPreis=SteuerSatz*BrottoPreis;
auf einmal völlig unmotiviert mit einer Speicheradresse gerechnet werden
würde, anstatt mit den Inhalten von SteuerSatz bzw. BruttoPreis
> Stimmt das so in etwa, oder wie würde man das machen?
zb so
Oh je...
Karl heinz Buchegger schrieb:> void float_to_uint8 (float floatwert, uint8_t *buffer)> {> uint8_t i;>> for (i = 0; i < sizeof(floatwert); i++)> buffer[i] = *((uint8_t*)&floatwert) + i );> }
Ok, also ich versuchs mal (vielen Dank für die Geduld bis jetzt
schonmal!!!):
Der "sizeof" liefert mir ja die Anzahl in Bytes zurück, richtig? Also
ergibt sizeof "4" - Die Schleife wird also 4-mal durchlaufen.
buffer[i] = *((uint8_t*)&floatwert) + i);
Der erste *Stern sorgt hier dafür, dass das Resultatden INHALT
wiedergibt.
Mit (uint8_t*) caste ich das Ergebnis ja auf ein unsigned Byte, der
*Stern hier ist mir nicht klar. Eine öffnende Klammer fehlt, ich denke
sie kommt dann einfach noch hinter den ersten Stern.
&floatwert bezeichnet ja die Adresse - das +i schiebt dann die Adresse
weiter?
Ihr seht, ich hab schon Probleme, eine der drei Varianten direkt zu
verstehen...und ihr schüttelt mal eben drei Stück einfach so raus :)
Dietmar Hersch schrieb:> Der "sizeof" liefert mir ja die Anzahl in Bytes zurück, richtig? Also> ergibt sizeof "4" - Die Schleife wird also 4-mal durchlaufen.
richtig.
Da aber niemand garantiert dass ein float immer 4 Bytes hat, bin ich
hier auf der sicheren Seite. Sollte das einmal anders sein, dann
erledigt der Compiler die Anpassung, indem sizeof einen anderen Wert
ergibt.
-> lass im Zweifelsfall immer den Compiler für dich arbeiten!
>> buffer[i] = *((uint8_t*)&floatwert) + i);>> Der erste *Stern sorgt hier dafür, dass das Resultatden INHALT> wiedergibt.
Du gehst das falsch an.
Fang beim Variablennamen an und arbeite dich von innen nach aussen durch
floatwert ist ein float
&floatwert ist die Adresse, wo dieser float abgespeichert ist.
vulgo: dieser Ausdruck ergibt einen Pointer auf float
(uint8_t*)&floatwert aus diesem Pointer auf float wird ein
Pointer of uint8_t. Der Zahlenwert der Adresse
ist immer noch derselbe, aber der Datentyp
hat sich geändert.
((uint8_t*)&floatwert) + i von dieser Adresse (auf uint8_t) gehst
dann i Stück uint8_t weiter im Speicher.
*(((uint8_t*)&floatwert) + i) und von der Adresse, an der wir dann
sind, von dort wird gelesen.
Gelesen wird ein uint8_t, weil ja der
Pointer (+Offset) auch zu einem
uint8_t* wurde.
Dietmar Hersch schrieb:> Eine öffnende Klammer fehlt, ich denke> sie kommt dann einfach noch hinter den ersten Stern.>
Uuups.
Kommt vom direkten Schreiben hier im Forum.
Ja da fehlt eine. Und ja, es muss
*(((uint8_t*)&floatwert) + i)
heissen.