Forum: Mikrocontroller und Digitale Elektronik C Anfängerfrage zu Pointern in Funktionen


von Marco D. (schoggo1992)


Lesenswert?

Servus ihr Lieben,

ich habe seit paar Tagen mit der C Programmierung begonnen und ich habe 
aktuell ein Problem welches ich noch nicht Lösen konnte. Ich stehe vor 
folgender Problemstellung:

Ich möchte einer Funktioen einen pointer übergeben und diesen einen 
schritt weiterlaufen lassen, hierzu der Quellcode:

void pointerVersuch(int *zahl){
  zahl++;
}

int main(){

  int array[3] = {0,1,2};
  int *zahl = array;

  while(1U){
  pointerVersuch(zahl);

  }

  return 0;
}

Der Befehl "zahl++;" lässt diesen auch weiterspringen, allderings wird 
der pointer wieder auf die alte Adresse gesetzt sobald die Funktion 
beendet ist (Im Debugger geschiet dies in Zeile der, also bei der 
geschlossenen Klammer des Funktionsrumpfes). Ich verstehe nicht ganz 
wieso. Ich sollte doch mit der Funktion pointer inkrementieren können, 
oder etwa nicht ? Wo ist mein Denkfehler ? V

Vielen Dank.

: Verschoben durch Moderator
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

zahl++ inkrementiert den Pointer selbst, d.h. er zeigt danach auf das 
nächste Element im Array. Die Elemente bleiben unverändert.
Wenn du (*zahl)++ schreibst, wird das Ziel des Pointers, d.h. das 0. 
Element im Array, verändert. Der Pointer zeigt danach immer noch auf 
dieses Element.

von Cyblord -. (cyblord)


Lesenswert?

Wenn du den Pointer selbst verändern willst (und nicht die Daten auf die 
er zeigt) musst du einen Pointer auf diesen Pointer übergeben.

void pointerVersuch(int **zahl){
}


pointerVersuch(&zahl);

: Bearbeitet durch User
von Marco D. (schoggo1992)


Lesenswert?

Danke für die sehr schnelle Antwort.

Ja ich weiß das der Wert sich so nicht ändert. Ich möchte nur, dass der 
ponter auf die nächste Adresse zeigt und diese Adresse beibehält wenn 
ich die Funktion verlasse. Ich verstehe nicht wieso der pointer nach 
verlassen der Funktion auf die Adresse vor "zahl++;" zurücksrpingt.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Marco D. schrieb:
> Ich verstehe nicht wieso der pointer nach verlassen der Funktion auf
> die Adresse vor "zahl++;" zurücksrpingt.

Weil der Funktion eine Kopie des Pointers übergeben wird und die 
Funktion nur die Kopie verändert.


Das ist bei allen Funktionsparametern so, auch bei int-Werten:
1
void funktion(int wert)
2
{
3
  wert++;
4
}
5
6
7
// ... irgendwo
8
9
int bla = 5;
10
11
printf("%d\n", bla);
12
13
funktion(bla);
14
15
printf("%d\n", bla);

Wird's klarer?

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Wenn du ein Objekt modifizieren willst, musst du einen Pointer oder eine 
Referenz auf das Objekt übergeben. Dabei spielt es keine Rolle, ob das 
Objekt selbst bereits ein Pointer ist oder nicht.

von Cyblord -. (cyblord)


Lesenswert?

Marco D. schrieb:
> Danke für die sehr schnelle Antwort.
>
> Ja ich weiß das der Wert sich so nicht ändert. Ich möchte nur, dass der
> ponter auf die nächste Adresse zeigt und diese Adresse beibehält wenn
> ich die Funktion verlasse. Ich verstehe nicht wieso der pointer nach
> verlassen der Funktion auf die Adresse vor "zahl++;" zurücksrpingt.

Weil der Pointer selbst "by value" übergeben wird. Der Pointer selbst 
wird kopiert. Die Funktion bekommt nur die Kopie. Die kann sie ändern so 
viel sie will. Das Original bleibt unverändert. Will man das Original 
verändern, so muss man einen Pointer darauf übergeben.
In deinem Fall also, eine Pointer auf den Pointer.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Marco D. schrieb:
> ch möchte nur, dass der
> ponter auf die nächste Adresse zeigt und diese Adresse beibehält wenn
> ich die Funktion verlasse.
Achso. Falsch gelesen.

Marco D. schrieb:
> Ich verstehe nicht wieso der pointer nach
> verlassen der Funktion auf die Adresse vor "zahl++;" zurücksrpingt.
Ein Pointer ist auch eine Art von Wert. Indem du den Pointer an die 
Funktion übergibst, wird dieser Pointer kopiert. In der Funktion 
inkrementierst du diese Kopie des Pointers. Nach Zurückkehren dieser 
Funktion verschwindet diese Kopie wieder. Da du ja aber nicht die Kopie 
ändern möchtest, sondern das Original, musst du wie cyblord gezeigt hat, 
einen Pointer auf den Pointer übergeben. Das Inkrementieren geht dann 
mit "(*zahl)++" - denn du willst ja das Ziel des Doppelpointers, nämlich 
den Original-Pointer, inkrementieren. Mit "(**zahl)++" könntest du auch 
das Array-Element inkrementieren.

von schoggomatic1992 (Gast)


Lesenswert?

Wow so schnell so viel Hilfe. Ja das macht absolut Sinn. Es wird bei 
Aufruf eine Kopie erstellt die nur während des Aufrufs besteht. OK 
vielen Dank !

von Dirk B. (dirkb2)


Lesenswert?

Du hast zwei Möglichkeiten, den Wert aus einer Funktion heraus zu 
bekommen.

Als Rückgabewert der Funktion
1
int * pointerVersuch(int *zahl){
2
  zahl++;
3
4
  return zahl;
5
}
6
7
int main(){
8
9
  int array[3] = {0,1,2};
10
  int *zahl = array;
11
12
  while(1U){
13
    zahl = pointerVersuch(zahl);
14
15
  }
16
17
  return 0;
18
}

 oder, wie schon gesagt, mit Übergabe der Adresse des Objektes.

von Marco D. (schoggo1992)


Lesenswert?

Ich hab es anscheinend immer noch nicht ganz verstanden. Hier mein 
Beispielcode um en nachzuvollziehen:

 void pointertraining1(int array[]){
  int *pointer = array;
  for(int i = 0;i<3;i++){
    *pointer = 1;
    pointer++;
  }
}

void pointertraining2(int array[]){
  for(int i = 0;i<3;i++){
    array[i] = 2;
  }
}

int main(){

  int zahlen[3] = {0,0,0};
  pointertraining1(zahlen);
  pointertraining2(zahlen);
}

Wenn ich euch richtig verstanden habe, kann ich die Daten (den array in 
dem Fall) nur ändern, indem ich diese mittels pointer manipuliere, weil 
der pointer nicht auf die Kopie, sondern auf das Original zugreift. Im 
zweiten Fall ändere ich nur die Werte der Arraykopie die ich aufrufe und 
sobald die Funktion schließt, sollte array die vorherigen Werte wieder 
annehmen. Mit beiden Funktionen kann ich aber in array schreiben und 
diese bleiben nach Beendigung der Funktionen bestehen. Was ist der 
Unterschied zu dem Beispiel von Rufus T.F. ?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Arrays werden an Funktionen genauso übergeben wie ein Pointer auf das 
erste Element des Arrays. Das bedeutet, daß das Array nicht kopiert 
wird.
1
void pointertraining2(int array[])

ist also exakt identisch zu
1
void pointertraining2(int *array)

von Einer K. (Gast)


Lesenswert?

Marco D. schrieb:
> Im zweiten Fall ändere ich nur die Werte der Arraykopie

Da gibt es nirgendwo eine Kopie des Array.
Um Arrays zu kopieren verwendet man memcpy() oder ähnlich.



In C laufen Arrayzugriffe immer über Pointer, wenn auch heimlich intern.

Ob du dieses schreibst:
> array[i] = 2;

oder:
> i[array] = 2;

oder:
> *(array+i) = 2;

Das ist völlig egal.
Es wird immer der gleiche Code generiert.
Es zeigt Äquivalenz von Zeiger und Array Zugriffen

von Klugscheisservernichter (Gast)


Lesenswert?

Arduino Fanboy D. schrieb:
> In C laufen Arrayzugriffe immer über Pointer, wenn auch heimlich intern.

Arduino Fanboy D. schrieb:
> Das ist völlig egal.
> Es wird immer der gleiche Code generiert.

Das ist totaler Blödsinn! Optimierter Code kann gleich aussehen, aber 
pointer und array sind grundverschiedene Dinge.

Ein pointer hat eine eigene Speicherstelle, in der die Adresse der Daten 
steckt, auf die der pointer zeigt. Ein array hat diese Speicherstelle 
nicht!

@Fanboy: Bevor du flamess, lies hier: 
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/012_c_zeiger_007.htm

von Bernd K. (prof7bit)


Lesenswert?

Marco D. schrieb:
> int *zahl

Es wird anschaulicher und leichter zu lesen wenn Du den * an den 
Typbezeichner hängst und nicht an den Variablembezeichner:

int* zahl

Man kann nämlich argumentieren daß die Eigenschaft ein Pointer zu sein 
Teil des Typs ist und daher zu diesem gehört. Du hast eine Variable 
"zahl" und diese ist vom Typ "Zeiger auf int". Das Sternchen ist also 
ganz klar ein Bestandteil des Typs, es ist besser das auch durch die 
Schreibweise kenntlich zu machen, Typ und Variablenname durch ein 
Leerzeichen zu trennen und das Pointersternchen direkt an den Typnamen 
anzukleben wo es hingehört.

das solltest Du Dir von Anfang an angewöhnen. Dem Compiler ist es egal, 
er sieht diese Leerzeichen überhaupt nicht aber die Lesbarkeit und 
Verständlichkeit steigert es enorm.

Also

int* zahl  // "zahl" ist ein Pointer auf int

int *zahl  // wenn man den pointer "zahl" dereferenzieren
           // würde dann wäre das ein int

also beides mal das exakt selbe in jeder Hinsicht, nur in der zweiten 
Form hoffnungslos verklausuliert gedacht und hingeschrieben.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Klugscheisservernichter schrieb:
> @Fanboy: Bevor du flamess, lies hier:
> http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/012_c_zeiger_007.htm
Gerade das Buch, ist nun wirklich keine gute Quelle....
In Sachen Zeiger, hat der Autor, bzw. sein Buch, einige Schwächen.

von Christopher J. (christopher_j23)


Lesenswert?

Klugscheisservernichter schrieb:
> Das ist totaler Blödsinn! Optimierter Code kann gleich aussehen, aber
> pointer und array sind grundverschiedene Dinge.

Oh je, da hat aber jemand C so überhaupt nicht verstanden.

Arduino Fanboy D. schrieb:
> Klugscheisservernichter schrieb:
>> @Fanboy: Bevor du flamess, lies hier:
>> http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/012_c_zeiger_007.htm
> Gerade das Buch, ist nun wirklich keine gute Quelle....
> In Sachen Zeiger, hat der Autor, bzw. sein Buch, einige Schwächen.

Sehe ich ganz genauso.

Wie wäre es stattdessen mit "6.5.2.1 Array Subscripting" aus dem C99 
Standard:

> A postfix expression followed by an expression in square brackets [] is a
> subscripted designation of an element of an array object. The definition of
> the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))).
> Because of the conversion rules that apply to the binary + operator, if E1
> is an array object (equivalently, a pointer to the initial element of an array
> object) and E2 is an integer, E1[E2] designates the E2-th element of E1
> (counting from zero).

: Bearbeitet durch User
von Klugscheisservernichter (Gast)


Lesenswert?

Bernd K. schrieb:
> int* zahl  // "zahl" ist ein Pointer auf int

Jetzt deklariere einmal zwei Variablen per Komma.
int a, b;

Und jetzt du mit Pointer.

von Klugscheisservernichter (Gast)


Lesenswert?

> A postfix expression......

Zeichne dir eine momory map und du wirst den Unterschied zwischen array 
und Pointer verstehen.

von Christopher J. (christopher_j23)


Lesenswert?

Klugscheisservernichter schrieb:
> Zeichne dir eine momory map und du wirst den Unterschied zwischen array
> und Pointer verstehen.

Ich brauch mir überhaupt nichts zeichnen, weil ich im Gegensatz zu dir 
verstanden habe, dass es keinen Unterschied gibt. Hier hast du ein 
Programm zum rumspielen. Sogar mit durch Komma getrennten Variablen (was 
ich bei Pointern allgemein für keine gute Idee halte, weil zu 
fehleranfällig).
1
#include <stdio.h>
2
#include <stdint.h>
3
4
int main(int argc, char const *argv[])
5
{
6
  uint32_t array[3] = {123, 456, 789};
7
8
9
  uint32_t* a0;
10
  uint32_t* a1;
11
  uint32_t* a2;
12
13
  uint32_t* p0,* p1;
14
//  uint32_t* p1;
15
  uint32_t* p2;
16
17
  a0 = &array[0];
18
  a1 = &array[1];
19
  a2 = &array[2];
20
21
  p0 = array+0;
22
  p1 = array+1;
23
  p2 = array+2;
24
25
  printf("a[0] hat die Adresse %x, a[1] hat die Adresse %x und a[2] hat die Adresse %x\n", a0, a1, a2);
26
  printf("p0 zeigt auf Adresse %x, p1 zeigt auf Adresse %x und p2 zeigt auf Adresse %x\n", p0, p1, p2);
27
28
  return 0;
29
}

Ausgabe:
1
$ ./bin/helloworld                   
2
a[0] hat die Adresse f6e1fc4, a[1] hat die Adresse f6e1fc8 und a[2] hat die Adresse f6e1fcc
3
p0 zeigt auf Adresse f6e1fc4, p1 zeigt auf Adresse f6e1fc8 und p2 zeigt auf Adresse f6e1fcc

von Klugscheisservernichter (Gast)


Lesenswert?

Christopher J. schrieb:
> Ausgabe:$ ./bin/helloworld
> a[0] hat die Adresse f6e1fc4, a[1] hat die Adresse f6e1fc8 und a[2] hat
> die Adresse f6e1fcc
> p0 zeigt auf Adresse f6e1fc4, p1 zeigt auf Adresse f6e1fc8 und p2 zeigt
> auf Adresse f6e1fcc

Dann Spiel einmal mit deinem Programm und gib die Adresse von p0 aus 
(nicht worauf gezeigt wird). Dann verstehst auch du es.

von Christopher J. (christopher_j23)


Lesenswert?

Klugscheisservernichter schrieb:
> Dann Spiel einmal mit deinem Programm und gib die Adresse von p0 aus
> (nicht worauf gezeigt wird). Dann verstehst auch du es.

Du willst es offensichtlich nicht verstehen, deshalb geb ich hier an der 
Stelle auf aber vielleicht hat es ja wenigstens dem TO etwas zum 
Verständnis geholfen.

von Klugscheisservernichter (Gast)


Lesenswert?

Also überzeugt? Gut so.

von Christopher J. (christopher_j23)


Lesenswert?

Das Niveau hier war auch schonmal höher...

von Bernd K. (prof7bit)


Lesenswert?

Klugscheisservernichter schrieb:
> Jetzt deklariere einmal zwei Variablen per Komma.
> int a, b;
>
> Und jetzt du mit Pointer.

Da verzichte ich lieber auf das Komma und schreib das untereinander. 
Oder wenn mehrere Variablen absichtlich ein einen bestimmten Typ haben 
sollen und dieser Typ irgendeine bestimmte Bedeutung hat der über den 
lokalen Kontext hinausgeht mach ich auch mal gerne ein typedef.

Ich will keinen Preis dafür gewinnen möglichst viel auf eine Zeile 
gequetscht zu haben sondern dafür daß es möglichst übersichtlich 
aussieht, den Preis einer zusätzlichen Zeile bezahle ich in diesem Falle 
gerne.


Ich hab damals noch auf Pascal gelernt und um dann den Umstellungsschock 
auf C so weit wie möglich abzufedern hab ich einige Gepflogenheiten 
mitgenommen, unter anderem die daß man allein mit den Typdeklarationen 
schon einiges über seine Abstraktion des Problems ausdrücken und 
dokumentieren kann und auch wenn vieles davon dem C-Compiler leider 
vollkommen egal ist dient es doch dem Menschen als Anhaltspunkt fürs 
Verständnis.


In Pascal hätt ich übrigens schreiben können:

var
  a, b: ^integer;

Denn da ist es wirklich Teil der Typsyntax und nicht nur eine halbgare 
Zweitverwendung des Dereferenzierungsoperators, in C ist es eher 
schieres Glück daß man es wenigstens in den meisten Fällen ebenfalls so 
hinschreiben kann als wäre es das wonach es schon aus Symmetriegründen 
eigentlich aussehen müsste, also mach ich es auch so.

von (prx) A. K. (prx)


Lesenswert?

Klugscheisservernichter schrieb:
> Das ist totaler Blödsinn! Optimierter Code kann gleich aussehen, aber
> pointer und array sind grundverschiedene Dinge.

Für Arrays und Pointer gelten völlig anders arbeitenden 
Speicherverwaltungen. In der Verwendung in Ausdrücken ist ein Array 
jedoch meist ein Pointer auf das erste Element in Form einer 
Adress-Konstante. Wobei eine Konstante weder änderbar ist, noch selbst 
wiederum eine Adresse besitzt.

Der Ausdruck a[i] ist in C als *(a+i) definiert, egal ob a als Array 
oder als Pointer definiert wurde. In Ausdrücken Unterschiedlich 
behandelt werden Arrays und Pointer m.W. nur in sizeof.

Weshalb C in Parameter-Deklarationen sowas wie f(int a[]) überhaupt 
zugelassen hat, habe ich nie verstanden.

: Bearbeitet durch User
von Peter S. (Gast)


Lesenswert?

Bernd K. schrieb:
> Es wird anschaulicher und leichter zu lesen wenn Du den * an den
> Typbezeichner hängst und nicht an den Variablembezeichner:
>
> int* zahl
>
> Man kann nämlich argumentieren daß die Eigenschaft ein Pointer zu sein
> Teil des Typs ist und daher zu diesem gehört.

So könnte man vielleicht argumentieren, wenn man gleichzeitig das 
Deklarieren von mehreren Variablen per Komma verbietet (was auch eine 
gute Idee wäre). Sonst könnte man leicht auf die Idee kommen, dass hier 
zwei Pointer deklariert werden:
1
uint32_t* a, b;
Hier ist aber nur a ein "pointer to unsigned integer". Die Variable b 
ist vom Typ "unsigned integer", weil das Sternchen eben nicht 
Bestandteil des Typs ist.

von (prx) A. K. (prx)


Lesenswert?

Die Syntax von Deklaration in C ist ein typischer Fall von gut gemeint, 
aber schlecht gemacht. War eine nette Idee, sie wie die Verwendung 
aussehen zu lassen, ist aber zu einer unrettbaren Katastrophe geworden. 
Das muss man nicht mögen, man muss nur damit leben lernen.

von Johannes S. (Gast)


Lesenswert?

schade das der gcc da nicht warnt, der ist ja schon sehr pedantisch was 
mögliche Fehler angeht. Aber die Verwendung des Wertes antelle des 
Pointers wird in den meisten Fällen zum Fehler führen sodass man eine 
falsche Deklaration schnell findet.

von Bernd K. (prof7bit)


Lesenswert?

Peter S. schrieb:
> uint32_t* a, b;
> Hier ist aber nur a ein "pointer to unsigned integer"

Wenn man Glück hat haut es einem Der Compiler kurz danach um die Ohren 
sobald man versucht den vermeintlichen Pointer auch als solchen zu 
benutzen. Aber ja, das ist nur wieder eine weitere häßliche 
Unvollkommenheit dieser Sprache die sich einreiht in eine lange Liste 
weiterer Unvollkommenheiten.

Dennoch kann es gelingen auch in C sauberen Code zu schreiben wenn man 
etwas Disziplin an den Tag legt, von manchen Konstrukten einen selbst 
auferlegten Abstand nimmt. Keiner soll behaupten C sei eine einfache 
Sprache, man kann sich bei undurchsichtigem Bodennebel schnell in den 
eigenen Fuß schießen wenn man sich keine Konventionen angewöhnt wie zum 
Beispiel Fuß immer zurückziehen bevor man abdrückt, niemals auf zwei 
Füße gleichzeitig schießen, im Zweifel einen halben Meter zurück gehen.

: Bearbeitet durch User
von Martin H. (horo)


Lesenswert?

Infrequently Asked Questions in comp.lang.c
2.8: Practically speaking, what is the difference between arrays and 
pointers?
About the difference between alcohol and marijuana; they have different 
characteristics, and that's not a problem if you don't mix them too 
carelessly.

Ciao, Martin

Beitrag #5565357 wurde von einem Moderator gelöscht.
Beitrag #5565360 wurde von einem Moderator gelöscht.
Beitrag #5565362 wurde von einem Moderator gelöscht.
Beitrag #5565368 wurde von einem Moderator gelöscht.
von (prx) A. K. (prx)


Lesenswert?

Wobei a in f(int a[10]) zwar wie ein Array aussieht, aber in keiner 
Weise eines ist. Den Unterschied sieht man bei sizeof. Bei echten Arrays 
gibts die Grösse vom gesamten Array, bei einem Pointer die vom Pointer.

Diese Form der Parameterdeklaration ist schlicht grober Unfug.

: Bearbeitet durch User
Beitrag #5565412 wurde von einem Moderator gelöscht.
Beitrag #5565593 wurde von einem Moderator gelöscht.
Beitrag #5565669 wurde von einem Moderator gelöscht.
von A. S. (Gast)


Lesenswert?

Bernd K. schrieb:
> Aber ja, das ist nur wieder eine weitere häßliche Unvollkommenheit
> dieser Sprache die sich einreiht in eine lange Liste weiterer
> Unvollkommenheiten.

Bernd, Du empfiehlst oben eine unkonventionelle Schreibweise, weil sie 
besser sei, und lässt dich dann darüber aus, wie schrecklich C ist, wenn 
man es denn so macht.

Und dann empfiehlst Du auch noch Verschleierung mit typedef. Ja, kann 
man alles tun. Trägt hier allerdings nur zur Verwirrung bei. Es hat 
Gründe, warum Profis z.b. typedef nur für Basistypen und funktionsptr 
verwenden und nicht um Pointer nicht mehr von Strukturen unterscheiden 
zu können.

von Bernd K. (prof7bit)


Lesenswert?

Achim S. schrieb:
> Und dann empfiehlst Du auch noch Verschleierung mit typedef.

Typedef soll auch bei mir nicht als Verschleierung dienen sondern das 
Verständnis erhöhen. Für sowas triviales wie oben hätt ich allerdings 
nicht extra noch ein typedef eingeführt, es sei denn ich brauche den 
selben Pointertypen noch an zig anderen Stellen und er zeigt auf etwas 
das besonders genug ist einen eigenen Namen zu verdienen oder ein 
treffenderer Name als "Zeiger auf irgendein int" würde die 
Verständlichkeit verbessern.

> und nicht um Pointer nicht mehr von Strukturen unterscheiden
zu können.

Das wird nicht der Fall sein wenn man die Namen seiner Typen nicht 
gerade ziellos auswürfelt. Und Zeigertypen als solche - ganz egal auf 
was sie zeigen - sind absolut nichts ungewöhnliches.

> Profis

Und ich kenne übrigens "Profis" deren Code ist so unterirdisch daß es 
die Sau graust und ich kenne "Amateure" deren Code ist so vorbildlich 
daß er in Lehrbüchern abgedruckt werden könnte, das ist also schonmal 
überhaupt kein Kriterium, also bitte keine solchen leeren Worthülsen 
wenn es um die eigentliche Sache geht.

Ich bin auch "Profi" (was auch immer das über die bloße Tatsache des 
Geldverdienens hinaus sonst noch zu bedeuten haben mag) und ich versuche 
meinen Code in erster Linie auf Lesbarkeit, Verständlichkeit und 
Wartbarkeit zu optimieren. Ich habe einen strengen Satz an äußerst 
strikt einzuhaltenden Konventionen an die ich mich genauso halte wie ich 
es von allen erwarte die diesen Code ebenfalls anfassen, in sehr weiten 
Teilen (99%) decken die sich mit den gängigen Styleguides in einigen 
wenigen Punkten erlaube ich mir gezielte Abweichungen von der "Norm" die 
ich alle einzeln genauestens begründen kann. Das mit der 
Pointerschreibweise ist so eine.

von Einer K. (Gast)


Lesenswert?

Bernd K. schrieb:
> in einigen
> wenigen Punkten erlaube ich mir gezielte Abweichungen von der "Norm" die
> ich alle einzeln genauestens begründen kann. Das mit der
> Pointerschreibweise ist so eine.
Da empfinde ich deine Sichtweise als etwas absurd.


int a;
int *b;

int a,*b;

Die Bezeichner a und b beziehen sich beide auf den Datentype int.
Das Value, welches verarbeitet werden will ist ein int.

Die Verwendung des Bezeichners a ermöglicht den direkten Zugriff auf das 
int Value.

Der Bezeichner b trägt das zusätzliche Attribut "Zeiger".
Für den Zugriff, auf das int Value, ist eine Dereferenzierung nötig.

---
Für mich gehört der Stern ganz eindeutig zum Bezeichner, nicht zum 
Datentype. Der Datentype des Value, mit dem ich dealen möchte, ist in 
beiden Fällen ein int.


----------------

Klugscheisservernichter schrieb im Beitrag #5565669:
> Aber auch für dich: Das Speicherabbild von pointer und array
> ist nicht gleich.
Hat das irgendwer behauptet?

Wenn dir nicht klar ist, welchen Fußabdruck ein Zeiger, oder ein Array, 
im Speicher hat, solltest du dir mal ein gutes C/C++ Buch anschaffen. Da 
sollte das erwähnt werden.
(etwas Nachdenken könnte auch schon reichen)

Tipp:
Das Wort "Äquivalenz" wird dir u.A. bei Wikipedia erklärt.

von (prx) A. K. (prx)


Lesenswert?

Arduino Fanboy D. schrieb:
> Für mich gehört der Stern ganz eindeutig zum Bezeichner, nicht zum
> Datentype.

So ist C ja auch definiert. Ob man das für klug hält oder nicht. Andere 
Sprachen, andere Deklarationstechnik.

In C++ gibts allerdings eine Tradition, sowas als
   int& b;
zu schreiben, obwohl das "&" genau wie "*" syntaktisch stärker an "b" 
als an "int" bindet.

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

A. K. schrieb:
> int& b;

Du meinst Referenzen?

In meiner kleinen Arduino C++ Welt kann ich weitestgehend auf Zeiger 
verzichten und stattdessen die Vorteile von Referenzen genießen.

Profanes/Naives Beispiel:
1
int array[] {4,5,9};
2
3
int &a = array[0];
4
int &b = array[1];
5
int &c = array[2];

Einmal anfassbar, als Array, oder einzeln als benannte Variable.
Die Verwendung in C würde Zeiger, und damit Speicher, erfordern.
Oder eben #define, was es aber auch nicht schöner/flexibler macht.

In C++ gibts das kostenlos

von batman (Gast)


Lesenswert?

Es ist eben C und nicht was anderes. Die übliche, pragmatische 
Konvention ist die Deklarationszeile
1
Objekt_type  Objekt, *Objekt_pointer;

Beitrag #5566400 wurde von einem Moderator gelöscht.
Beitrag #5566414 wurde von einem Moderator gelöscht.
von Marco D. (schoggo1992)


Lesenswert?

Bei mir angekommen ist, dass Funktionen nur Abbilder der Parameter 
erzeugen und ich deshalb mit Pointern arbeiten muss um etwas an meinen 
Variablen/Pointern zu ändern, welche ich als Parameter übergeben habe, 
wenn diese Veränderungen nach Funktionsabruf bestehen bleiben sollen. 
Ich habe allerdings noch Verständnisprobleme mit der Speicherverwaltung 
mittels malloc(). Ich habe eine Funktion geschrieben die mir ein array 
aus Sinuswerten erzeugt und einen pointer auf das erste Element 
zurückgibt:

double *sinarrayGenerieren(){
  double array[11];
  for(int i = 0; i<11;i++){
    array[i] = sin(2.0*PI/11*(i+1));
  }
  return array;
}

Da erhielt ich aber die Warnung, dass ich eine lokale Variable 
zurückgebe. Die Warnung macht Sinn, weil meine Variable nach Aufruf der 
Funktion gelöscht wird und ich so auf einen Speicherort zeige, der 
jederzeit umgeschrieben werden kann weil nicht mehr zugewiesen, 
zumindest nach meinem jetzigen Verständnis.

Daher habe ich es umgeschrieben und pointer verwendet:

double *sinarrayGenerieren(){
  double *array = malloc(sizeof(double)*11);
  for(int i = 0; i<11;i++){
    array[i] = sin(2.0*PI/11*(i+1));
  }
  return array;
}

Weiter unten im main Programm habe ich den speicher wieder freigegeben, 
aber erst als ich die Funktion aufgerufen hatte:

double *sinustabelle = sinarrayGenerieren();

.
.
.

free(sinustabelle);

Ich habe nun zwei Fragen:

1. Zu der ersten Variante: Ich übergebe bei Funktionsaufruf ja einen 
pointer, in dem Fall *sinustabelle. Der pointer *array existiert ja nach 
Funktionsaufruf nicht mehr, der wurde doch aber durch return an 
*sinustabelle weitergegeben oder nicht ?

2. Zu der zweiten Variante: Erzeuge ich bei meiner Variante Memory Leaks 
? ich reserviere in der Funktion speicher für einen pointer. Dann 
übergebe ich den pointer mit return und gebe den speicher für 
sinustabelle wieder frei. Aber der speicher für array wurde ja nicht 
freigegeben. Oder kann ich davon ausgehen, da sinustabelle auf den 
speicherort von array zeigt, dass dadurch der speicher von array 
freigegeben wird ? Ist ja die gleiche Adresse.

von (prx) A. K. (prx)


Lesenswert?

Marco D. schrieb:
> double *sinarrayGenerieren()

Eine leere Parameterliste ist nicht das, wonach es aussieht, sondern 
steht für eine unbekannte Parameterliste.

Alternative ohne dynamische Speicherverwaltung:
1
void sinarrayGenerieren(double *array){
2
  for(int i = 0; i<11;i++){
3
    array[i] = sin(2.0*PI/11*(i+1));
4
  }
5
}
und dann
1
  double array[11];
2
  sinarrayGenerieren(array);

von Dirk K. (merciless)


Lesenswert?

1. ja
2. nein

ABER: Schlechter Stil: Besser erst
Speicher reservieren, dann Funktion
aufrufen und Pointer übergeben:
1
double *p = malloc(...);
2
sinarrayGenerieren(p);
3
...
4
free(p);

merciless

von Einer K. (Gast)


Lesenswert?

Marco D. schrieb:
> Da erhielt ich aber die Warnung, dass ich eine lokale Variable
> zurückgebe.
Mit Fug und Recht.

Marco D. schrieb:
> 1. Zu der ersten Variante:
die ist kaputt.
Das sagt ja auch schon die Warnung.

Marco D. schrieb:
> Oder kann ich davon ausgehen, da sinustabelle auf den
> speicherort von array zeigt, dass dadurch der speicher von array
> freigegeben wird ? Ist ja die gleiche Adresse.
Solange du den Zeiger nicht manipulierst, ist alles gut.

von Christopher J. (christopher_j23)


Lesenswert?

Marco D. schrieb:
> 1. Zu der ersten Variante: Ich übergebe bei Funktionsaufruf ja einen
> pointer, in dem Fall *sinustabelle. Der pointer *array existiert ja nach
> Funktionsaufruf nicht mehr, der wurde doch aber durch return an
> *sinustabelle weitergegeben oder nicht ?

Korrekt. Um genau zu sein übergibst du bei Funktionsaufruf gar nichts 
aber die Funktion übergibt einen Pointer wenn sie zurückkehrt. Es ist 
übrigens nie verkehrt zu checken ob malloc auch eine Speicheradresse 
zurückgegeben hat oder eben nicht, weil z.B. keiner mehr verfügbar war.

Marco D. schrieb:
> 2. Zu der zweiten Variante: Erzeuge ich bei meiner Variante Memory Leaks
> ? ich reserviere in der Funktion speicher für einen pointer. Dann
> übergebe ich den pointer mit return und gebe den speicher für
> sinustabelle wieder frei. Aber der speicher für array wurde ja nicht
> freigegeben. Oder kann ich davon ausgehen, da sinustabelle auf den
> speicherort von array zeigt, dass dadurch der speicher von array
> freigegeben wird ? Ist ja die gleiche Adresse.

Ja, du kannst davon ausgehen, dass der Speicher freigegeben wird. Für 
free() zählt nur die Adresse auf die der Pointer zeigt, nicht der 
Pointer selber.

von (prx) A. K. (prx)


Lesenswert?

Dirk K. schrieb:

> ABER: Schlechter Stil: Besser erst
> Speicher reservieren, dann Funktion
> aufrufen und Pointer übergeben:

Inwiefern ist das besser, wenn in der Dokumentation der Funktion der 
dynamische Charakter des Rückgabewerts deutlich aufgeführt wird?

Schlechter Stil ist allerdings die Verwendung von malloc ohne Kontrolle 
auf NULL. ;-)

von Christopher J. (christopher_j23)


Lesenswert?

Nochwas:

Marco D. schrieb:
> 1. Zu der ersten Variante: Ich übergebe bei Funktionsaufruf ja einen
> pointer, in dem Fall *sinustabelle. Der pointer *array existiert ja nach
> Funktionsaufruf nicht mehr, der wurde doch aber durch return an
> *sinustabelle weitergegeben oder nicht ?

Wenn du ein Array innerhalb einer Funktion deklarierst, so wird dieses 
Array auf dem Stack landen. Wenn die Funktion zurückkehrt, wird dieser 
Speicherbereich jedoch wieder freigegeben. Dein Pointer *array existiert 
zwar als *sinustabelle weiter aber der Inhalt des Arrays (auf dem Stack) 
kann beliebig überschrieben werden. Deshalb die Warnung und deshalb 
besser anders machen, z.B. wie A.K. vorgeschlagen hat.

von Rolf M. (rmagnus)


Lesenswert?

Bernd K. schrieb:
> Typedef soll auch bei mir nicht als Verschleierung dienen sondern das
> Verständnis erhöhen. Für sowas triviales wie oben hätt ich allerdings
> nicht extra noch ein typedef eingeführt, es sei denn ich brauche den
> selben Pointertypen noch an zig anderen Stellen und er zeigt auf etwas
> das besonders genug ist einen eigenen Namen zu verdienen oder ein
> treffenderer Name als "Zeiger auf irgendein int" würde die
> Verständlichkeit verbessern.

Dann würde ich aber einen typedef für "irgendein int" machen und nicht 
für einen Zeiger darauf. Ich halte es nicht für sinnvoll, Zeiger hinter 
Typedefs zu verstecken, genauso wenig übrigens wie Arrays.

A. K. schrieb:
> In C++ gibts allerdings eine Tradition, sowas als
>    int& b;
> zu schreiben, obwohl das "&" genau wie "*" syntaktisch stärker an "b"
> als an "int" bindet.

Für mich bindet zumindest das * ganz selbstverständlich an das "int". In 
C++ ist das übrigens auch üblich, das * an den Typ zu machen (und wird 
auch vom Erfinder der Sprache selbst so verwendet). Beim & kann ich die 
Argumentation eher nachvollziehen, da Referenzen in C++ im Gegensatz zu 
Zeigern keine eigenen Typen sind.

Arduino Fanboy D. schrieb:
> In meiner kleinen Arduino C++ Welt kann ich weitestgehend auf Zeiger
> verzichten und stattdessen die Vorteile von Referenzen genießen.

> Einmal anfassbar, als Array, oder einzeln als benannte Variable.
> Die Verwendung in C würde Zeiger, und damit Speicher, erfordern.
> Oder eben #define, was es aber auch nicht schöner/flexibler macht.
>
> In C++ gibts das kostenlos

Das bekommst du aber (in C und C++) mit Zeigern genauso gut hin. 
Tatsächlich werden Referenzen in g++ hinter den Kulissen auf die gleiche 
Weise implementiert wie Zeiger - indem sie die Adresse des Zielobjekts 
enthalten. Die Optimierung schmeißt in deinem Programm dann sowohl für 
Zeiger als auch für Referenzen die zusätzliche Speicherung der Adresse 
raus.

Arduino Fanboy D. schrieb:
> Da empfinde ich deine Sichtweise als etwas absurd.

Und ich deine - auch wenn es die gleiche sein mag, die K&R damals 
hatten, als sie das verbrochen haben.

> int a;
> int *b;
>
> int a,*b;
>
> Die Bezeichner a und b beziehen sich beide auf den Datentype int.

Die Zeilen definieren jeweils eine Variable.
Die eine definiert eine Variable vom Typ "int" mit Namen a, die andere 
eine vom Typ "Zeiger auf int" mit Name b.
Meine Sichtweise sagt genau ds:

int* : Zeiger auf int …
b    : … mit Namen "b"

Deine klingt für mich so:

int  : Typ "int"…
*b   : … mit Namen "b", aber es soll dann doch kein int sein, sondern 
das was, wenn man es dereferenzieren würde, zu einem int führen würde.

> Die Verwendung des Bezeichners a ermöglicht den direkten Zugriff auf das
> int Value.
>
> Der Bezeichner b trägt das zusätzliche Attribut "Zeiger".

Dieses "Attribut" ist aber ein elementarer Bestandteil des Typs von b.

> Klugscheisservernichter schrieb:
>> Aber auch für dich: Das Speicherabbild von pointer und array
>> ist nicht gleich.
> Hat das irgendwer behauptet?
>
> Wenn dir nicht klar ist, welchen Fußabdruck ein Zeiger, oder ein Array,
> im Speicher hat, solltest du dir mal ein gutes C/C++ Buch anschaffen. Da
> sollte das erwähnt werden.
> (etwas Nachdenken könnte auch schon reichen)
>
> Tipp:
> Das Wort "Äquivalenz" wird dir u.A. bei Wikipedia erklärt.

Irgendwie diskutiert ihr aneinander vorbei. Einer sagt: "Ein Auto hat 
vier Räder" und der andere sagt: "Nein, es hat einen Motor".

A. K. schrieb:
> Dirk K. schrieb:
>
>> ABER: Schlechter Stil: Besser erst
>> Speicher reservieren, dann Funktion
>> aufrufen und Pointer übergeben:
>
> Inwiefern ist das besser, wenn in der Dokumentation der Funktion der
> dynamische Charakter des Rückgabewerts deutlich aufgeführt wird?

Wenn der Aufrufer den Speicher bereitstellt, hat das erstens den 
Vorteil, dass der sich selber raussuchen kann, wo er diesen Speicher 
hält, und zweitens ist er dann sowohl für die Allokation, als auch für 
die Freigabe verantwortlich. Da muss der Nutzer nicht mal die Doku 
lesen, um zu wissen, dass er den Speicher wieder freigeben muss, wenn er 
ihn selber allokiert.

> Schlechter Stil ist allerdings die Verwendung von malloc ohne Kontrolle
> auf NULL. ;-)

Diese Kontrolle bringt auf dem PC in der Praxis nicht viel. Erstens 
versinkt der eh in der Auslagerungshölle, schon lange bevor malloc NULL 
zurückgibt, zweitens ist die Allokation auf Systemen mit Memory 
Overcommit sowieso auch dann erfolgreich, wenn eigentlich kein Speicher 
mehr verfügbar ist.

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Für mich bindet zumindest das * ganz selbstverständlich an das "int".

Für den Parser des Compilers bindet es stärker an den Namen als an den 
Grundtyp. Andernfalls wäre q in int*p,q auch ein Pointer.

Sinngemäss (keine korrekte Syntax):
  int *p,q
ist
  int (*p),q
und nicht
  (int *) p,q

> da Referenzen in C++ im Gegensatz zu Zeigern keine eigenen Typen sind.

Was meinst du damit?

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
>> Schlechter Stil ist allerdings die Verwendung von malloc ohne Kontrolle
>> auf NULL. ;-)
>
> Diese Kontrolle bringt auf dem PC in der Praxis nicht viel.

Wir sind im Forenzweig für Mikrocontroller, nicht dem für PCs. ;-)

von Rolf M. (rmagnus)


Lesenswert?

A. K. schrieb:
> Rolf M. schrieb:
>> Für mich bindet zumindest das * ganz selbstverständlich an das "int".
>
> Für den Parser des Compilers bindet es stärker an den Namen als an den
> Grundtyp. Andernfalls wäre q in int*p,q auch ein Pointer.

Ja, in der Hinsicht hast du natürlich recht. Ich hatte "binden" nicht im 
Sinne der Parsers verstanden, sondern in dem Sinne, zu was das * eher 
gehört.

>> da Referenzen in C++ im Gegensatz zu Zeigern keine eigenen Typen sind.
>
> Was meinst du damit?

War vielleicht etwas falsch ausgedrückt, bzw. ich hab etwas 
durcheinander gebracht.
Natürlich gibt es Referenz-Typen, aber Referenzen sind selbst keine 
Objekte.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Ja, in der Hinsicht hast du natürlich recht. Ich hatte "binden" nicht im
> Sinne der Parsers verstanden, sondern in dem Sinne, zu was das * eher
> gehört.

Vielleicht hatte ich zu viel mit dem Inneren von Compilern zu tun, um 
noch derart menschlich denken zu können. ;-)

> Natürlich gibt es Referenz-Typen, aber Referenzen sind selbst keine
> Objekte.

Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.

: Bearbeitet durch User
Beitrag #5566809 wurde von einem Moderator gelöscht.
von Rolf M. (rmagnus)


Lesenswert?

A. K. schrieb:
>> Natürlich gibt es Referenz-Typen, aber Referenzen sind selbst keine
>> Objekte.
>
> Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.

Sie haben keine Größe im Speicher (sizeof liefert die Größe des 
referenzierten Objekts, man kann sie nicht dynamisch allokieren, es gibt 
keine Arrays aus Referenzen), aber ihre Umsetzung kann natürlich 
Speicher benötigen. Das ist in der Hinsicht ähnlich wie bei Funktionen.

Beitrag #5566900 wurde von einem Moderator gelöscht.
von A. S. (Gast)


Lesenswert?

A. K. schrieb:
> Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.

Das Objekt, ja. Aber nicht dessen Referenz.


Im C gibt es übrigens einen workaround.... Ein [1] dahinter, dann ist 
das Objekt bei Funktionsübergabe nur sein Zeiger, ...

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
>> Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.
>
> Sie haben keine Größe im Speicher

Wenn etwas keine Grösse hat, aber Platz braucht, in welcher 
philosophischen Sphäre bewegen wir uns dann? Diese Struct ist jedenfalls 
gemäss sizeof 8 Bytes gross (x64):
1
struct S {
2
        int& y;
3
};

Zur Sprache: Welcher Begriff passt zum Member y selbst, dem Ding in der 
Sprache mit dem Namen y. Was ist das? Wird sonst schwierig mit der 
Diskussion. Denn y braucht Platz, wie immer es heisst.

Für mich gibts jedenfalls einen Unterschied zwischen dem Member y und 
der Verwendung des Members in Ausdrücken.

: Bearbeitet durch User
von mh (Gast)


Lesenswert?

Achim S. schrieb:
> A. K. schrieb:
>> Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.
>
> Das Objekt, ja. Aber nicht dessen Referenz.

Der c++17 Standard (N4687) sagt dazu:
1
4.4 The C ++ memory model [intro.memory]
2
...
3
3 A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having
4
nonzero width. [ Note: Various features of the language, such as references and virtual functions, might involve
5
additional memory locations that are not accessible to programs but are managed by the implementation.
6
— end note ] Two or more threads of execution (4.7) can access separate memory locations without interfering
7
with each other.
8
...
und:
1
11.3.2 References [dcl.ref]
2
...
3
4 It is unspecified whether or not a reference requires storage (6.7).
4
...

A. K. schrieb:
> Rolf M. schrieb:
>>> Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.
>>
>> Sie haben keine Größe im Speicher
>
> Diese Struct ist 8 Bytes gross (x64):struct S {
>         int& y;
> };
>
> Zur Sprache: Welcher Begriff passt zum Member y selbst, dem Ding in der
> Sprache mit dem Namen y. Was ist das? Wird sonst schwierig mit der
> Diskussion. Denn y braucht Platz, wie immer es heisst.

y ist eine reference, bzw genauer eine lvalue reference.

von (prx) A. K. (prx)


Lesenswert?

mh schrieb:
> 4 It is unspecified whether or not a reference requires storage (6.7).

Der Satz gefällt mir wesentlich besser als obiges "... haben keine 
Grösse im Speicher".

von mh (Gast)


Lesenswert?

Nachtrag zu deinem Edit

A. K. schrieb:
> Für mich gibts jedenfalls einen Unterschied zwischen dem Member y und
> der Verwendung des Members in Ausdrücken.

Ja, erst in einem Ausdruck steht die Referenz für das Objekt selbst.
1
8 Expressions [expr]
2
...
3
5
4
If an expression initially has the type “reference to T” (11.3.2, 11.6.3), the type is adjusted to T prior to
5
any further analysis. The expression designates the object or function denoted by the reference, and the
6
expression is an lvalue or an xvalue, depending on the expression. [ Note: Before the lifetime of the reference
7
has started or after it has ended, the behavior is undefined (see 6.8). — end note ]
8
9
...

A. K. schrieb:
> mh schrieb:
>> 4 It is unspecified whether or not a reference requires storage (6.7).
>
> Der Satz gefällt mir wesentlich besser als obiges "... haben keine
> Grösse im Speicher".

Vermutlich haben die Verantwortlichen auch etwas länger über die 
Formulierung nachgedacht ;-)

von Rolf M. (rmagnus)


Lesenswert?

A. K. schrieb:
> Rolf M. schrieb:
>>> Ok. Platz brauchen diese Nicht-Objekte aber trotzdem.
>>
>> Sie haben keine Größe im Speicher
>
> Wenn etwas keine Grösse hat, aber Platz braucht, in welcher
> philosophischen Sphäre bewegen wir uns dann?

Man braucht dafür keine Philosophie, nur Abstraktion.

> Diese Struct ist jedenfalls
> gemäss sizeof 8 Bytes gross (x64):
> struct S {
>         int& y;
> };

Und wie groß ist die Referenz gemäß sizeof? Welches Alginment hat eine 
Referenz? Auf diese Fragen gibt es auf der abstrakten Ebene von C++ 
keine Antwort.
Man kann das so sehen: Die Referenz ist kein Objekt und hat damit keine 
spezifische Größe im Speicher, aber um ihre Funktion umzusetzen, müssen 
der Struktur, die sie enthält, Informationen hinzugefügt werden.
Etwa so wie bei virtuellen Memberfunktionen, wo das Objekt meist auch 
größer wird, wenn man der Klasse eine virtuelle Memberfunktion 
hinzufügt, da sie einen versteckten vtable-Zeiger enthält.
Bei der Referenz enthält die Klasse eben einen versteckten Zeiger auf 
das zu referenzierende Objekt.

> Zur Sprache: Welcher Begriff passt zum Member y selbst, dem Ding in der
> Sprache mit dem Namen y. Was ist das?

Eine Referenz. Formal auf C++-Ebene enthält eine Referenz keine Adresse, 
sondern ist lediglich ein anderer Name für ein bereits existierendes 
Objekt, so ähnlich wie ein typedef ein anderer Name für einen 
existierenden Typ ist. Wie das implementiert ist, bleibt dabei aber 
komplett dem Compiler überlassen. Die Speicherung der Adresse des zu 
referenzierenden Objekts ist dabei lediglich eine recht offensichtliche 
Möglichkeit, das zu tun, aber nirgends vorgeschrieben.

: Bearbeitet durch User
von Dirk K. (merciless)


Lesenswert?

A. K. schrieb:
> Inwiefern ist das besser, wenn in der Dokumentation der Funktion der
> dynamische Charakter des Rückgabewerts deutlich aufgeführt wird?
>
> Schlechter Stil ist allerdings die Verwendung von malloc ohne Kontrolle
> auf NULL. ;-)
Du gibst die Ownership für den Speicher
weiter. Das kann leicht übersehen werden.
Außerdem wird die Wiederverwendbarkeit
der Funktion eingeschränkt.

Schlechter Stil ist die Verwendung von
malloc() überhaupt: Würde ich nur unter
Androhung von Waffengewalt so implementieren,
ich würde C++ mit Smart-Pointern verwenden
oder das Array auf den Stack packen. Ich habe
lediglich den Code des TO angepasst.

merciless

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.