mikrocontroller.net

Forum: Compiler & IDEs Pointer und C


Autor: mikes (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich arbeite mich gerade in c ein und bin nun noch nicht ganz Freund mit
Pointern und Adressoperatoren.
Nun möchte ich ein char array "Rücksetzen" sprich alle Werte 0 setzen
und dies mit einer Fkt. realisieren um die Arbeit mit Pointern &Co. zu
üben. Das Array soll mal 4 chars beinhalten.


void array_loeschen(uchar *nr ){
    uchar i;
    for(i=0 ; i>3 ; i++)
          nr[i]=0;
}

///vorher: 2,3,4,5

//Aufruf:

array_loeschen(&nummern);

//danach: 0,0,0,0 ??


Was mache ich falsch, denn es läuft so nicht?

Dank und Gruß

Autor: Daniel J. (deejay)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

Du machst zwei Sachen falsch:
Zum einen sollte der Aufruf der Lösch-Funktion array_loeschen(nummern);
lauten, da ein Array ja nichts anderes als ein Pointer ist.
Das zweite: Schau Dir mal die Abbruch-Bedingung der for-Schleife an...

MfG
  Daniel Jelkmann

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

dein Problem sind nicht die Pointer sondern du hast noch nicht
verstanden wie eine for-Schleife funktioniert. Ich geb dir mal einen
Tip:

         vvvvv
          vvv
           v
for(i=0 ; i>3 ; i++)
           ^
          ^^^
         ^^^^^

;-)

Matthias

Autor: mikes (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ups,

doch, ich hab die for Schleife schon verstanden (da ist ja auch nicht
viel dran), war halt nur ein >< Dreher.

Danke

Autor: Daniel J. (deejay)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

selbst wenn es nur ein ><-Dreher war, ist die Bedingung für 4 Zeichen
immer noch falsch ;-)

MfG
 Daniel Jelkmann

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nö...

Autor: Daniel J. (deejay)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

wie nö?
Wenn da steht: for(i=0 ; i>3 ; i++) und es ein Dreher war, dann müsste
er ja "for(i=0 ; i<3 ; i++)" gemeint haben. Damit läuft die Schleife
von 0 über 1 bis zum Index i=2... Das sind drei Zeichen... Das Array
besteht aber laut Posting aus 4 Zeichen...
Oder bezog sich das "nö" jetzt auf einen anderen Beitrag?

MfG
  Daniel Jelkmann

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry, hast Recht...

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn "nummern" ein Array ist (z.B. char nummern[4]), dann ist
&nummern
der Pointer auf den Pointer des 0.en Elements.
Was du willst müsste dann entweder
array_loeschen(&nummern[0]);
oder einfach nur
array_loeschen(nummern);
lauten.

Wenn Nummern eine Variable ist (char nummern) ist zwar der Aufruf
richtig, aber dann würde die Schleife ins Nirvana laufen...

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wenn "nummern" ein Array ist (z.B. char nummern[4]), dann ist
> &nummern
> der Pointer auf den Pointer des 0.en Elements.

Nein.

Zwar zeugt &nummern davon, dass man das Kapitel über Pointer&Arrays im
K&R noch nicht verstanden hat, aber es ergibt denselben Code wie
nummern:

$ cat foo.c
int nummern[4];

void *
a(void)
{
        return (void *)&nummern;
}

void *
b(void)
{
        return (void *)nummern;
}
$ avr-gcc -Os -S foo.c
$ cat foo.s
...
.global a
        .type   a, @function
a:
        ldi r24,lo8(nummern)
        ldi r25,hi8(nummern)
        ret
        .size   a, .-a

.global b
        .type   b, @function
b:
        ldi r24,lo8(nummern)
        ldi r25,hi8(nummern)
        ret
        .size   b, .-b
...

(Mal alle Kommentare rausgeworfen wegen der Übersichtlichkeit.)

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist interessant. Soweit ich mich erinnere dürfte das laut ANSI-C
nicht die selbe Wirkung haben.
Vielleicht unterstützt der AVR-GCC gar keine Pointer auf Pointer. Sieht
auf alle Fälle so aus. Oder er optimiert das weg (-Os)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Das ist interessant. Soweit ich mich erinnere dürfte das laut ANSI-C
> nicht die selbe Wirkung haben.

Deine Erinnerung ist falsch.

> Vielleicht unterstützt der AVR-GCC gar keine Pointer auf Pointer.

Mach's mal halblang.  Der GCC ist ein C-Compiler und keine Krücke.

>  Sieht auf alle Fälle so aus. Oder er optimiert das weg (-Os)

Nein, mit -O0 hat man lediglich ein bisschen Stackframe-Gewurschtel
zusätzlich.

Glaub's mal, die Adresse eine Arrays ist einfach mal kein Zeiger auf
einen Zeiger.  Auch wenn man ein Array meistenteils wie einen Zeiger
benutzen kann: beides ist nicht identisch.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Soweit ich mich erinnere dürfte das laut ANSI-C nicht die selbe
> Wirkung haben.

Doch, muß es sogar.

"return (void *)nummern;" ergibt die Adresse des ersten Elements des
Arrays.

"return (void *)&nummern;" ergibt die Adresse des Arrays selbst.

Das erste Element ist immer an der Startadresse des Arrays, also sind
die Adressen gleich.

> Vielleicht unterstützt der AVR-GCC gar keine Pointer auf Pointer.

Doch, tut er. Aber in dem Programm kommen keine vor. Du hast Zeiger und
Arrays auch noch nicht verstanden. Wichtig ist, daß ein Zeiger eben ganz
und gar nicht dasselbe wie ein Array ist. Sie verhalten sich oft gleich,
aber eben nicht immer.


@mikes:
Was spricht eigentlich dagegen, memset zu verwenden?

Autor: Tobi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wichtig ist, daß ein Zeiger eben ganz
>und gar nicht dasselbe wie ein Array ist.

Natürlich nicht, das sind ja auch im Speicher aufeinanderfolgende
Variablen. Schließlich ist ja auch eine Variable auch kein Zeiger (z.B.
char Foo).

C behandelt halt Arrays speziell und mit "nummern" (wenn das wieder
das Array von vorhin ist) ist die Adresse vom 0. Element gemeint.

Nun dachte ich (was ja falsch ist, ich seh's ja ein!), das &nummern
gleichzusetzen ist, wie z.B.

int Addr; //Hält dann den Wert der Adresse
char *Zeichen //Pointer
...
Addr=&Zeichen //das wäre dann Pointer auf Pointer
...

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

> int Addr; //Hält dann den Wert der Adresse
> char *Zeichen //Pointer
> ...
> Addr=&Zeichen //das wäre dann Pointer auf Pointer
> ...

Addr ist kein Pointer auf einen Pointer da Addr gar kein Pointer ist
:-) Addr enthält nur die Adresse der Variablen Zeichen. Ob Zeichen
jetzt ein char, ein int, ein foo oder ein *foo ist spielt dabei
überhaupt keine Rolle.

Matthias

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal ne andere Lösung.

void array_loeschen(unsigned char *buffer )
{
 memset(&buffer,0,sizeof(buffer));
}

Autor: Μαtthias W. (matthias) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

und was soll das auser einem Programmierfehler sein? ;-)

Dein Code "nullt" die ersten n Elemente von Buffer wobei n dabei nur
von der Größe des Pointers (also typischerweise 2, 4 oder 8 Byte)
abhängig ist.

Matthias

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Mal ne andere Lösung.

> void array_loeschen(unsigned char *buffer )
> {
>  memset(&buffer,0,sizeof(buffer));
> }

Kann nicht funktionieren.  Würde nur zwei Bytes in buffer löschen, da
sizeof(buffer) == 2 ist.

Innerhalb einer Funktion geht das mit sizeof() so gar nicht, da der
generierte Code nicht wissen kann, wie groß das Array hinter dem
übergebenen Zeiger ist.  Mit einem Makro würde es gehen:

#define array_loeschen(ary) memset((ary), 0, sizeof(ary))

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Müßte aber so funktionieren.

void array_loeschen(unsigned char *buffer )
{
 memset(&buffer,0,strlen(buffer));
}

Autor: Daniel J. (deejay)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

memset(&buffer,0,strlen(buffer)) macht wohl nur in bestimmten Fällen,
das was man erwarten würde. Wenn das erste Zeichen im Array eine Null
ist, bewirkt die Funktion beispielsweise garnichts, dh. die restlichen
Array-Elemente bleiben unverändert.
Ich glaube oben war nie die Rede davon, dass buffer einen String
enthält...
Daher hilft es nur, die Array-Größe zu übergeben oder eben soetwas zu
basteln, wie Jörg es vorgeschlagen hat.

MfG
 Daniel Jelkmann

Autor: Stefan Kleinwort (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Thomas:
Wie ermittelt strlen die Größe von buffer? Indem es nach einer Null im
String sucht.

Bei diesem Beispiel handelt es sich aber nicht um einen String, d.h. Du
kannst auch nicht erwarten, dass irgendwo eine Null steht. Im
schlimmsten Fall wird gelöscht, bis irgendwo in einer Variablen Null
steht. Bis dahin wird alles gelöscht - und nicht nur buffer.

Beim anderen Extrem steht gleich am Anfang von buffer eine Null. Dann
wird garnichts gelöscht - auch wenn der Rest des Arrays ungleich Null
ist.

Die saubere Lösung - meiner Meinung nach:
Mit typedef sich einen Buffertyp definieren. Den Funktionen als
Übergabeparameter keinen Ptr auf *uchar o.ä. mitgeben sondern einen Ptr
auf den Buffertyp. Dann funktioniert auch sizeof überall - auch
innerhalb von Funktionen.

Gruß, Stefan

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Thomas

> void array_loeschen(unsigned char *buffer )
> {
>   memset(&buffer,0,strlen(buffer));
> }

Das kannst Du auch einfacher haben

void array_loeschen(unsigned char *buffer )
{
  buffer[0] = '\0';
}

Es war nie die Rede von einem Buffer fuer einen String.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan

Du musst aber vorsichtig sein. Der sizeof muss
auf *Ptr losgelassen werden. Sonst hast Du wieder
nur einen sizeof von einem Pointer

typedef unsigned char buffer_t[64];

void array_loeschen( buffer_t * buffer )
{
  memset( &buffer, 0, sizeof( *buffer ) );
}

Hat halt einen Nachteil: geht nur mit buffer_t Arrays.

Wenns allgemein sein soll, hilft alles nichts: Die Groesse
des Arrays muss Parameter sein. Alles andere ist eine
Einladung zu Fehlern.
Genau dieses ist auch das Problem mit der Standard-Funktion
gets(). Da sie die Array-Groesse nicht mitbekommt, hat sie
keine Chance sich gegen misbrauch zu wehren. Daher gets()
ist fuer Programmierer eine no-no Funktion. Immer fgets()
verwenden. Bei der wurde dieser Kardinalfehler in der
Argumentliste nicht gemacht.

Autor: Stefan Kleinwort (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Karl Heinz,

> Du musst aber vorsichtig sein. Der sizeof muss
> auf *Ptr losgelassen werden. Sonst hast Du wieder
> nur einen sizeof von einem Pointer

Klar ;-)
Ehrlich gesagt war ich zu faul, die Lösung gleich komplett
auszuformulieren.

> Hat halt einen Nachteil: geht nur mit buffer_t Arrays.

Das kann auch ein Vorteil sein ... es gibt einen Fehler, wenn man
Schrott übergibt ...

Viele Grüße, Stefan

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In C++ könnte man sich auch ein Template basteln:

template<typename T, size_t Size>
static inline void array_loeschen(T (&buffer)[Size])
{
    memset(buffer, 0, sizeof(T) * Size);
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Hat halt einen Nachteil: geht nur mit buffer_t Arrays.
>
> Das kann auch ein Vorteil sein ... es gibt einen Fehler, wenn man
> Schrott übergibt ...

Auch hier wieder:
Extreme Vorsicht. Es ist absolut wichtig hier einen Pointer
zu uebergeben.

zb. Kompiliert dieses:

#include <memory.h>

typedef unsigned char buffer_t[64];

void array_loeschen( buffer_t buffer )
{
  memset( &buffer, 0, sizeof( buffer ) );
}

int main()
{
  unsigned char Test[32];

  array_loeschen( Test );
}

ohne Probleme.
typedef, im Gegensatz zu seiner Bezeichnung, definiert eben
keinen neuen Typ, sondern erzeugt nur einen Alias dafuer.
Im obigen Gegen-Beispiel ist es fuer den Compiler immer noch so,
dass er jeden beliebigen Pointer auf unsigned char fuer
buffer_t akzeptiert. Denn buffer_t ist nur ein anderer Name
fuer ein unsigned char Array, und wie wir alle wissen werden
Arrays immer per Pointer uebergeben. d.h. ob Du

void array_loeschen( buffer_t buffer )

schreibst, oder

void array_loeschen( unsigned char* buffer )

oder

void array_loeschen( unsigned char buffer[] )

macht fuer den Compiler keinen Unterschied.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tschuldigung. Die fehlerhafte Version muss natuerlich

void array_loeschen( buffer_t buffer )
{
  memset( buffer, 0, sizeof( *buffer ) );
}

lauten.
Ist zwar in der Argumentliste immer noch problematisch, jedoch
ist der memset jetzt korrekt.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.