Forum: Compiler & IDEs Warum ich Pointer für immer hassen werde!


von Andreas S. (schunki)


Lesenswert?

Hallo zusammen!

Wie die Überschrift schon zum Ausdruck bringt sind Pointer und ich nicht 
gerade die besten Freunde! Was soll's auf in den Kampf! Nur liegen die 
Pointer z.Z. anscheinend weit in Führung. Vielleicht kann mir einer von 
Euch weiter helfen.

Folgende Situation:
Ich lege ein globales zweidimensionales Array mit den Dimensionen 4x4 
an.
1
int16_t TestMatrix1[4][4];

Ich weiß zwar, dass es sich dabei eigentlich bereits um einen Pointer 
handelt, aber der Ordnung halber lege ich noch einen globalen Pointer an 
der dann mein Array später ansprechen soll.
1
int16_t (*PtrTestMatrix1)[4][4];

In meiner Main wird dann der Pointer zugewiesen und soll später an eine 
Funktion übergeben werden.
1
void matrixtest(void){
2
     PtrTestMatrix1 = TestMatrix1;
3
     func(PtrTestMatrix1);
4
}

In der Funktion selbst will ich dann auf Werte von TestMatrix1 zugreifen 
und diese ggf. verändern.
1
void func(int16_t (*Matrix)[4][4]){
2
     *(Matrix+4)=2;     //um z.B. auf Position [0][2] den Wert 2 zu schreiben
3
}

So hatte ich mir das Ganze zumindest gedacht. Jedoch bombadiert mich 
mein Compiler mit zig Warnungen und Fehlermeldungen nachdem ich jetzt 
schon die gefühlte 1.000.000te Variante ausprobiert habe.

Kann mir also hier jemand auf die Schnelle sagen, wie es richtig geht?


Gruß
Andreas

: Verschoben durch User
von Cyblord -. (cyblord)


Lesenswert?

Andreas S. schrieb:

> In der Funktion selbst will ich dann auf Werte von TestMatrix1 zugreifen
> und diese ggf. verändern.
>
>
1
> void func(int16_t (*Matrix)[4][4]){
2
>      *(Matrix+4)=2;     //um z.B. auf Position [0][2] den Wert 2 zu 
3
> schreiben
4
> }
5
>

Einfach so:
1
  void func(int16_t** m) {
2
    m[0][2]=2;
3
}

Dieser Funktion übergibst du direkt dein Array (nicht den extra Pointer 
drauf).
1
func(TestMatrix1);

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:

> Folgende Situation:
> Ich lege ein globales zweidimensionales Array mit den Dimensionen 4x4
> an.
>
>
1
> int16_t TestMatrix1[4][4];
2
>
>
> Ich weiß zwar, dass es sich dabei eigentlich bereits um einen Pointer
> handelt

siehst du. Da fängt es schon an.

Nein!

Ein Pointer ist ein Pointer. Ein Array ist ein Array.
Und ein Pointer ist KEIN Array! Ein Array ist KEIN Pointer.

Wo immer du das auch her hast: VERGISS ES.
Ein Array ist kein Pointer.
In manchen Fällen degeneriert ein Array zu seiner Startadresse, zum 
Beispiel wenn ein Array an eine Funktion übergeben wird. Aber das 
bedeutet nicht, dass ein Array ein Pointer IST!

: Bearbeitet durch User
von Nick (Gast)


Lesenswert?


von Peter II (Gast)


Lesenswert?

Andreas S. schrieb:
> Kann mir also hier jemand auf die Schnelle sagen, wie es richtig geht?

woher soll denn der Compiler wissen das sich um ein zweidimensionales 
Array handelst, wenn du nur noch ein pointer hast?

Lass den Unsinn mit dem extra Zeiger weg und schon geht es. 
Zweidimensionales Array lassen sich auch nicht als Parameter für 
Funktionen übergeben, weil dort wieder das wissen über die Dimensionen 
verloren geht.

du kannst aber ein normales Array verwenden und dort den index selber 
errechnen.


int test[8];

test[ x*4 + y ] = 123;

von Chris (Gast)


Lesenswert?

Ein statisches Array wie hier geht problemlos auch als 
Parameteruebergabe.
Was aber nicht geht ist ein dynamisches Array, also wo nicht bekannt ist
wie es structuriert ist.

von Karl H. (kbuchegg)


Lesenswert?

1
int matrix[4][4];
2
3
4
void foo( int (*mat)[4] )
5
{
6
  mat[3][3] = 8;
7
}
8
9
int main()
10
{
11
  foo( matrix );
12
}

von Helmut L. (helmi1)


Lesenswert?

1
#include <stdio.h>
2
3
typedef int TM[4][4];
4
5
TM tm;
6
7
void TestFunc(TM ttm);
8
9
void main(void)
10
{
11
  tm[2][3] = 4;
12
  tm[0][2] = 3;
13
  TestFunc(tm);
14
}
15
16
void TestFunc(TM ttm)
17
{
18
  printf("D1:%d\n",ttm[2][3]);
19
  printf("D2:%d\n",ttm[0][2]);
20
}

 Oder man macht es mit einen typedef

von Karl H. (kbuchegg)


Lesenswert?

>
1
>   void func(int16_t** m) {
2
>     m[0][2]=2;
3
> }
4
>
>

Der Datentyp stimmt nicht.
ein 2-d Array ist kein **
Das Aufbau im Speicher ist ein anderer.

Ein ** wäre in diesem Fall angebaracht
1
  +---+                +---+---+---+---+
2
  | o----------------->|   |   |   |   |
3
  +---+                +---+---+---+---+  +---+---+---+---+
4
  | o------------------------------------>|   |   |   |   |
5
  +---+         +---+---+---+---+         +---+---+---+---+
6
  | o---------->|   |   |   |   |
7
  +---+         +---+---+---+---+   +---+---+---+---+
8
  | o------------------------------>|   |   |   |   |
9
  +---+                             +---+---+---+---+

Auch das kann man als 2D Array ansehen. Aber es ist eben nicht das was 
mit einem
1
int mat[4][4];
allokiert wird.

: Bearbeitet durch User
von Andreas S. (schunki)


Lesenswert?

cyblord ---- schrieb:
> Einfach so:  void func(int16_t** m) {
>     m[0][2]=2;
> }
>
> Dieser Funktion übergibst du direkt dein Array (nicht den extra Pointer
> drauf).
> func(TestMatrix1);

So bekomme ich in der Funktion kein Array mehr, sondern mein Debugger 
zeigt mir hier lediglich noch eine einzige int-Variable unter m an. Kein 
Array mehr!

von Andreas S. (schunki)


Lesenswert?

Karl Heinz schrieb:
>>
1
>>   void func(int16_t** m) {
2
>>     m[0][2]=2;
3
>> }
4
>>
>>
>
> Der Datentyp stimmt nicht.
> ein 2-d Array ist kein **

Sondern??????

von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:
> Karl Heinz schrieb:
>>>
1
>>>   void func(int16_t** m) {
2
>>>     m[0][2]=2;
3
>>> }
4
>>>
>>>
>>
>> Der Datentyp stimmt nicht.
>> ein 2-d Array ist kein **
>
> Sondern??????

Ein ** wäre in diesem Fall angebaracht
1
  +---+                +---+---+---+---+
2
  | o----------------->|   |   |   |   |
3
  +---+                +---+---+---+---+  +---+---+---+---+
4
  | o------------------------------------>|   |   |   |   |
5
  +---+         +---+---+---+---+         +---+---+---+---+
6
  | o---------->|   |   |   |   |
7
  +---+         +---+---+---+---+   +---+---+---+---+
8
  | o------------------------------>|   |   |   |   |
9
  +---+                             +---+---+---+---+
Auch das kann man als 2D Array ansehen. Aber es ist eben nicht das was 
mit einem
1
int mat[4][4];
allokiert wird.

der sieht im Speicher so aus
1
   +---+---+---+---+
2
   |   |   |   |   |
3
   +---+---+---+---+
4
   |   |   |   |   |
5
   +---+---+---+---+
6
   |   |   |   |   |
7
   +---+---+---+---+
8
   |   |   |   |   |
9
   +---+---+---+---+
wobei die einzelnen Zeilen fortlaufend allokiert werden. Es gibt keine 
Zwischenpointer, die die einzelnen Zeilen zu einem Array zusammenfassen 
würden.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Helmut Lenzen schrieb:

>  Oder man macht es mit einen typedef

Wobei typedefs bei komplizierteren Datenstrukturen (ja, ein 2D Array in 
C qualifiziert sich schon für sowas) IMMER eine extrem gute Idee sind!

von Andreas S. (schunki)


Lesenswert?

Chris schrieb:
> Ein statisches Array wie hier geht problemlos auch als
> Parameteruebergabe.
> Was aber nicht geht ist ein dynamisches Array, also wo nicht bekannt ist
> wie es structuriert ist.

Da ich bei der ersten Version des Programms bei vielen Matritzen und 
einem Funktionsaufruf mit Call by value Speicherprobleme bekommen habe, 
will ich hier nun mal mein Glück mit Call by Referenz probieren!

von Rolf Magnus (Gast)


Lesenswert?

Andreas S. schrieb:
> aber der Ordnung halber lege ich noch einen globalen Pointer an
> der dann mein Array später ansprechen soll.
> int16_t (*PtrTestMatrix1)[4][4];

Wozu soll diese "Ordnung" gut sein? Du legst doch auch nicht für jeden 
int einen Pointer an, über den du dann darauf zugreifst, oder?

> In der Funktion selbst will ich dann auf Werte von TestMatrix1 zugreifen
> und diese ggf. verändern.
> void func(int16_t (*Matrix)[4][4]){
>      *(Matrix+4)=2;     //um z.B. auf Position [0][2] den Wert 2 zu
> schreiben
> }

Du nutzt hier einen Zeiger auf ein Array, was eher unüblich, aber 
durchaus möglich ist. Die Syntax für den Zugriff wäre dann:
1
      (*Matrix)[0][2]=2;     //um z.B. auf Position [0][2] den Wert 2 zu schreiben

Eher üblich ist ein Zeiger auf das erste Element des Arrays, der sich 
automatisch bei der Übergabe ergibt. Das kann man sogar direkt so 
hinschreiben, wie die Array-Definition selbst:
1
void func(int16_t Matrix[4][4])
2
{
3
      Matrix[0][2]=2;     //um z.B. auf Position [0][2] den Wert 2 zu schreiben
4
}
5
6
int16_t TestMatrix1[4][4];
7
func(TestMatrix1);

Hier wird nicht das ganze Array in die Funktion kopiert, sondern nur ein 
Zeiger übergeben, da bei Funktions-Argumenten aus einem Array autmatisch 
ein Zeiger auf dessen erstes Element wird.

von Peter II (Gast)


Lesenswert?

Karl Heinz schrieb:
> int matrix[4][4];
>
> void foo( int (*mat)[4] )
> {
>   mat[3][3] = 8;
> }
>
> int main()
> {
>   foo( matrix );
> }

ist das wirklich zulässig. Ich dachte Zahlen in [] werden bei 
funktionparametern ignoriert?

Aber auch wenn das zulässig ist, schön ist es auf jeden Fall nicht. Denn 
man braucht 2 verschiedene Datentypen um das gleiche darzustellen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Andreas S. schrieb:
> Da ich bei der ersten Version des Programms bei vielen Matritzen und
> einem Funktionsaufruf mit Call by value Speicherprobleme bekommen habe,
> will ich hier nun mal mein Glück mit Call by Referenz probieren!

Es ist mehr als unwahrscheinlich, daß das Dein Problem ist -- Arrays 
werden in C nie mit "call by value" übergeben.

von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:

> So bekomme ich in der Funktion kein Array mehr, sondern mein Debugger
> zeigt mir hier lediglich noch eine einzige int-Variable unter m an. Kein
> Array mehr!

Logisch.
Bei der Übergabe der Startadresse eines Arrays, geht immer die Kentniss 
über die Größe einer Dimension verloren.

Eine Funktion kann prinzipiell nicht feststellen wie groß das Array ist, 
dessen Startadresse sie in Form eines Pointers bekommen hat.
Eine Funktion kann noch nicht einmal feststellen, ob der Pointer jetzt 
auf ein Array oder auf eine einzelne Variable zeigt.

Das obliegt einzig und alleine deiner Sorgfalt, dass es hier zu keinen 
Problemen kommt.

von Karl H. (kbuchegg)


Lesenswert?

Peter II schrieb:
> Karl Heinz schrieb:
>> int matrix[4][4];
>>
>> void foo( int (*mat)[4] )
>> {
>>   mat[3][3] = 8;
>> }
>>
>> int main()
>> {
>>   foo( matrix );
>> }
>
> ist das wirklich zulässig. Ich dachte Zahlen in [] werden bei
> funktionparametern ignoriert?

Ja und nein.
Alle Angaben, bis auf die erste, sind Teil des Datentyps!
Denn ein
1
  int (*mat)[4]
ist etwas anderes als ein
1
  int (*mat)[5]


Man hätte es auch als
1
void foo( int mat[][4] )
2
{
3
  mat[3][3] = 8;
4
}
schreiben können. In der ersten Klammer kannst du die Dimensionsangabe 
weglassen. Sie hat sowieso keinen Effekt. Aber in den weiteren nicht!

von Rolf Magnus (Gast)


Lesenswert?

Peter II schrieb:
> ist das wirklich zulässig.

Es ist notwendig.

> Ich dachte Zahlen in [] werden bei funktionparametern ignoriert?

Nur bei der ersten Dimension, da bei der Übergabe eh aus dem Array ein 
Zeiger auf dessen erstes Element gemacht wird und damit die Größe 
verlorengeht. Das erste Element ist in diesem Falle aber wieder ein 
Array, und dessen Größe muß bekannt sein, damit der Zeigertyp 
vollständig ist.

von Karl H. (kbuchegg)


Lesenswert?

Ganz ehrlich.
Anstatt da mit 2D-Arrays rumzumachen, würde ich die 4 Vektoren einer 
Matrix in eine Struktur verpacken
1
struct vector
2
{
3
  double coord[4];
4
};
5
6
struct matrix
7
{
8
  struct vector axes[4];
9
};

dann bist du die 2D-Arrays los und durch die Verpackung in Strukturen 
verhält sich alles wieder wie gewohnt.

von Andreas S. (schunki)


Lesenswert?

Sooooo...... ich hab's!

1
void func(int16_t (*Matrix)[4][4]){
2
     (*Matrix)[0][2]=2;     //so geht's!!!
3
}

und der Funktionsaufruf in der Main dazu lautet
1
func(PtrTestMatrix1);

@Rufus:
Aber wird nicht der Funktionszustand samt aller in ihr enthaltenden 
lokalen Variablen auf dem Stack zwischengespeichert, wenn ich eine 
Funktion aufrufe? Dann müssten doch auch alle meine Matritzen hier 
abgelegt werden und mir je nach Menge und Datentyp meinen Stack recht 
schnell voll packen! Daher bin ich es jetzt einmal mit globalen 
Variablen angegangen und arbeite hier zusätzlich auch noch mit Pointern.

von (prx) A. K. (prx)


Lesenswert?

Oder man verwendet Typedefs.

typedef double coord_t[4];
coord_t var[4];
void function (coord_t *arg);
Aufruf: function(var):

von (prx) A. K. (prx)


Lesenswert?

Andreas S. schrieb:
> Aber wird nicht der Funktionszustand samt aller in ihr enthaltenden
> lokalen Variablen auf dem Stack zwischengespeichert, wenn ich eine
> Funktion aufrufe?

Arrays werden als Pointer auf das erste Element übergeben. Nur alles 
andere wird als Wert übergeben.

: Bearbeitet durch User
von Lutz H. (luhe)


Lesenswert?

Es soll kein Programm mit Pointern existieren, dass stabil läuft.
Deshalb werden solche Programme als unsicherer Code bezeichnet.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:
> Sooooo...... ich hab's!
>
>
>
1
> void func(int16_t (*Matrix)[4][4]){
2
>      (*Matrix)[0][2]=2;     //so geht's!!!
3
> }
4
>
>
> und der Funktionsaufruf in der Main dazu lautet
>
>
1
> func(PtrTestMatrix1);
2
>
>

Das funktioniert zwar, ist aber im Grunde eine unnötige 
Pointer-Hierarchie zuviel.

von Andreas S. (schunki)


Lesenswert?

A. K. schrieb:
> Arrays werden als Pointer auf das erste Element übergeben. Nur alles
> andere wird als Wert übergeben.

Nochmal..... es ging mir nicht um die Übergabe. Sondern was mit den 
Variablen einer Funktion geschieht, wenn ich eine neue Funktion aus ihr 
heraus aufrufe. Meines Wissens nach, wird der Zustand der dann 
"pausierenden" Funktion samt Rücksprungadresse zu dieser Funktion auf 
dem Stack abgelegt. Da müsste dann doch eigentlich auch die Arrays 
betreffen. Zumindest die, die nicht an die neue Funktion übergeben 
werden.

Beispiel:
1
void main....
2
3
  int a=5;
4
  int Array1[5];
5
  int Array2[3];
6
  Array1 = {1,2,3,4,5};
7
  Array2 = {8,9,10}; 
8
  func(&Array1);

wenn mein Prozessor jetzt aus der Main raus in func springt, wird die 
Rücksprungadresse auf den Stack geschrieben, damit der Prozessor weiß wo 
er nach Abarbeiten von func weiter machen muss. Zuätzlich müssen doch 
die Variable a und zumindest das Array2 zwichengespeichert werden. Oder 
seh ich das falsch?

von Andreas S. (schunki)


Lesenswert?

Karl Heinz schrieb:
> Das funktioniert zwar, ist aber im Grunde eine unnötige
> Pointer-Hierarchie zuviel.

Jetzt lasst mir doch mal mein Erfolgserlebnis! ;-)

von Helmut L. (helmi1)


Lesenswert?

lutz h. schrieb:
> Es soll kein Programm mit Pointern existieren, dass stabil läuft.
> Deshalb werden solche Programme als unsicherer Code bezeichnet.

Wenn man nicht Autofahren kann soll man die Finger davon lassen ...

von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:

> Aber wird nicht der Funktionszustand samt aller in ihr enthaltenden
> lokalen Variablen auf dem Stack zwischengespeichert, wenn ich eine
> Funktion aufrufe?

Ja. Was hat das mit dem Problem zu tun?

> Dann müssten doch auch alle meine Matritzen hier
> abgelegt werden und mir je nach Menge und Datentyp meinen Stack recht
> schnell voll packen!

Nö. Wieso sollte das so sein?

Arrays werden grundsätzlich und immer durch Übergabe der Startadresse 
übergeben. Arrays werden nicht übergeben, indem die Funktion eine Kopie 
der Daten bekommt.

> Daher bin ich es jetzt einmal mit globalen
> Variablen angegangen und arbeite hier zusätzlich auch noch mit Pointern.

Unnötig.


Brechen wir den Fall mal runter auf 1D (damit wir nicht dauernd die 
Syntax mitschleppen)

so ist es der einfachste Fall
1
void foo( int werte[] )
2
{
3
  werte[3] = 8;
4
}
5
6
int main()
7
{
8
  int values[5];
9
10
  foo( values );
11
}

Ob man jetzt im Funktionskopf
1
void foo( int werte[] )
schreibt, oder
1
void foo( int * werte )
ist Jacke wie Hose. Beide Formen sind nichts anderes als 
unterschiedliche Schreibweisen, sind aber in allen Punkten identisch.

Was du jetzt machst ist, du führst einen zusätzlichen Pointer ein.
1
void foo( int werte[] )
2
{
3
  werte[3] = 8;
4
}
5
6
int main()
7
{
8
  int values[5];
9
  int * ptr;
10
11
  ptr = values;
12
13
  foo( ptr );
14
}
wozu der Pointer? Kein Mensch braucht den.

Du gehst sogar noch einen Schritt weiter, indem du einen Pointer auf 
Pointer bastelst
1
void foo( int** werte )
2
{
3
  (*werte)[3] = 8;
4
}
5
6
int main()
7
{
8
  int values[5];
9
  int * ptr;
10
11
  ptr = values;
12
13
  foo( &ptr );
14
}
und dir nochmal eine Pointer Ebene dazu holst. Anstatt das Problem zu 
vereinfachen, verkomplizierst du es noch weiter.

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


Lesenswert?

Andreas S. schrieb:
> Da müsste dann doch eigentlich auch die Arrays
> betreffen. Zumindest die, die nicht an die neue Funktion übergeben
> werden.

Nochmal..... es wird die Adresse vom Array übergeben, nicht das Array 
selbst. Und dabei bleibt es. Es wird also nicht anschliessend das Array 
irgendwohin kopiert, auch nicht in der Funktion selbst bei Aufruf einer 
weiteren Funktion.

> wenn mein Prozessor jetzt aus der Main raus in func springt, wird die
> Rücksprungadresse auf den Stack geschrieben, damit der Prozessor weiß wo
> er nach Abarbeiten von func weiter machen muss. Zuätzlich müssen doch
> die Variable a und zumindest das Array2 zwichengespeichert werden. Oder
> seh ich das falsch?

Äpfel und Birnen. Hier liegen die Arrays von Anfang an im Kontext von 
main und da bleiben sie auch. Im fraglichen Fall ging es um Übergabe von 
Arrays.

Die Fälle
  void f1(void) { int a[10]; g(a); }
  void f2(int a[10]) { g(a) }
sind also sehr verschieden im Platz auf dem Stack. Nur im ersten Fall 
liegt das Array auf dem Stack, im zweiten Fall nicht.

: Bearbeitet durch User
von Frank B. (f-baer)


Lesenswert?

lutz h. schrieb:
> Es soll kein Programm mit Pointern existieren, dass stabil läuft.
> Deshalb werden solche Programme als unsicherer Code bezeichnet.

So ein Unfug! Pointer sind ein mächtiges Werkzeug, das aber auch viel 
Verantwortung im Umgang voraussetzt. Nutzt man Pointer richtig, und dazu 
gehört beispielsweise immer die Prüfung auf Gültigkeit des Pointers, 
dann ist das genauso sicher als würde man nur mit Call by Value 
arbeiten. Nur muss der Programmierer dazu den Kopf anstrengen. Aber wer 
sein Leben lang nur Java "programmiert" hat, ist das leider nicht 
gewohnt...

von Karl H. (kbuchegg)


Lesenswert?

Andreas S. schrieb:
> A. K. schrieb:
>> Arrays werden als Pointer auf das erste Element übergeben. Nur alles
>> andere wird als Wert übergeben.
>
> Nochmal..... es ging mir nicht um die Übergabe. Sondern was mit den
> Variablen einer Funktion geschieht, wenn ich eine neue Funktion aus ihr
> heraus aufrufe. Meines Wissens nach, wird der Zustand der dann
> "pausierenden" Funktion samt Rücksprungadresse zu dieser Funktion auf
> dem Stack abgelegt.

Das ist zwar nicht ganz falsch. Aber du zäumst das Pferd von der 
falschen Seite auf.
Denn die lokalen Variablen der Funktion, die den Aufruf macht, die 
existieren ja sowieso schon auf dem Stack. D.h. da wird nichts 
zusätzlich angelegt.
Lediglich für die Funktion, die aufgerufen wird, wird am Stack etwas 
zusätzliches angelegt. Die Funktion kriegt aber sowieso keine Arrays in 
Form von Werten übergeben. Es wird keine Kopie der Daten erzeugt. Daher 
belastet der Funktionsaufruf den Stack lediglich in Form eines einzigen 
Pointers, nämlich der lokalen Variable der gerufenen Funktion.

In diesem Sinne:
> Meines Wissens nach, wird der Zustand der dann "pausierenden"
> Funktion .... auf dem Stack abgelegt.

Nein, der wird dort nicht extra abgelegt. Der ist schon dort! Dieser 
'Zustand' ist erzeugt worden als diese Funktion betreten wurde. Wenn die 
Funktion seinerseits dann andere Funktionen aufruft, dann ändert sich am 
Umfang dieses 'Variablen-Zustandes' nichts mehr.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Frank Bär schrieb:

> arbeiten. Nur muss der Programmierer dazu den Kopf anstrengen. Aber wer
> sein Leben lang nur Java "programmiert" hat, ist das leider nicht
> gewohnt...


:-)

Meine erste Bekanntschaft mit einem Java Programm schloss ich, als ich 
mir ein Demo-Programm aus dem Web geholt habe, es gestartet habe und in 
der Ausgabe den Text
"Null Pointer Exception"
gelesen habe.
Ich habs dann gleich wieder von der Maschine runtergelöscht und das 
Java-SDK deinstalliert.

von Cyblord -. (cyblord)


Lesenswert?

Karl Heinz schrieb:
> Frank Bär schrieb:
>
>> arbeiten. Nur muss der Programmierer dazu den Kopf anstrengen. Aber wer
>> sein Leben lang nur Java "programmiert" hat, ist das leider nicht
>> gewohnt...
>
>
> :-)
>
> Meine erste Bekanntschaft mit einem Java Programm schloss ich, als ich
> mir ein Demo-Programm aus dem Web geholt habe, es gestartet habe und in
> der Ausgabe den Text
> "Null Pointer Exception"
> gelesen habe.
> Ich habs dann gleich wieder von der Maschine runtergelöscht und das
> Java-SDK deinstalliert.

Tja in den Anfängerfehlern sind sich C und Java nicht unähnlich.

Der typische Fall warum ein Null Pointer Exception ausgelöst wird:

Object o;
o.doThis();

passiert Anfängern in C fast genauso. Nur dass es da keine hübsche 
Exception gibt.
Bei Win98 konnte man sich so in C noch einen Bluescreen einfange. Seit 
2000 wird das Programm halt vom BS gekillt.

gruß cyblord

von Karl H. (kbuchegg)


Lesenswert?

Frank Bär schrieb:
> lutz h. schrieb:
>> Es soll kein Programm mit Pointern existieren, dass stabil läuft.
>> Deshalb werden solche Programme als unsicherer Code bezeichnet.
>
> So ein Unfug! Pointer sind ein mächtiges Werkzeug, das aber auch viel
> Verantwortung im Umgang voraussetzt.

Exakt.
Pointer sind nichts anderes als das, was die Assembler-Programmierer als 
'indirekte Adressierung' kennen.

Anstatt bei einem Load (ich benutz jetzt mal Z80) die Adresse von der 
geladen werden soll direkt anzugeben
1
   ld  A, (0x1234)
steht die Adresse ihrerseits wieder in einem Register
1
   ld  A,[HL]
wie die Adresse in das Register HL gekommen ist, ist damit nicht gesagt. 
Der Befehl sagt lediglich: Lade den Akku von der Adresse, die im 
Register HL gespeichert ist. Eben indirekt adressiert. HL ist 'die 
Pointervariable', welche die Adresse enthält.

In diesem Sinne: Ich kann mir nicht vorstellen, dass es auch nur 
irgendein Programm gibt, welches im Inneren nicht in irgendeiner Form 
einen 'Pointer' benutzt. OK, vielleicht ganz einfache. LED einschalten, 
ausschalten, blinken. Aber in irgendeiner Form kommt indirekte 
Adressierung in jedem ernstzunehmenden Programm irgendwo vor.

Das muss man eben lernen, damit umzugehen. Da führt kein Weg drann 
vorbei. Am Anfang sind Pointer etwas seltsam, aber irgendwann macht es 
klack und man merkt, dass das zum einen viel einfacher ist als man die 
ganze Zeit gedacht hat und zum zweiten das man sich ein mächtiges 
Werkzeug gefügig gemacht hat. Der ganze Hype "Pointer sind ja sooooo 
schwer" ist völlig unnötig.

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


Lesenswert?

Nur sind eben Pointer auch ein prima Weg, sich ins Knie zu schiessen. 
Weshalb das auch oft passiert - und auch oft Profis passiert. 
Programmiersprachen mit wesentlich restriktiverem Umgang mit Pointern 
und Arrays können etliche Fehler verhindern, teils direkt bei der 
Programmierung, teils zu Laufzeit.

von Davis (Gast)


Lesenswert?

A. K. schrieb:

> Nur sind eben Pointer auch ein prima Weg, sich ins Knie zu schiessen.
> Weshalb das auch oft passiert - und auch oft Profis passiert.

Quatschkopf. Einem Profi, dem dies (oft) passiert - ist kein Profi.

von Karl H. (kbuchegg)


Lesenswert?

Davis schrieb:

> Quatschkopf. Einem Profi, dem dies (oft) passiert - ist kein Profi.

Mit der Betonung auf oft.
Natürlich passiert es auch einem Profi ab und an. Allerdings verzweifelt 
ein Profi nicht daran, sondern behebt das Problem.
Und wie du schon sagst: So oft ist das dann auch wieder nicht, denn - er 
ist ja Profi und hat die Dinge von der Pieke auf gelernt. Profis gehen 
auch ganz anders an derartige Sachen heran, eben weil sie schon wissen 
wo die neuralgischen Punkte liegen, wie die zu behandeln sind und (auch 
wichtig) wie sie zu testen sind.

: Bearbeitet durch User
von Cyblord -. (cyblord)


Lesenswert?

A. K. schrieb:
> Nur sind eben Pointer auch ein prima Weg, sich ins Knie zu schiessen.
> Weshalb das auch oft passiert - und auch oft Profis passiert.
> Programmiersprachen mit wesentlich restriktiverem Umgang mit Pointern
> und Arrays können etliche Fehler verhindern, teils direkt bei der
> Programmierung, teils zu Laufzeit.

Nur kann man das nicht mit einer besseren Programmiersprache alleine 
erreichen. Der Compiler kann erstmal nur alles zur Übersetzungszeit 
finden. In dem Moment wo du das dynamische anlegen eines Arrays zur 
Laufzeit hast, kannst du solche Dinge wie Überlauf usw. nur durch das 
Laufzeitsystem abfangen und das erkaufst du durch massig overhead. Für 
die Systemprogrammierung, BS-Programmierung oder für kleine Controller 
ist das einfach nicht praktikabel.

gruß cyblord

von Rolf Magnus (Gast)


Lesenswert?

Andreas S. schrieb:
> Nochmal..... es ging mir nicht um die Übergabe. Sondern was mit den
> Variablen einer Funktion geschieht, wenn ich eine neue Funktion aus ihr
> heraus aufrufe. Meines Wissens nach, wird der Zustand der dann
> "pausierenden" Funktion samt Rücksprungadresse zu dieser Funktion auf
> dem Stack abgelegt. Da müsste dann doch eigentlich auch die Arrays
> betreffen.

Und was denkst du, wo dein Array liegt, bevor du eine Unterfunktion 
aufrufst?

Frank Bär schrieb:
> Aber wer sein Leben lang nur Java "programmiert" hat, ist das leider
> nicht gewohnt...

... und vergisst, daß auch seine Java-Laufzeitumgebung und der 
Betriebssystem-Kernel in C geschrieben worden sind - unter ausgiebiger 
Nutzung von Zeigern. Daher ist das:

lutz h. schrieb:
> Es soll kein Programm mit Pointern existieren, dass stabil läuft.
> Deshalb werden solche Programme als unsicherer Code bezeichnet.

auch eine ziemlich unsinnige Aussage, da es praktisch auf jedes Programm 
zutrifft, wenn man den gesamten ausgeführten Code betrachtet und nicht 
nur den sebst geschriebenen Teil.

von Frank B. (f-baer)


Lesenswert?

A. K. schrieb:
> Nur sind eben Pointer auch ein prima Weg, sich ins Knie zu schiessen.
> Weshalb das auch oft passiert - und auch oft Profis passiert.
> Programmiersprachen mit wesentlich restriktiverem Umgang mit Pointern
> und Arrays können etliche Fehler verhindern, teils direkt bei der
> Programmierung, teils zu Laufzeit.

Restriktiverer Umgang mit Pointern geht immer mit erhöhter Codegröße 
einher.
Folgendes Szenario:
Man will Rohdaten im Flash speichern. Bei Integer-Werten ist das noch 
trivial, aber spätestens bei Gleitkommazahlen wird es interessant.
Hier ist dann sogar der ach so verhasste void-Pointer erforderlich. 
Benutze ich den nicht, brauche ich unterschiedliche Funktionen für 
unterschiedliche Datentypen. Das ergibt an der Stelle Overhead und ist 
langsam.

Oder beim Arbeiten mit komplexen Strukturen:
Eine nicht-globale Struktur kann ich nicht an eine Unterfunktion 
übergeben, ohne mit Pointern zu arbeiten. Ich habe dann die Wahl, die 
Struktur global zu machen, was RAM frisst, oder auf Optimierung mithilfe 
von Unterfunktionen zu verzichten, was wieder die Codegröße nach oben 
treibt.

Ein korrektes Errorhandling ist immer erforderlich, ob ich nun mit 
Pointern arbeite, oder ohne. Die zusätzliche Abfrage, ob der Pointer 
gültig ist, bricht da keinem das Bein.

Und nur mal so: Sobald man wirklich hardwarenah arbeitet, z.B. mit 
eigenem Filesystem, sind Pointer absolut unverzichtbar. Lineare 
Programmierung mag ohne Pointer auskommen, aber ein interrupt- oder 
DMA-basiertes System lässt sich kaum noch ohne Zeiger handhaben.

Wer sich mit Pointern ins Knie schiesst, programmiert entweder 
kenntnisbefreit oder ohne Sorgfalt. Letzteres fällt beim Test auf, 
ersteres führt leider zu häufig nicht zu der Erkenntnis, dass man 
Nachholbedarf in Sachen Pointer hat, sondern gipfelt in der Aussage, 
dass Pointer Mist sind und andere Programmiersprachen eh alles viel 
besser machen. Es gibt Gründe, warum sich C auf Mikrocontrollern wacker 
hält. Es erzeugt performanten UND kleinen Code. Und das eben unter 
Anderem deswegen, weil man in C mit Zeigern arbeiten kann.

von Thosch (Gast)


Lesenswert?

cyblord ---- schrieb:
> Nur kann man das nicht mit einer besseren Programmiersprache alleine
> erreichen. Der Compiler kann erstmal nur alles zur Übersetzungszeit
> finden.
Klar. Allerdings kann eine "bessere Programmiersprache" (ich meine damit 
hier einen restriktiveren Compiler) insbesondere Anfänger vor gar zu 
unsinnigen Konstrukten bewahren.
z.B. zwingt einen die strikte Typisierung in Sprachen wie Pascal zu 
einer sauberen Verwendung von Pointern, so daß man sich bestimmte 
Pfuschereien, die C nicht verbietet, gar nicht erst angewöhnt.

Wer wie ich im Studium mit Pascal das Programmieren gelernt hat (1990),
hat auch mit C wenig Probleme. (sofern man mit einem guten C-Buch 
umsteigt!) Und es bleiben einem sicherlich viele der typischen 
C-Anfängerfehler erspart.


> In dem Moment wo du das dynamische anlegen eines Arrays zur
> Laufzeit hast, kannst du solche Dinge wie Überlauf usw. nur durch das
> Laufzeitsystem abfangen und das erkaufst du durch massig overhead. Für
> die Systemprogrammierung, BS-Programmierung oder für kleine Controller
> ist das einfach nicht praktikabel.

Das ist nochmal 'ne andere Welt. Natürlich programmiert man Anwendungen 
auf dem PC anders als µC-Firmware. Und nutzt auf dem "großen Rechner" 
natürlich auch die diversen Runtime-Checks wie Range- und 
Stack-Checking, da der Overhead hier kaum eine Rolle spielt. Damit 
findet man gerade typischen Anfängerfehler schneller und lernt, sie zu 
vermeiden.

Insofern ist es natürlich von Vorteil, wenn man zuvor auf dem PC 
programmieren gelernt hat, bevor man auf eine µC-Plattform losgeht...

von Lutz H. (luhe)


Lesenswert?

Frank Bär schrieb:
> beispielsweise immer die Prüfung auf Gültigkeit des Pointers,

Wird aber oft weggelassen, wie hier im Beispiel.

von Karl H. (kbuchegg)


Lesenswert?

lutz h. schrieb:
> Frank Bär schrieb:
>> beispielsweise immer die Prüfung auf Gültigkeit des Pointers,
>
> Wird aber oft weggelassen, wie hier im Beispiel.

Weil es ein zweischneidiges Schwert ist.
Denn was ist ein 'gültiger' Pointer?
Er kann erst mal nicht NULL sein, soviel steht schon mal fest. Aber 
dann? Wie erkennst du, ob ein Nicht-NULL Pointer gültig ist?
Die Antwort ist: ohne massiven Zusatzaufwand - gar nicht.

d.h. alles was man tun kann ist, eine Prüfung einzubauen, ob man einen 
NULL Pointer bekommen hat. Das ist in vielen Fällen auch angebracht, es 
gibt jedoch auch Fälle, in denen man mit Sicherheit weiß, dass man 
keinen NULL Pointer bekommen wird, weil diesen Teil der Vereinbarung 
bereits der Aufrufer geprüft hat.
Software wird ja normalerweise in Schichten aufgebaut. Und da muss man 
dann auch abwägen, bis zu welchen tieferen Schichten man die Prüfungen 
mitzieht und ab wann (und vor allen Dingen bei welchen Funktionen) man 
davon ausgeht, dass der Pointer nicht NULL sein kann, weil höher 
liegende Schichten diesen Fall bereits abgefangen haben.
Es ist auch nicht ungewöhnlich, dass man 2 Versionen hat: eine Debug 
Version, die wesentlich rigoroser diese Problematik prüft und meldet und 
eine Release Version in der diese Tests in den unteren Schichten 
unterbleiben, weil sich ja beim Kunden die Software nicht mehr ändert 
und es daher auch keinen Call gibt, der einen NULL Pointer bis in diese 
Schichten durchreichen würde. An dieser Stelle wird dann der möglichen 
Performance der Vorzug gegeben. Eine Prüfung auf NULL Pointer in einer 
Matrix-Vektor Multiplikation ist in einem CAD nun mal ein massives 
Performance-Problem.

Aber: das behandelt nur den Fall NULL Pointer.
gegen einen wild gewordenen Pointer oder einen Pointer auf eine längst 
gelöschte Datenstruktur hilft auch das nicht. Da hilft nur: Üben, üben, 
üben, sauberer Programmaufbau, wissen was man tut, Erfahrung, vermeiden 
von neuralgischen Konstrukten, üben, üben, üben.
Und Mann! Was habe ich in meiner Studeinzeit geübt: lineare Listen, 
einfach und doppelt verkettet, mit Tail-Pointer und ohne, mit 
dezidiertem Head-Element und ohne, mit dezidiertem Tail-Element und 
ohne, Kombinationen davon. Solange, bis ich von jeder Implementierung 
exakt und 100% verstanden habe, wie sie funktioniert, wie und warum 
welche Pointer Manipulationen in welcher Reihenfolge gemacht werden - 
gemacht werden müssen, weil man sich sonst einen noch benötigten Pointer 
unter dem Hintern weglöscht. Wo die Knackpunkte bei den neuralgischen 
Operationen 'Add' und 'Remove' liegen. Wie sich die vorher genannte Wahl 
der Zutaten auf die Code-Gestaltung der Operationen auswirkt.
Dann dasselbe mit Bäumen: Binärbäume, nicht balanziert und balanziert, 
B-Bäume, Rot-Schwarz Bäume. Hashtables. Zusammengesetzte 
Datenstrukturen. Aber dann hatte ich Pointer intus. Sie waren genauso 
natürlich, wie es auch ein int ist. Und siehe da, keine Probleme beim 
ersten Einsatz in der Industrieprogrammierung.

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


Lesenswert?

Karl Heinz schrieb:
> Und wie du schon sagst: So oft ist das dann auch wieder nicht, denn - er
> ist ja Profi und hat die Dinge von der Pieke auf gelernt. Profis gehen
> auch ganz anders an derartige Sachen heran, eben weil sie schon wissen
> wo die neuralgischen Punkte liegen, wie die zu behandeln sind und (auch
> wichtig) wie sie zu testen sind.

So sollte es jedenfalls sein.

Nur ist ein Profi im Wortsinn einfach nur jemand, der das aus 
beruflichen Gründen macht. Ein grosser Teil der auf dem Markt 
verfügbaren Software wird gewerblich erstellt, von Leuten die davon 
leben. Und trotzdem finden sich da eine Unzahl von eigentlich 
vermeidbaren Fehlern, einschliesslich der berüchtigten buffer overflows.

von (prx) A. K. (prx)


Lesenswert?

cyblord ---- schrieb:
> Nur kann man das nicht mit einer besseren Programmiersprache alleine
> erreichen.

Natürlich nicht. Aber den einen einfachen Schlüssel zur Perfektionierung 
der Welt gibts nicht. Die Sprache ist nur ein kleiner Teil.

> In dem Moment wo du das dynamische anlegen eines Arrays zur
> Laufzeit hast, kannst du solche Dinge wie Überlauf usw. nur durch das
> Laufzeitsystem abfangen und das erkaufst du durch massig overhead.

Das "massig" würde ich nicht unterschreiben, aber Overhead ist da. Ist 
halt die Frage, was man will. So schnell wie möglich, oder so gut wie 
möglich.

> Für die Systemprogrammierung, BS-Programmierung oder für kleine
> Controller ist das einfach nicht praktikabel.

Schrott rein Schrott raus? Sys/OS-Programmierung besteht streckenweise 
aus mehr Checks als produktivem Code. Oder sollte es zumindest. Und die 
Stellen, wo das nicht der Fall ist, tauchen über kurz oder lang in den 
Fixreports auf (unchecked parameter => exploit).

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:

> Nur ist ein Profi im Wortsinn einfach nur jemand, der das aus
> beruflichen Gründen macht. Ein grosser Teil der auf dem Markt
> verfügbaren Software wird gewerblich erstellt, von Leuten die davon
> leben. Und trotzdem finden sich da eine Unzahl von eigentlich
> vermeidbaren Fehlern, einschliesslich der berüchtigten buffer overflows.

LOL.
Ich kann es drehen und wenden wie ich will. Ich muss dir zustimmen.

Weil es gerade ein wenig hier her passt, auch wenn es im eigentlichen 
Sinne nichts mit Pointern zu tun hatte - oder doch? Ja doch ein bischen 
schon.
Vor 3 Wochen hatten wir hier einen Bug zu fixen, in einer Funktion die 
gaaaanz tief unten sitzt. Wir haben uns den Spass gemacht, die Funktion 
in den Source Code Verwaltungen zurückzuverfolgen. Er war von Anfang an 
drinnen. Die Funktion ist datiert auf die Jahreszahl 1987. :-)
Das war auch für mich neuer Rekord. Mein persönlicher Rekord liegt bei 
ca. 15 Jahren alten Bugs, die über diesen langen Zeitraum unentdeckt 
blieben. Allerdings ist von diesem Code so gut wie nichts mehr im 
Produktionseinsatz.

: Bearbeitet durch User
von michi42 (Gast)


Lesenswert?

ja ja mit Pointern kann man ganze Systeme in Schutt und Asche legen.
Deshalb wird auch in den Sicherheitsbehörden davor gewarnt.
https://netzpolitik.org/2010/terrorismusexperte-warnt-vor-c/
SCNR :-)

von Axel S. (a-za-z0-9)


Lesenswert?

Was ich an der ganzen Diskussion putzig finde: sie kratzt nur an der 
Oberfläche der möglichen Probleme mit Pointern.

OK, der TE hat eigentlich gar kein Pointerproblem. Sondern ein massives 
Verständnisproblem was Arrays in C sind und wie sie an Funktionen 
übergeben werden. Schwamm drüber.

Pointer werden erst wirklich haarig, wenn dynamisch allozierter Speicher 
ins Spiel kommt. Da geht es dann um Fragen wie "Soll der caller den 
Speicher reservieren oder der callee?". Und noch wichtiger: "wer soll 
den Speicher wieder freigeben und wann?". Sobald man die Domäne der 
Kleinst-µC verläßt kommt noch die Frage der Reentrance 
(Threadsicherheit) dazu, was static oder globale Variablen rausschmeißt.

Und an der Stelle steht man mit C dann wirklich schlecht da. Und wünscht 
sich C++ mit Konstruktoren und Destruktoren (die auch verläßlich 
aufgerufen werden, nicht wie bei Java). Oder try() ... finish() Blöcke. 
Oder Smartpointer.

In C++ habe ich mir nach vielen schlechten Erfahrungen angewöhnt, keine 
"nackten" Pointer mehr im Anwendungscode zu haben. Alle Pointer müssen 
entweder in Objekte gekapselt sein oder Smartpointer.

Zum Glück entfallen in C++ auch viele Gründe für Pointer, weil man 
echtes call by reference und vor allem auch return-by-reference hat.


XL

von Cyblord -. (cyblord)


Lesenswert?

A. K. schrieb:

> Schrott rein Schrott raus? Sys/OS-Programmierung besteht streckenweise
> aus mehr Checks als produktivem Code. Oder sollte es zumindest. Und die
> Stellen, wo das nicht der Fall ist, tauchen über kurz oder lang in den
> Fixreports auf (unchecked parameter => exploit).

Ja sicher, aber sie werden trotzdem mit einer "unsicheren" Sprache 
entwickelt. Was nicht ausschließt dass man darin dann checks einbaut.

von Lutz H. (luhe)


Lesenswert?

Karl Heinz schrieb:
> Anstatt das Problem zu
> vereinfachen, verkomplizierst du es noch weiter.

Danke für die anschauliche Erläuterung zu Pointern,
so toll zusammengefasst habe ich es noch nicht gesehen.
Ich glaub ich werde mir den Beitrag unters Kopfkissen legen.
Gruß Lutz

von Guest (Gast)


Lesenswert?

Karl Heinz schrieb:
> Er kann erst mal nicht NULL sein, soviel steht schon mal fest. Aber
> dann? Wie erkennst du, ob ein Nicht-NULL Pointer gültig ist?
> Die Antwort ist: ohne massiven Zusatzaufwand - gar nicht.

Wieso das? ;-)

Es gibt Decives mit RAM auf 0, z.B. M16C usw..
Dort könnten Daten liegen, auf die ein Pointer zeigt und schon hat der 
Pointer den Wert 0 (NULL), zeigt aber auf gültige Daten.

Fies was? ;-)

Deswegen lasse ich bei solchen Devices das RAM bei 4 anfangen, um das 
Problem zu umgehen.

von (prx) A. K. (prx)


Lesenswert?

Guest schrieb:
> Es gibt Decives mit RAM auf 0, z.B. M16C usw..#

Ich bin auch schon 16- und 32-Bittern begegnet, mit einem Adressraum von 
-0x8000[0000] bis +0x7FFF[FFFF]. Die I/O fing unten an.

> Dort könnten Daten liegen, auf die ein Pointer zeigt und schon hat der
> Pointer den Wert 0 (NULL), zeigt aber auf gültige Daten.

Die Adresse 0 lag genau mitten drin. Das ist dann schon schwerer zu 
vermeiden, insbesondere bei den 16-Bittern mit 64KB Adressraum.

Diese schrägen Vögel eignen sich auch ganz gut als Demonstration, dass 
die übliche und oft implizit getroffene Annahme, Adressen seien stets 
vorzeichenlos, unzutreffend sein kann.

: Bearbeitet durch User
von cybmorg (Gast)


Lesenswert?

Axel Schwenke schrieb:
> OK, der TE hat eigentlich gar kein Pointerproblem. Sondern ein massives
> Verständnisproblem was Arrays in C sind und wie sie an Funktionen
> übergeben werden. Schwamm drüber.

Zu viele versuchen programmieren zu lernen, ohne zu verstehen, wie ein 
Computer funktioniert. Und wenn man nicht weiss, was Speicher und 
Adressen sind, wie Daten im Speicher organisiert sind, dann versteht man 
natuerlich auch keine Pointer.

> Pointer werden erst wirklich haarig, wenn dynamisch allozierter Speicher
> ins Spiel kommt. Da geht es dann um Fragen wie "Soll der caller den
> Speicher reservieren oder der callee?".

Alles eine Frage der Organisation. Ein ordentlich strukturiertes 
Programm hat damit keine haarigen Probleme. Ein nicht ordentlich 
strukturiertes Programm hat auch mit Hilfsmittlen wie Smartpointern und 
Garbage Collection Probleme. Die treten dann eben in anderer Form auf. 
Aber vor Allem treten sie oft erst viel spaeter im Entwicklungszyklus 
auf - weswegen die vorher genannten Hilfsmittel scheinbar die 
Entwicklung erleichtern, de fakto aber nur den Zeitpunkt verschieben, an 
dem ein Projekt scheitert. Gut, bei so manchem Projekt scheint das 
ausreichend zu sein...

von Klaus F. (kfalser)


Lesenswert?

A. K. schrieb:
> Diese schrägen Vögel eignen sich auch ganz gut als Demonstration, dass
> die übliche und oft implizit getroffene Annahme, Adressen seien stets
> vorzeichenlos, unzutreffend sein kann.

Naja, man findet immer alles wenn man sucht.
Da ich aber weiss, dass ich meinen Code NIEMALS auf so abstruse und 
exotische Architekturen portieren werde, kann ich mit den traditionellen 
Annahme gut leben und mein Programm wird eventuell sicherer, wenn ich 
bestimmte Überprüfungen mache, auch wenn diese nur 99.9% der 
Architekturen richtig sind.

Guest schrieb:
> Es gibt Decives mit RAM auf 0, z.B. M16C usw..
> Dort könnten Daten liegen, auf die ein Pointer zeigt und schon hat der
> Pointer den Wert 0 (NULL), zeigt aber auf gültige Daten.

Alle Prozessoren unterstützen die Adresse 0, aber dort liegt kein freier 
Speicher, wo Variablen abgelegt werden.
Ein kurzer Blick aus Datenblatt des M16 zeigt, dass an Adresse 0 die 
Special Function Registers (SFR) liegen, das ist keine RAM.
Und die meisten Prozessoren haben an Adresse 0 die ISR Tabelle, die 
möchte man mit einem NULL-Pointer nicht überschreiben :-)

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

cybmorg schrieb:
> Zu viele versuchen programmieren zu lernen, ohne zu verstehen, wie ein
> Computer funktioniert. Und wenn man nicht weiss, was Speicher und
> Adressen sind, wie Daten im Speicher organisiert sind, dann versteht man
> natuerlich auch keine Pointer.

In der Tat. Die Junks machen dann "Java".

Pointer sind das natürlichste der Welt wenn man es mal aus Sicht
des Prozessors sieht ...

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


Lesenswert?

Klaus Falser schrieb:
> Da ich aber weiss, dass ich meinen Code NIEMALS auf so abstruse und
> exotische Architekturen portieren werde,

Ich hatte einen C-Compiler dafür gebaut. ;-)

: Bearbeitet durch User
von Andreas S. (schunki)


Lesenswert?

cybmorg schrieb:
> Zu viele versuchen programmieren zu lernen, ohne zu verstehen, wie ein
> Computer funktioniert. Und wenn man nicht weiss, was Speicher und
> Adressen sind, wie Daten im Speicher organisiert sind, dann versteht man
> natuerlich auch keine Pointer.

Axel Schwenke schrieb:
> OK, der TE hat eigentlich gar kein Pointerproblem. Sondern ein massives
> Verständnisproblem was Arrays in C sind und wie sie an Funktionen
> übergeben werden.

Der TE dachte, dass man diese Forum dazu nutzen kann um evtl. 
"Verständnisprobleme" zu beseitigen und Antworten auf eine, für andere 
vielleicht einfache, Frage zu erhalten. Ich kann es natürlich nicht 
nachweisen, aber ich bin mir recht sicher, dass auch Euch das Wissen 
über C nicht mit in die Wiege gelegt wurde und auch Ihr zu Euren 
Anfangen in C bestimmt einige Verständislücken hattet!

Aber trotzdem Danke für die ausführliche Diskussion zu meinem Problem!


Gruß
Andreas

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

Klaus Falser schrieb:
> Alle Prozessoren unterstützen die Adresse 0, aber dort liegt kein freier
> Speicher, wo Variablen abgelegt werden.

Doch das gibt's ;-)

> Ein kurzer Blick aus Datenblatt des M16 zeigt, dass an Adresse 0 die
> Special Function Registers (SFR) liegen, das ist keine RAM.
> Und die meisten Prozessoren haben an Adresse 0 die ISR Tabelle, die
> möchte man mit einem NULL-Pointer nicht überschreiben :-)

Hast natürlich Recht, mein Fehler, es ist nicht der M16C sondern RX, der 
hat on-chip RAM auf 0x00.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Andreas S. schrieb:
> aber ich bin mir recht sicher, dass auch Euch das Wissen
> über C nicht mit in die Wiege gelegt wurde

Die ersten 2 Wochen mit C ist mein PC mehrfach fast aus dem
Fenster geflogen ... Mach Dir keinen Kopf.

Stelle Dir mal einen Prozessor vor, der ein Byte aus dem Speicher
holen will. Im Adressregister ist die RAM-Adresse, sie "zeigt" auf
das Byte. Jetzt stell Dir vor, hinter dem Byte steht noch etwas von
Interesse, dann inkrementierst Du diesen Zeiger und hast es.
Ganz egal, ob das ein Array, ein String oder eine Struktur ist.

von Andreas S. (schunki)


Lesenswert?

Joachim Drechsel schrieb:
> Die ersten 2 Wochen mit C ist mein PC mehrfach fast aus dem
> Fenster geflogen ... Mach Dir keinen Kopf.

Ne, den mach ich mir auch nicht! Mein letzter Beitrag war auch weniger 
als Verzweiflungs-Beitrag zu verstehen, sondern war nur auf die meiner 
Meinung nach teilweise etwas überheblichen Posts bezogen.

Mittlerweile hab ich ja mein ursprüngliches Problem auch gelöst!

Und für alle, die evtl. auch noch ein wenig mit Pointern kämpfen. Hier 
mal ein echt gut gemachter Beitrag, der mir zu der Thematik gut geholfen 
hat.

http://wwwuser.gwdg.de/~kboehm/ebook/18_kap14_w6.html

von Mark B. (markbrandis)


Lesenswert?

Andreas S. schrieb:
> Warum ich Pointer für immer hassen werde

Ernst gemeinte Frage: Wer zwingt Dich dazu, eine Programmiersprache mit 
direktem Zugriff auf Pointer / Pointerarithmentik zu verwenden?

Es gibt so viele Alternativen: Java, Python, C#, Ruby, JavaScript, 
Pascal, ...

von Andreas S. (schunki)


Lesenswert?

Mark Brandis schrieb:
> Ernst gemeinte Frage: Wer zwingt Dich dazu, eine Programmiersprache mit
> direktem Zugriff auf Pointer / Pointerarithmentik zu verwenden?
>
> Es gibt so viele Alternativen: Java, Python, C#, Ruby, JavaScript,
> Pascal, ...

Ernst gemeinte Antwort: Dieses Forum nennt sich doch 
MIKROCONTROLLER.net, oder? Ich programmiere hier einen Mikrocontroller. 
Bis jetzt ist mir da aber noch keiner untergekommen, der mit Java, 
Python oder einer anderen der von Dir genannten Sprachen programmiert 
wird.

von Mark B. (markbrandis)


Lesenswert?

Andreas S. schrieb:
> Mark Brandis schrieb:
>> Ernst gemeinte Frage: Wer zwingt Dich dazu, eine Programmiersprache mit
>> direktem Zugriff auf Pointer / Pointerarithmentik zu verwenden?
>>
>> Es gibt so viele Alternativen: Java, Python, C#, Ruby, JavaScript,
>> Pascal, ...
>
> Ernst gemeinte Antwort: Dieses Forum nennt sich doch
> MIKROCONTROLLER.net, oder?

Und weiter? Hier werden auch DSPs, PCs und Omas alte Drehorgel 
programmiert.

> Ich programmiere hier einen Mikrocontroller.

Das weiß exakt keine Sau, weil du es nirgends erwähnt hast.

> Bis jetzt ist mir da aber noch keiner untergekommen, der mit Java,
> Python oder einer anderen der von Dir genannten Sprachen programmiert
> wird.

Selbstverständlich gibt es das.

Beispiele:
Java
http://www.elektronikpraxis.vogel.de/softwareengineering/programmiersprachen/articles/402653/

C#
https://www.dotnetpro.de/articles/onlinearticle2794.aspx

Python (PDF-Datei)
http://elk.informatik.fh-augsburg.de/da/da-49/da-thoms-cc.pdf

JavaScript
http://www.heise.de/hardware-hacks/meldung/JavaScript-fuer-Mikrocontroller-1934018.html

: Bearbeitet durch User
von Andreas S. (schunki)


Lesenswert?

Mark Brandis schrieb:
> Das weiß exakt keine Sau, weil du es nirgends erwähnt hast.

Sorry, aber ich dachte eigentlich, dass es ausreichend ist wenn ich 
meinen Beitrag im Unterpunkt "µC & Elektronik" verfasse!

Dabei kann ich jetzt nicht dafür, dass ein Admin meinen Beitrag ins 
Forum "GCC" verschoben hat, wie ich jetzt gerade festgestellt habe!

Mark Brandis schrieb:
> Selbstverständlich gibt es das.

Welche da wären????

von frank (Gast)


Lesenswert?

>Wo immer du das auch her hast: VERGISS ES.

naja, du magst recht haben. aber die sprache selbst nutzt die beiden zum 
verwechseln aehnlich.

array[4] == *array+4

von Peter II (Gast)


Lesenswert?

frank schrieb:
> array[4] == *array+4

das sollte aber nicht gleich sein ...

von (prx) A. K. (prx)


Lesenswert?

frank schrieb:
> array[4] == *array+4

Mit einem Klammerpärchen wird ein Schuh draus, also
  4[array] == *(4+array)

von Simon K. (simon) Benutzerseite


Lesenswert?

NULL muss ja nicht mit 0 definiert sein. Sondern einfach nur mit einer 
beliebigen ungültigen Adresse.

von Peter II (Gast)


Lesenswert?

Simon K. schrieb:
> NULL muss ja nicht mit 0 definiert sein. Sondern einfach nur mit einer
> beliebigen ungültigen Adresse.

es prüft aber kaum jemand auf NULL sondern schreibt.

if (!ptr) {
  ....
}

von Mark B. (markbrandis)


Lesenswert?

Andreas S. schrieb:
> Sorry, aber ich dachte eigentlich, dass es ausreichend ist wenn ich
> meinen Beitrag im Unterpunkt "µC & Elektronik" verfasse!
>
> Dabei kann ich jetzt nicht dafür, dass ein Admin meinen Beitrag ins
> Forum "GCC" verschoben hat, wie ich jetzt gerade festgestellt habe!

Ah, okay. Man erkennt es dem Thread halt nicht an, dass er woanders 
entstanden ist.

> Mark Brandis schrieb:
>> Selbstverständlich gibt es das.
>
> Welche da wären????

Habe meinen Beitrag oben editiert.

von Dr. Sommer (Gast)


Lesenswert?

Simon K. schrieb:
> NULL muss ja nicht mit 0 definiert sein. Sondern einfach nur mit einer
> beliebigen ungültigen Adresse.
Fast; NULL ist als 0 (Integer) definiert, aber wenn man 0 (Integer) 
einem Pointer zuweist wird das zu einem plattformabhängigem Bitmuster 
konvertiert, das irgendwie eine ungültige Adresse angibt. Das können 
0-bits sein, muss aber nicht.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:
> es prüft aber kaum jemand auf NULL sondern schreibt.

Das macht nichts, zumal in C++ 0 die Rolle von NULL übernimmt. Nur muss 
jene Adresse, die intern für NULL verwendet wird, nicht den Wert 0 
haben. Der Compiler erkennt den Operanden als Pointer und wird das 
Statement
  if (ptr) ...
beispielsweise in den Code
  compare R1 to 0xDEADBEEF and branch if equal
übersetzen.

In einem solchen Fall muss bei
  char *p = NULL;
  return (ptrsize_t)p;
auch nicht das gleiche rauskommen wie bei
  return *(ptrsize_t)&p;

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


Lesenswert?

Andreas S. schrieb:
> Sorry, aber ich dachte eigentlich, dass es ausreichend ist wenn ich
> meinen Beitrag im Unterpunkt "µC & Elektronik" verfasse!

Der Begriff „Controller“ ist trotzdem ziemlich weitreichend, und es
gibt in der Tat auch welche, die man in anderen Sprachen als C oder
C++ (oder Assembler) programmieren kann.  Etwas weiter gefasst, ist
auch ein ARM9 ein „Controller“, wie man ihn beispielsweise auf dem
berüchtigten Raspberry Pi findet, und den wiederum kann man deutlich
PC-ähnlicher programmieren als vielleicht einen ATtiny44.

Solange du nicht im Eröffnungsposting irgendwie erwähnst, um welche
Größenordnung von Prozessor/Controller es sich handelt, kann man da
nur mutmaßen.

von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Das macht nichts, zumal in C++ 0 die Rolle von NULL übernimmt. Nur muss
> jene Adresse, die intern für NULL verwendet wird, nicht den Wert 0
> haben.
Nicht ganz, siehe oben - 0, NULL, und nullptr sind in C++ fast beliebig 
austauschbar, wobei nullptr zu bevorzugen ist. Auf jeder Plattform 
gilt immer, dass (int) 0, (int) nullptr, (int) NULL, gleich sind. Die 
Konvertierung in das plattformspezifische bitmuster erfolgt erst beim 
Konvertieren in einen Pointer-Typ. Das ganze funktioniert auch 
umgekehrt, ein Nullpointer (der nicht notwenigerweise aus 0-bits 
besteht) muss, wenn in einen integer konvertiert, 0 ergeben. Deswegen 
funktioniert if(pointer) auch.

A. K. schrieb:
> auch nicht das gleiche rauskommen wie bei
Richtig!, aber was ist ptrsize_t, meinst du uintptr_t?!?!

von (prx) A. K. (prx)


Lesenswert?

Dr. Sommer schrieb:
> Richtig!, aber was ist ptrsize_t, meinst du uintptr_t?!?!

Frag mich nicht ob/wo das festgelegt ist, aber dem Typ ptrsize_t bin ich 
schon oft begegnet. Als jenem Integer-Typ, der in Grösse und 
Wertebereich zu einem Pointer äquivalent ist.

Der Typ uintptr_t ist mir hingegen unbekannt, zumal ja nicht einmal klar 
ist, ob ein Pointer vorzeichenlos ist.

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


Lesenswert?

Dr. Sommer schrieb:
> Nicht ganz, siehe oben

Ich schrieb nicht rein zufällig "intern". Intern im Compiler. Daher 
drückst du das gleiche aus wie ich.

von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Frag mich nicht ob/wo das festgelegt ist, aber dem Typ ptrsize_t bin ich
> schon oft begegnet.
http://en.cppreference.com/w/c/types/integer Hier steht was von 
(u)intptr_t, aber nichts von ptrsize_t - Google liefert zu letzterem 
ganze 215 Ergebnisse.
Die signedness von Pointern hat ja nichts damit zu tun was (u)intptr_t 
ist - (u)intptr_t ist einfach nur ein (un)signed integer mit der selben 
größe eines Pointers. Ob man signed haben will muss man selber 
entscheiden, oder ggf. std::is_signed<void*>::value verwenden...

von Yalu X. (yalu) (Moderator)


Lesenswert?

ptrsize_t ist mir auch noch nicht über den Weg gelaufen. Wenn man danach 
googelt, findet man ein paar Beispiele, wo sich die Leute diesen Typ 
selber definiert haben.

Im C99-Standard gibt es intptr_t und uintptr_t, aber kein ptrsize_t.

von cppler (Gast)


Lesenswert?

grinsel
Wenn man in C++ und JAVA konsequent
1
try ... catch ...
 anwendet gibt es weder eine NULLPOINTEREXCEPTION in JAVA noch einen 
Programmcrash in C++.
Allerdings stimmt es schon das die "Profis" seeeehr nachgelassen haben, 
denn wenn man z.B. einen BigBlue Bandsicherungsserver startet kommen als 
erstes so gefühlt eine Millionen JAVA Nullpointerexceptions m(
Das kann man aber sehr leicht nachvollziehen, weil die Inder in den 
Anzügen 1:1 Kopien von JAVA-Büchern erstellen und diese immer wieder 
kopieren statt z.B. zu vererben oder auch immer wieder der Klassiker man 
erstellt einen Stub und vergißt den dann in der eigentlichen Klasse ...
Der Vorteil von JAVA ist halt das man damit nicht voll auf die Nase 
fällt und auch grobe Schnitzer von der VM abgefangen werden, der 
Nachteil ist halt das genau das passiert :-P
Wie war das nochmal in PASCAL "The compiler wont let you shoot in your 
foot" ;-)
C ist nunmal nicht sooo schön geworden wie andere Sprachen, hat sich 
aber weitgehend durchgesetzt.
Wenn sich nun die C++ Compiler auf Embedded komplett durchsetzen würden 
wäre da schonmal viel gewonnen.

von Peter II (Gast)


Lesenswert?

cppler schrieb:
> Wenn man in C++ und JAVA konsequenttry ... catch ... anwendet gibt es
> weder eine NULLPOINTEREXCEPTION in JAVA noch einen
> Programmcrash in C++.

das ich nicht lache.

Wirf doch mal eine exception in einen destructor. Da hilft dir ein try 
catch nichts mehr.
Und auch wenn man seinen eigenem Programmspeicher wild überschreibt, 
kann man das auch nicht mehr abfangen.

von root (Gast)


Lesenswert?

lutz h. schrieb:
> Es soll kein Programm mit Pointern existieren, dass stabil läuft.
> Deshalb werden solche Programme als unsicherer Code bezeichnet.

auf Assembler/Maschinenebene sind Adressen(=Pointer) Daten.
Und jeder Compiler transformiert nur die menschliche Abstraktion
auf Maschinenebene herunter. Und sei's Haskell, in dessen
Programmiermodel es nicht einmal Variablen gibt ;-)

Zum Topic:
ich bin verdammt froh C und C++ nicht aus den Internetforen gelernt
zu haben. Ok auf newsgroup war ich eine Weile ...
Gruß an Kalr Heinz und Rolf Magnus ;-)

compiler linearisieren mehrdim. Felder oft durch folgende Transformation

int x[N][M][T];
int * px = &x[0][0][0];
&x[n][m][t] == (px + n*M*T + m*T + t); // wahr

glaube nicht das diese Transformation durch Standard abgedeckt ist.
Aber man kann sich damit vieles erklären. Z.B auch warum erste Dimension
bei Übergabe als Funktionparameter unwichtig ist. Siehe oben - N wird
nicht benutzt. Eigentlich definiert (erzwingt) Standard dadurch
implizit die obige Linearisierung.

Ich gebe Karl Heinz recht, dass das Gedankenmodell besser umgesetzt 
wäre,
wenn man auf Vektoren umstiege. Da zahlt sich das Nachdenken aus.

Gruß,
Daniel

von (prx) A. K. (prx)


Lesenswert?

root schrieb:
> &x[n][m][t] == (px + n*M*T + m*T + t); // wahr
>
> glaube nicht das diese Transformation durch Standard abgedeckt ist.

Doch. C definiert x[n] als *(x+n), intern gerechnet als *(x+E*n) wenn x 
ein Pointer/Array und n eine Integer ist, wobei E die Grösse des 
Elements ist, hier also int[M][T]. Wenn du das noch zweimal anwendest 
kommt genau deine Formel raus.

Kurioser Nebeneffekt dieser Definition ist, dass die Indizierung ebenso 
kommutativ ist wie die Addition, also kein Unterschied zwischen x[n] und 
n[x] besteht.

: Bearbeitet durch User
von Andreas S. (schunki)


Lesenswert?

Jörg Wunsch schrieb:
> Solange du nicht im Eröffnungsposting irgendwie erwähnst, um welche
> Größenordnung von Prozessor/Controller es sich handelt, kann man da
> nur mutmaßen.

Ok, da hast Du natürlich recht! Aber in meinem eigentlichen Post war die 
Plattform auch eher unwichtig. Da ging es ja rein um 
programmiertechnische Dinge.

von Daniel -. (root)


Lesenswert?

A. K. schrieb:
> Kurioser Nebeneffekt dieser Definition ist, dass die Indizierung ebenso
> kommutativ ist wie die Addition, also kein Unterschied zwischen x[n] und
> n[x] besteht.

Wenn jemand diese Schreibweise konsequent verwenden würde,
den würde die C-Gemeinde vermutlich schnell lynchen :)

Offtopic:
Zeiger werfen oft Steine in das Zahnrad des Optimierers,
der hasst die Zeiger bestimmt :)

von cppler (Gast)


Lesenswert?

Peter II schrieb:
> cppler schrieb:
>> Wenn man in C++ und JAVA konsequenttry ... catch ... anwendet gibt es
>> weder eine NULLPOINTEREXCEPTION in JAVA noch einen
>> Programmcrash in C++.
>
> das ich nicht lache.
>
> Wirf doch mal eine exception in einen destructor. Da hilft dir ein try
> catch nichts mehr.
> Und auch wenn man seinen eigenem Programmspeicher wild überschreibt,
> kann man das auch nicht mehr abfangen.

Tja nun man muß halt immer wissen was man tut aber mir fällt gerade 
keine try/catch Variante in einem Constructor ein, könntest Du mich 
erhellen ?
:-P

von Axel S. (a-za-z0-9)


Lesenswert?

Andreas S. schrieb:
> Axel Schwenke schrieb:

>> OK, der TE hat eigentlich gar kein Pointerproblem. Sondern ein massives
>> Verständnisproblem was Arrays in C sind und wie sie an Funktionen
>> übergeben werden.

> Ich kann es natürlich nicht
> nachweisen, aber ich bin mir recht sicher, dass auch Euch das Wissen
> über C nicht mit in die Wiege gelegt wurde und auch Ihr zu Euren
> Anfangen in C bestimmt einige Verständislücken hattet!

Das wurde es ganz sicher nicht.

Es macht aber einen großen Unterschied ob man etwas nicht weiß und dann 
halt zusieht, es zu lernen.

Oder ob man mit einer gehörigen Portion Halbwissen "Arrays sind doch 
wohl Pointer, oder?" und einer provokanten These "ich werde Pointer 
immer hassen" in ein Forum platzt. Noch dazu eins, wo man steeng 
genommen offtopic ist. Pointer sind schließlich kein bisschen 
µC-spezifisch, sondern ganz normales C-Handwerkszeug.


XL

von Andreas S. (schunki)


Lesenswert?

Axel Schwenke schrieb:
> Oder ob man mit einer gehörigen Portion Halbwissen "Arrays sind doch
> wohl Pointer, oder?" und einer provokanten These "ich werde Pointer
> immer hassen" in ein Forum platzt. Noch dazu eins, wo man steeng
> genommen offtopic ist. Pointer sind schließlich kein bisschen
> µC-spezifisch, sondern ganz normales C-Handwerkszeug.

Zu Deinen ersten Anführungszeichen sag ich mal, wenn Du mich zitierst, 
dann auch bitte richtig und auch nicht aus dem Kontext gerissen! Zu 
Deinen zweiten Anführungszeichen - Eine These und eine Meinung zu einer 
bestimmten Sache sind zwei völlig unterschiedliche Paar Schuhe!!!!!

Und zum Abschluss ist es noch lange kein Halbwissen wenn etwas nicht 
oder falsch verstanden ist. Wenn dieser Umstand als Halbwissen gewertet 
wird, dann wirst Du dies wohl so gut wie jedem hier unterstellen können, 
weil hier bestimmt jeder irgendwo die ein oder andere Wissenslücke hat!

von Lutz H. (luhe)


Lesenswert?

Andreas S. schrieb:
> weil hier bestimmt jeder irgendwo die ein oder andere Wissenslücke hat

Wer es nicht glaubt und mehr als drei Jahre die Fahrprüfung hat, kann 
mal heimlich die theoretische Prüfung machen.

Ich bedanke mich an dieser Stelle bei allen Mitwirkendem im Forum,
ich lerne immer wieder Sachen kennen, mit denen bei der täglichen Arbeit 
wenig zu tun habe, deshalb sehr interessant sind.

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.