Pointer und C

Gast #283961
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ß
#283967
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
Gast #283969
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...
Moderator Persönliche Seite #283970
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.)
Moderator Persönliche Seite #283972
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.
Gast #283973
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?
Gast #283974
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
...
Persönliche Seite #283975
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
Moderator Persönliche Seite #283978
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))
#283980
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
Gast #283981
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
#283983
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.
Gast #283984
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
#283986
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.

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