Forum: Compiler & IDEs Pointer und C


von mikes (Gast)


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ß

von Daniel J. (deejay)


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

von Μαtthias W. (matthias) Benutzerseite


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

von mikes (Gast)


Lesenswert?

ups,

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

Danke

von Daniel J. (deejay)


Lesenswert?

Hi,

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

MfG
 Daniel Jelkmann

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Nö...

von Daniel J. (deejay)


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

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

Sorry, hast Recht...

von Tobi (Gast)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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

von Tobi (Gast)


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)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Rolf Magnus (Gast)


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?

von Tobi (Gast)


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

von Μαtthias W. (matthias) Benutzerseite


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

von Markus (Gast)


Lesenswert?

Mal ne andere Lösung.

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

von Μαtthias W. (matthias) Benutzerseite


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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

von Thomas (Gast)


Lesenswert?

Müßte aber so funktionieren.

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

von Daniel J. (deejay)


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

von Stefan Kleinwort (Gast)


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

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von Stefan Kleinwort (Gast)


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

von Rolf Magnus (Gast)


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

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

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.