Forum: Compiler & IDEs Stern Stern, Bedeutung


von Erdling (Gast)


Lesenswert?

Hier ein Schnipsel von einem DMA-Example für den Raspberry Pi.
Was genau passiert in der ersten Zeile nach dem Kommentar?
1
    //force page into RAM and then lock it there:
2
    ((int*)*virtAddr)[0] = 1;
3
    mlock(*virtAddr, PAGE_SIZE);
4
    memset(*virtAddr, 0, PAGE_SIZE); //zero-fill the page for convenience

von Dirk B. (dirkb2)


Lesenswert?

Die Adresse, die an der Adresse steht auf die virtAddr zeigt, wird als 
Zeiger auf int aufgefasst und dort (wegen der [0]) der Wert 1 abgelegt.

Der * von *virtAddr ist eine Dreferenzierung.
Das (int*) ist ein cast.

Die Definition von virtAddr sollte in der Art sein:

Typ **virtAddr;

von Karl H. (kbuchegg)


Lesenswert?

virtAddr ist ein Pointer, der auf einen Pointer zeigt, in dem steht mit 
welchem Speicher zu arbeiten ist
1
  virtAddr
2
  +--------+
3
  |    o---------------------+
4
  +--------+                 |
5
                             |
6
                             v
7
                        +----------+                  +---------------+
8
                        |      o--------------------->| eigentlicher  |
9
                        +----------+                  | Speicher-     |
10
                                                      | Bereich       |
11
                                                      | für           |
12
                                                      | die Daten     |
13
                                                      |               |
14
                                                      +---------------+

es könnte zb sein, dass der mittlere Pointer einfach nur Teil eines 
Arrays ist, indem die Pointer in den Speicher gesammelt werden und 
mittels virtAddr wird einer dieser Pointer ausgewählt.

1
  virtAddr
2
  +--------+                                   +-------------+
3
  |    o------+                          +---->|             |
4
  +--------+  |                          |     |             |
5
              |                          |     |             |
6
              |         +----------+     |     +-------------+
7
              |         |      o---------+
8
              |         +----------+                  +---------------+
9
              +-------->|      o--------------------->| eigentlicher  |
10
                        +----------+                  | Speicher-     |
11
                        |      o-------+              | Bereich       |
12
                        +----------+   |              | für           |
13
                                       |              | die Daten     |
14
                                       |              |               |
15
                                       v              +---------------+
16
                                  +-------------+
17
                                  |             |
18
                                  |             |
19
                                  |             |
20
                                  +-------------+
1
virtAddr
such dir das Rechteck 'virtAdr' in der Zeichnung

1
*virtAddr
verfolge den Pfeil, der davon ausgeht. Du landest bei einem weiteren 
Rechteck

1
(int*)*virtAddr
das was du dort in diesem Rechteck vorfindest, fasse es als Pointer auf 
einen int auf.

1
((int*)*virtAdr)[0] =
verfolge den dort ausgehenden Pointer weiter und schreibe dort wo er 
hinzeigt (wegen Array-Pointer Dualität) etwas rein (wobei du den 
speicher als einen int auffasst)

: Bearbeitet durch User
von Erdling (Gast)


Lesenswert?

Autor: Dirk B. (dirkb2) schrieb:

>Die Definition von virtAddr sollte in der Art sein:
>Typ **virtAddr;
Yep.
1
void makeVirtPhysPage(void** virtAddr, void** physAddr) {
2
    *virtAddr = valloc(PAGE_SIZE); //allocate one page of RAM
3
4
    //force page into RAM and then lock it there:
5
    ((int*)*virtAddr)[0] = 1;
6
    mlock(*virtAddr, PAGE_SIZE);
7
    memset(*virtAddr, 0, PAGE_SIZE); //zero-fill the page for convenience
8
9
    //Magic to determine the physical address for this page:
10
    uint64_t pageInfo;
11
    int file = open("/proc/self/pagemap", 'r');
12
    lseek(file, ((uint32_t)*virtAddr)/PAGE_SIZE*8, SEEK_SET);
13
    read(file, &pageInfo, 8);
14
15
    *physAddr = (void*)(uint32_t)(pageInfo*PAGE_SIZE);
16
    printf("makeVirtPhysPage virtual to phys: %p -> %p\n", *virtAddr, *physAddr);
17
}
18
19
20
21
//der Aufruf in main():
22
    //configure DMA:
23
    //allocate 1 page for the source and 1 page for the destination:
24
    void *virtSrcPage, *physSrcPage;
25
    makeVirtPhysPage(&virtSrcPage, &physSrcPage);

Muß man das wirklich - aus meiner Sicht - so kompliziert gestalten?
Das zu verstehen ist'n zäher Brocken...

von Karl H. (kbuchegg)


Lesenswert?

Erdling schrieb:

> Muß man das wirklich - aus meiner Sicht - so kompliziert gestalten?

Das ist doch nicht kompliziert.

Das ist das ganz normale Ergebnis, das rauskommt, wenn man eine Funktion 
in die Lage versetzen will, beim Aufrufer eine Pointer Variable zu 
verändern.

> Das zu verstehen ist'n zäher Brocken...

Das ist stink normales C. Willkommen in der 2-Stern Programmierung

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Das ist das ganz normale Ergebnis, das rauskommt, wenn man eine Funktion
> in die Lage versetzen will, beim Aufrufer eine Pointer Variable zu
> verändern.

Wie schreibst du das denn, wenn eine Funktion einen int beim Aufrufer 
verändern soll?
Doch wohl so
1
void foo( int * pI )
2
{
3
  *pI = 6;
4
}
5
6
int main()
7
{
8
  int j;
9
10
  foo( &j );   // j kriegt von foo den Wert 6
11
}

Was ist das allgemeine Schema?
1
void foo( T * pointerToVariable )
2
{
3
  * pointerToVariable = Wert für die Variable;
4
}
5
6
int main()
7
{
8
  T variable;
9
10
  foo( &variable );
11
}


und jetzt setzt du für T einfach mal einen Pointer Datentyp ein. Zb. 
einen void *

Wenn in
1
    void *virtSrcPage, *physSrcPage;
2
    makeVirtPhysPage(&virtSrcPage, &physSrcPage);
die Funktion in der Lage sein soll, an virtSrcPage einen neuen Wert 
zuzuweisen (weil die Funktion Speicher allokiert und die Adresse dieses 
allokierten Speichers in virtSrcPage ablegen soll), dann muss die 
Funktion einen Pointer auf die Pointer-Variable virtSrcPage kriegen.
Damit muss logischerweise das Funktionsargument ein void ** sein. Immer 
1 * mehr als der Datentyp der Variablen, die sie verändern können soll.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Erdling schrieb:
> Muß man das wirklich - aus meiner Sicht - so kompliziert gestalten?

Wenn man sich einige Zeit mit Pointern in C intensiv beschäftigt hat, 
ist das überhaupt nicht mehr kompliziert. Den Code (den Du da oben 
gepostet hast) liest sich dann genauso flüssig wie die Tageszeitung am 
Morgen.

Bis dahin ist es aber ein harter weg.

Die doppelten Sternchen sind nur deshalb nötig, damit die Funktion die 
eigentlichen Pointer, die übergeben werden, modifizieren kann. Denk Dir 
erstmal überall ein Sternchen weg. Dann wird es einfacher.

> Das zu verstehen ist'n zäher Brocken...

Das lernt man nicht in ein paar Tagen.... mach Dir nichts draus. Es kann 
auch mal Fälle mit 3 Sternchen hintereinander geben. Dann wirds wirklich 
heftig. ;-)

von Erdling (Gast)


Lesenswert?

Ich glaube dieses vereinfachte Beispiel:

>Was ist das allgemeine Schema?
1
void foo( T * pointerToVariable )
2
{
3
  * pointerToVariable = Wert für die Variable;
4
}
5
6
int main()
7
{
8
  T variable;
9
10
  foo( &variable );
11
}

zusammen mit der Ziel WORUM es überhaupt geht:
>Wenn die Funktion in der Lage sein soll, an virtSrcPage einen neuen Wert
>zuzuweisen (weil die Funktion Speicher allokiert und die Adresse dieses
>allokierten Speichers in virtSrcPage ablegen soll), dann muss die
>Funktion einen Pointer auf die Pointer-Variable virtSrcPage kriegen.

hat mir das Tor zum Verständnis geöffnet. Wenn man es versteht fühlt 
sich der Aha!-Effekt nämlich besonders gut an...

Danke für die Mühe.

von Mark B. (markbrandis)


Lesenswert?

Karl Heinz schrieb:
> virtAddr ist ein Pointer, der auf einen Pointer zeigt, in dem steht mit
> welchem Speicher zu arbeiten ist
>
>
1
> 
2
>   virtAddr
3
>   +--------+
4
>   |    o---------------------+
5
>   +--------+                 |
6
>                              |
7
>                              v
8
>                         +----------+                  +---------------+
9
>                         |      o--------------------->| eigentlicher  |
10
>                         +----------+                  | Speicher-     |
11
>                                                       | Bereich       |
12
>                                                       | für           |
13
>                                                       | die Daten     |
14
>                                                       |               |
15
>                                                       +---------------+
16
>


Hast Du ein ASCII-Malprogramm dafür? Auch haben will :-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Mark Brandis schrieb:
> Hast Du ein ASCII-Malprogramm dafür? Auch haben will :-)

Hat jeder, so ein ASCII-Malprogramm nennt sich Text-Editor. ;-)

von Bitflüsterer (Gast)


Lesenswert?

Frank M. schrieb:
> Mark Brandis schrieb:
>> Hast Du ein ASCII-Malprogramm dafür? Auch haben will :-)
>
> Hat jeder, so ein ASCII-Malprogramm nennt sich Text-Editor. ;-)

Ich halte es zwar für durchaus denkbar, daß Karl Heinz die Bilder von 
Hand im Editor malt, aber für Faulpelze wie mich gibts: AACircuit unter 
http://www.tech-chat.de/download.html

von c-lover (Gast)


Lesenswert?

ich liebe es:
zeiger die auf zeiger zeigen->
 ->die auf zeiger zeigen
 ->die auf zeiger zeigen
 ->die auf zeiger zeigen
 ->die auf zeiger zeigen....
das kenn ich zur genüge. nicht c ist schuld sondern der progger.
erinnert mich sehr stark an "objekt orientierten assembler" (borland?)
alle haben es gefordert.
irgendwann kam dann aus der entwicklung:"wer assembler objektorientiert 
programmiert ist pervers..." und man würde sich über das beispiel einer 
funktionoerden applikaton freuen.
c ist halt das werkzeug, das nicht belehrbare psychotiker zu ihren 
programm-konstrukten (ohne kommentare!) motiviert.
und ich bewundere progger die da "add hoc2 noch einen sinn erkennen (so 
er denn eindeutig ist...)

nur mal so frust ablassend (its my daily work)

von Karl H. (kbuchegg)


Lesenswert?

Bitflüsterer schrieb:
> Frank M. schrieb:
>> Mark Brandis schrieb:
>>> Hast Du ein ASCII-Malprogramm dafür? Auch haben will :-)
>>
>> Hat jeder, so ein ASCII-Malprogramm nennt sich Text-Editor. ;-)
>
> Ich halte es zwar für durchaus denkbar, daß Karl Heinz die Bilder von
> Hand im Editor malt

Tue ich.
Wenn man das Bild im Kopf hat, das entstehen soll, geht das relativ 
schnell.

von Mark B. (markbrandis)


Lesenswert?

Karl Heinz schrieb:
> Bitflüsterer schrieb:
>> Frank M. schrieb:
>>> Mark Brandis schrieb:
>>>> Hast Du ein ASCII-Malprogramm dafür? Auch haben will :-)
>>>
>>> Hat jeder, so ein ASCII-Malprogramm nennt sich Text-Editor. ;-)
>>
>> Ich halte es zwar für durchaus denkbar, daß Karl Heinz die Bilder von
>> Hand im Editor malt
>
> Tue ich.
> Wenn man das Bild im Kopf hat, das entstehen soll, geht das relativ
> schnell.

Äh. Nein? Das Problem ist ja das Einfügen von Elementen, was jedes 
andere Element in der gleichen Zeile nach hinten verschiebt.

Also wenn man das von Anfang an richtig im Kopf hat und perfekt 
hinzimmert - dann meinen Respekt. Das krieg ich so nicht hin.

von Markus F. (mfro)


Lesenswert?

Mark Brandis schrieb:
> Äh. Nein? Das Problem ist ja das Einfügen von Elementen, was jedes
> andere Element in der gleichen Zeile nach hinten verschiebt.

Dafür gibt's - seit den Anfängen der IT - die "Insert"-Taste, die 
zwischen Einfügen und überschreiben umschaltet...

von Mark B. (markbrandis)


Lesenswert?

Markus F. schrieb:
> Dafür gibt's - seit den Anfängen der IT - die "Insert"-Taste, die
> zwischen Einfügen und überschreiben umschaltet...

Ach wer benutzt die schon ;-)

von Rolf Magnus (Gast)


Lesenswert?

Markus F. schrieb:
> Dafür gibt's - seit den Anfängen der IT - die "Insert"-Taste, die
> zwischen Einfügen und überschreiben umschaltet...

... aber leider in der Regel das Entfernen nicht entsprechend mit 
umschaltet. Also ich tippe irgendwo ein Zeichen rein, und alles danach 
bleibt, wie es ist und verschiebt sich nicht. Ich lösche das Zeichen 
wieder, weil an der falschen Stelle, und alles verschiebt sich. Ich will 
dafür ein Leerzeichen einfügen, was aber stattdessen das nächste Zeichen 
auch noch mit was falschem überschreibt, weil ich erst wieder auf 
einfügen hätte umschalten müssen. Das hat mich schon einiges an Nerven 
gekostet. ;-)

c-lover schrieb:
> ich liebe es:
> zeiger die auf zeiger zeigen->
>  ->die auf zeiger zeigen
>  ->die auf zeiger zeigen
>  ->die auf zeiger zeigen
>  ->die auf zeiger zeigen....
> das kenn ich zur genüge. nicht c ist schuld sondern der progger.

Wie würdest du das denn machen? Wenn man aus eine Funktion etwas über 
einen Parameter rausgeben will, macht man einen Zeiger darauf. Wenn das, 
was man rausgibt, auch schon ein Zeiger ist, ergibt sich logischerweise 
ein Zeiger auf einen Zeiger.

> c ist halt das werkzeug, das nicht belehrbare psychotiker zu ihren
> programm-konstrukten (ohne kommentare!) motiviert.

Wer sich "progger" nennt, und die Großmachtaste nicht findet, sollte 
nicht so große Töne spucken.

von rmu (Gast)


Lesenswert?

c-lover schrieb:
> ich liebe es:
> zeiger die auf zeiger zeigen->
>  ->die auf zeiger zeigen
>  ->die auf zeiger zeigen
>  ->die auf zeiger zeigen
>  ->die auf zeiger zeigen....
> das kenn ich zur genüge. nicht c ist schuld sondern der progger.

Sowas kommt doch dauernd vor, verkettete Liste z.B.

Verkehrt in C ist die Syntax, wie man Variablen, vor allem Pointer, 
deklariert (Zeiger auf Funktion, die einen Zeiger auf ein Array aus 
Funktionszeigern mit selber Signatur returniert?).

Hirnrissig wirds dann beim Derefenzieren von (mehreren) Pointern, das 
schreibt sich in Pascal z.B. viel verständlicher. Das haben auch die 
Schöpfer der Go-Sprache erkannt (die teilweise ja auch C aus den Windeln 
geholfen haben), und dort die Syntax repariert: 
http://blog.golang.org/gos-declaration-syntax

Bei Exzessivem *-Gebrauch wird man von bösen Zungen auch Zweistern- oder 
Dreistern-Programmierer geschimpft 
(http://c2.com/cgi/wiki?ThreeStarProgrammer).

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

rmu schrieb:
> Verkehrt in C ist die Syntax

Vielleicht nicht "verkehrt", sondern Dir unverständlich?

von rmu (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> rmu schrieb:
>> Verkehrt in C ist die Syntax
>
> Vielleicht nicht "verkehrt", sondern Dir unverständlich?

Unverständlich nicht, aber trotzdem verkehrt, wie in "die Reihenfolge 
stimmt nicht", auf mehreren Ebenen.

Intuitiv wäre m.E. das pascal-artige "lass p einen Zeiger auf int sein" 
und nicht das C-artige "lass int den Typ sein, der beim Derefenzieren 
von p herauskommt".

In manchen Coding Conventions wird "verboten", sowas wie
1
int *p, i;
zu schreiben, sondern verlangen
1
int* p; // pointer: stern beim Typ, eigene Zeile notwendig
2
int i;
mit dem Stern (oder in C++ auch das & für Referenz) beim Typ.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

rmu schrieb:
> In manchen Coding Conventions wird "verboten", sowas wie
>
1
> int *p, i;
2
>
> zu schreiben, sondern verlangen
>
1
> int* p; // pointer: stern beim Typ, eigene Zeile notwendig
2
> int i;
3
>
> mit dem Stern (oder in C++ auch das & für Referenz) beim Typ.

Ich schreibe:
1
int * p;

:-P

von Rolf Magnus (Gast)


Lesenswert?

rmu schrieb:
> Sowas kommt doch dauernd vor, verkettete Liste z.B.
>
> Verkehrt in C ist die Syntax, wie man Variablen, vor allem Pointer,
> deklariert (Zeiger auf Funktion, die einen Zeiger auf ein Array aus
> Funktionszeigern mit selber Signatur returniert?).

Paradebeispiel für schwer verständliche Deklarationen ist die 
Standard-Funktion signal():
1
void (*signal(int sig, void (*handler)(int)))(int);

Aber das läßt sich recht leicht entschärfen:
1
typedef void (*sighandler_t)(int); 
2
sighandler_t signal(int sig, sighandler_t handler);

rmu schrieb:
> Intuitiv wäre m.E. das pascal-artige "lass p einen Zeiger auf int sein"
> und nicht das C-artige "lass int den Typ sein, der beim Derefenzieren
> von p herauskommt".

Diese verdrehte Logik bei der Deklaraton in C fand ich auch schon immer 
eher unglücklich. Urspügnlich war das ja geacht, um's intuitiver zu 
machen, aber das ist meines Erachtens nach hinten losgegangen.

von rmu (Gast)


Lesenswert?

Rolf Magnus schrieb:
> rmu schrieb:
>> Intuitiv wäre m.E. das pascal-artige "lass p einen Zeiger auf int sein"
>> und nicht das C-artige "lass int den Typ sein, der beim Derefenzieren
>> von p herauskommt".
>
> Diese verdrehte Logik bei der Deklaraton in C fand ich auch schon immer
> eher unglücklich. Ursprünglich war das ja gedacht, um's intuitiver zu
> machen, aber das ist meines Erachtens nach hinten losgegangen.

Ich denke eher, dass das eine Folge einer Optimierung des Compilers war, 
da sich so der Ausdruck-Parser auch für die Variablen-und 
Typ-Deklarationen missbrauchen lässt. Das ist ja auf einem Rechner 
entstanden, der 64k 18bit-Worte RAM Maximalausbau hatte.

Wenn man mit Funktionszeigern jongliert kann man gar nicht genug 
typedefs verwenden, das hilft definitiv, sonst wirds wie bei der 
signal-decl oben schnell mal unklar, wo man welchen Namen hinschreibt, 
und wie der resultierende Typ dann eigentlich heisst.

Zum Glück wurde der "->" Operator vorgesehen, man stelle sich vor, was 
man sonst für Konvolute aus verklammerten * und . schreiben müsste...

Ansonsten seh ich das recht fatalistisch, man kann nix ändern daran, 
verschwinden wird C auch nicht, und schliesslich gewöhnt man sich 
irgendwann eh an alles... ;-)

Um auch was sinnvolles beizutragen, hier 
http://cm.bell-labs.com/cm/cs/who/dmr/chist.html gibts die Geschichte 
von C aus der Sicht von Dennis M. Ritchie.

von Karl H. (kbuchegg)


Lesenswert?

rmu schrieb:
> Rolf Magnus schrieb:
>
>> Diese verdrehte Logik bei der Deklaraton in C fand ich auch schon immer
>> eher unglücklich. Ursprünglich war das ja gedacht, um's intuitiver zu
>> machen, aber das ist meines Erachtens nach hinten losgegangen.
>
> Ich denke eher, dass das eine Folge einer Optimierung des Compilers war,
> da sich so der Ausdruck-Parser auch für die Variablen-und
> Typ-Deklarationen missbrauchen lässt. Das ist ja auf einem Rechner
> entstanden, der 64k 18bit-Worte RAM Maximalausbau hatte.

Denke ich eher nicht.
Denn bei Casts musst du die Datentypen ohne den Variablennamen an der 
richtigen Stelle parsen.
Casts gabs auch bei K&R C schon.

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.