Forum: PC-Programmierung Unterschied char* a; char *a;


von timo (Gast)


Lesenswert?

Kann mir jemand den Unterschied zwischen
char* a;
char *a;

erklären?

Danke

von Stefan E. (sternst)


Lesenswert?

Gibt keinen.

von Klaus W. (mfgkw)


Lesenswert?

Doch, gibt einen.

Nicht für den Compiler, aber für den menschlichen Leser.

Man ist als unbedarfter Mensch vielleicht geneigt, char* als Typ zu 
sehen (soweit richtig) und deshalb diese beiden zusammenzuschreiben.

Tatsächlich bindet aber das * stärker an den folgenden Namen als an das 
char.
Bei nur einem Variablennamen fällt das noch nicht unangenehm auf, aber 
bei mehreren:
1
char*     p1,
2
          p2;
Damit wird p1 vom Typ char* vereinbart, p2 dagegen vom Typ char!
Gerade als Anfänger fällt man darauf leicht herein und unterstellen, daß 
beide char* wären.
Deshalb kann man kaum zu dieser Schreibweise raten.

So dagegen:
1
char     *p1,
2
          p2,
3
         *p3,
4
          p4;
macht man deutlich, daß p1 und p3 vom Typ char* sind, p2 und p4 dagegen 
vom Typ char (auch wenn die Namen natürlich in einem echten Programm 
fragwürdig wären).

von Johann (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Doch, gibt einen.
>
> Nicht für den Compiler, aber für den menschlichen Leser.

Hallo Klaus:

Du bist hiermit der Gewinner der Woche in der Disziplin Haarespalten.

Gratulation!

von Klaus W. (mfgkw)


Lesenswert?

Ach, stört mich nicht.
In diesem Forum doch schon gar nicht :-)

Im Ernst: bei so einer Gelegenheit darf man doch schon mal erwähnen, daß 
ein syntaktisch korrektes Programm noch nicht alles ist, oder nicht?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Die Schreibweise
1
char* a;

statt, wie bis dahin üblich,
1
char *a;

stammt m.W. vom Herrn Stroustrup. Der Vorschlag war sicher gut gemeint,
hat aber wohl mehr zur Ver- als Entwirrung unter den C++-Anfängern
beigetragen.

von timo (Gast)


Lesenswert?

@ Klaus Wachtler

Danke dass du es genau so erklärt hast, denn ich war fast schon der 
meinung, char* bezieht sich damit auf alles folgende.

Danke :)

von timo (Gast)


Lesenswert?

Ich hätte noch eine Frage:

<Datentyp> **<Name> Zeiger auf Zeiger auf <Datentyp>

Das verstehe ich, da habe ich einen Zeiger, der auf einen anderen zeigen 
kann.



<Datentyp> *<Name>[n] Feld aus n Zeigern auf <Datentyp>

Hier habe ich ein Feld, voll mit Zeigern.


<Datentyp> (*<Name>)[n] Zeiger auf Feld aus n <Datentyp>-
Elementen

Aber das verstehe ich nicht, und weiß nicht wie ich es anwenden soll.

Ich habe einen Zeiger, der auf ein Feld mit n Elementen zeigt.
Nur wie schreibe ich etwas in das Feld hinein?

Danke

von Schuhplattler Sepp, Oberammergau (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Im Ernst: bei so einer Gelegenheit darf man doch schon mal erwähnen, daß
> ein syntaktisch korrektes Programm noch nicht alles ist, oder nicht?

Richtig!
Lesbarkeit ist ein wichtiges Kriterium der Softwarequalität!

von DirkB (Gast)


Lesenswert?

Das
<Datentyp> (*<Name>)[n] ist doch das Selbe wie
<Datentyp>  *<Name>[n]
da der * an den Namen gebunden ist.

Der Zeiger auf Feld aus n <Datentyp>-Elementen ist dann
<Datentyp> *(<Name>[n])

von (prx) A. K. (prx)


Lesenswert?

timo schrieb:

> Ich habe einen Zeiger, der auf ein Feld mit n Elementen zeigt.
> Nur wie schreibe ich etwas in das Feld hinein?

Der Hintergrund der bescheuerten Deklarationssyntax von C ist der 
Versuch, die Deklaration wie die Verwendung aussehen zu lassen.

Daher passt zur Deklaration
  int (*p)[10];
die Verwendung
  (*p)[i]

von (prx) A. K. (prx)


Lesenswert?

DirkB schrieb:

> <Datentyp> (*<Name>)[n] ist doch das Selbe wie
> <Datentyp>  *<Name>[n]
> da der * an den Namen gebunden ist.

Nein, denn [] bindet stärker als *.

von Rolf Magnus (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> Bei nur einem Variablennamen fällt das noch nicht unangenehm auf, aber
> bei mehreren:
> char*     p1,
>           p2;
> Damit wird p1 vom Typ char* vereinbart, p2 dagegen vom Typ char!
> Gerade als Anfänger fällt man darauf leicht herein und unterstellen, daß
> beide char* wären.
> Deshalb kann man kaum zu dieser Schreibweise raten.

Richtig. Man schreibt es besser so:
1
char*     p1;
2
char*     p2;

dann erübrigt sich die ganze Fragestellung.

Yalu X. schrieb:
> Die Schreibweise
> char* a;
>
> statt, wie bis dahin üblich,
> char *a;
>
> stammt m.W. vom Herrn Stroustrup.

Manchmal wird auch eine dritte Schreibweise verwendet:
1
char * a;


> Der Vorschlag war sicher gut gemeint, hat aber wohl mehr zur Ver- als
> Entwirrung unter den C++-Anfängern beigetragen.

Ich kenne eigentlich keinen Fall, wo jemand dadurch verwirrt wurde und 
finde die Originalschreibweise deutlich verwirrender, weil das *, obwohl 
es Teil des Typs ist, am Namen statt am Typ hängt. Wie Kernighan und 
Ritchie darauf gekommen sind, daß das dahingehören könnte, ist mir immer 
noch ein Rätsel. Ich kenne zwar den Gedankengang, aber finde ihn 
unsinnig.

timo schrieb:
> <Datentyp> (*<Name>)[n] Zeiger auf Feld aus n <Datentyp>-
> Elementen
>
> Aber das verstehe ich nicht, und weiß nicht wie ich es anwenden soll.
>
> Ich habe einen Zeiger, der auf ein Feld mit n Elementen zeigt.

Ja. Das ist zwar eher unüblich, aber möglich.

> Nur wie schreibe ich etwas in das Feld hinein?

(*<Name>)[n] = x;

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf Magnus schrieb:
> Ich kenne eigentlich keinen Fall, wo jemand dadurch verwirrt wurde

Ich schon. Hier ist z.B. einer:

timo schrieb:
> Danke dass du es genau so erklärt hast, denn ich war fast schon der
> meinung, char* bezieht sich damit auf alles folgende.

;-)

Rolf Magnus schrieb:
> und finde die Originalschreibweise deutlich verwirrender, weil das *,
> obwohl es Teil des Typs ist, am Namen statt am Typ hängt.

Ja, die C-Deklarationen sind teilweise schon etwas gewöhnungsbedürftig.
Wenn man das Prinzip aber erst einmal durchschaut hat, sind sie gerade
bei komplizierteren Datentypen oft etwas leichter lesebar. Beispiel:
1
int (*fptr)(int);

Packte man alles Typzugehörige zusammen und an den Anfang, sähe es so
aus:
1
int(*)(int) fptr;

Ich finde nicht, dass das übersichtlicher ist. Oder wie sähe deiner
Meinung nach die lesbarste Deklaration eines Funktionspointers aus?

Die zweite Schreibweise (int(*)(int)) taucht allerding bei Cast-Opera-
tionen auf, was aus Gründen der Einheitlichkeit dafür sprechen würde,
sie auch für Deklarationen zu verwenden.

von (prx) A. K. (prx)


Lesenswert?

Der Fehler in C ist, dass * ein Präfix-Operator ist, die übrigen 
Operatoren dieser Klasse aber Postfix-Operatoren sind, wie (), [] und 
".". Daraus folgt direkt der ganze überflüssige Rattenschwanz mit 
Vorrangstufen, Klammern usw, der Deklaration und Verwendung so bizarr 
macht. Mit Dereferenzierung dahinter statt davor löst sich vieles  in 
der Deklaration in Luft auf, auch die Verwendung wird einfacher.

Aus (*p).a aka p->a wird p*.a. Keine spezieller Operator nötig.
Array aus Pointern: p[]* statt *p[]
Pointer auf Array: p*[] statt (*p)[]
usw.

Dann könnte man noch den Schwachfug eliminieren, den Typnamen als 
einleitendes Schlüsselwort für Variablendeklarationen einzuspannen, was 
eine Kopplung von Scanner und Parser erzwingt, weil der Scanner einen 
Typnamen kontextabhängig mal als normalen Namen und mal als Typnamen 
klassifizieren muss. Also vorneweg die nun obligatorische Storage Class, 
dann der Name, die Typenkette endend mit dem Basistyp. Ergäbe dann
   auto p[10]* int;
Einfach von links nach rechts gehen und es liest sich als genau das, was 
man verbal auch so ausdrücken würde, nämlich dass "p" ein Array aus 
Pointern auf "int" ist. Nicht wie bisher vom u.U. nicht vorhandenen 
Namen (beim cast) unter Beachtung des Vorrangs mal links mal rechts nach 
aussen.

Nebenbei wäre C++ so eine der Zweideutigkeiten erspart geblieben, denn 
sowas wie
   typename(a);
lässt sich nur durch künstliche Festlegung aufdröseln, weils syntaktisch 
sowohl eine Deklaration von "a" mit überflüssigen Klammern als auch 
Aufruf von Konvertierungsfunktion/Konstruktur sein kann. Wer meint, das 
liesse sich ja durch die Klammer erkennen: Erstens ändert das nichts am 
Problem des Parsers, zweitens dürfte sich auch ein Beispiel konstruieren 
lassen, dass in beiden Fällen nichts Überflüssiges enthält.

von Klaus W. (mfgkw)


Lesenswert?

Hört sich plausibel an.
Nur leider etwas zu spät :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

A. K. schrieb:
> Mit Dereferenzierung hinter
> den Namen löst sich vieles  in der Deklaration in Luft auf, auch die
> Verwendung wird einfacher.

Gute Idee. Ganz nebenbei würde man dadurch auch den Fehler in
1
a = b/*pc;

vermeiden.

Wäre die Postfixdereferenzierung wirklich perfekt, oder entstehen
dadurch vielleicht andere Nachteile?

von Klaus W. (mfgkw)


Lesenswert?

Yalu X. schrieb:
> Gute Idee. Ganz nebenbei würde man dadurch auch den Fehler in
> a = b/*pc;

schick, das hatte ich noch gar nicht auf dem Schirm.

von Stefanie B. (sbs)


Lesenswert?

Yalu X. schrieb:
> A. K. schrieb:
>> Mit Dereferenzierung hinter
>> den Namen löst sich vieles  in der Deklaration in Luft auf, auch die
>> Verwendung wird einfacher.
>
> Gute Idee. Ganz nebenbei würde man dadurch auch den Fehler in
>
>
1
> a = b/*pc;
2
>
>
> vermeiden.
>
> Wäre die Postfixdereferenzierung wirklich perfekt, oder entstehen
> dadurch vielleicht andere Nachteile?

Naja andersherm dann ;)
1
a= pc*/b

von (prx) A. K. (prx)


Lesenswert?

Yalu X. schrieb:

> Wäre die Postfixdereferenzierung wirklich perfekt, oder entstehen
> dadurch vielleicht andere Nachteile?

Ich habs nie implementiert und auch nicht komplett durchdacht, bin mir 
daher nicht völlig sicher. Man müsste beispielsweise Casts unter die 
Lupe nehmen, wie die sich dann elegant darstellen lassen.

von (prx) A. K. (prx)


Lesenswert?

Stefan B. schrieb:

> Naja andersherm dann ;)

Das ist ein Fehler in der Definition des Kommentarsymbols, nicht von 
"*". Das // in C++ kann in keinem anderen Kontext vorkommen.

Besser wäre beispielsweise /. xxx ./ gewesen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Stefan B. schrieb:
> Naja andersherm dann ;)a= pc*/b

Das wäre nicht so schlimm. Das Kommentarendezeichen */ ist kein Scanner-
Token, da das Herausfiltern der Kommentare vor der lexikalischen Analyse
(sogar schon vor dem Preprocessing) stattfindet.

von Matthias L. (Gast)


Lesenswert?

>Wäre die Postfixdereferenzierung wirklich perfekt, oder entstehen
>dadurch vielleicht andere Nachteile?

Diese gefällt mir zumindest besser, und ich finde sie verständlicher, 
als bei C.

Ich kenne sie beruflich beim Programmieren von SPS in der Sprache ST:

Dort sieht das so aus:
1
var1    : BYTE;
2
var2    : POINTER TO BYTE;
3
4
var1:= 1;  // das := ist hier die Zuweisung
5
6
var2:= ADR(var1);    // Zeiger auf var1 zeigen lassen
7
var2^:= 91;          // var1 über Zeiger was zuweisen
8
9
Array gehen so:
10
array    : ARRAY[0..7] OF BYTE;
11
var      : POINTER TO BYTE;
12
13
var:= ADR(array[0]);        // Zeiger auf 0-tes Arrayelement
14
var^:= 7;                   // in 0-tes Element schreiben
15
var:= var + SIZEOF(var^);   // Zeiger ein Element weiter schieben
16
17
oder auch so:
18
array    : ARRAY[0..7] OF BYTE;
19
var      : POINTER TO ARRAY{0..7] OF BYTE;
20
21
var:= ADR(array);
22
var[4]^:= 66;

von (prx) A. K. (prx)


Lesenswert?

Yalu X. schrieb:

> Das wäre nicht so schlimm. Das Kommentarendezeichen */ ist kein Scanner-
> Token, da das Herausfiltern der Kommentare vor der lexikalischen Analyse
> (sogar schon vor dem Preprocessing) stattfindet.

Ändert nichts am Problem:
1
/*
2
   dieser = code+ist*/deaktiviert;
3
*/

von Markus G. (Gast)


Lesenswert?

Wieder mal ein typischer mikrocontroller.net Tread. Er ist wieder mal 
entgleist.
Die ursprüngliche Frage war, was der Unterschied ist zwischen

char* ichBinEinZeiger und
char *ichBinEinZeiger

Die Antwort ist: Es ist dasselbe, eine Frage des persönlichen 
Geschmacks.

Damit ist es im Sinne des Fragestellers beantwortet. Ende und gut. Kein 
weiteres gequatsche um des Kaisers Bart. Gerade Anfänger können diesen 
sinnlosen Mist, der immer wieder hinterherkommt nicht einordnen. Sonst 
würden sie ja keine Frage stellen. Also was soll das Ganze? Wenn ihr 
zuviel Freizeit habt, könnt ihr bei mir den Rasen mähen oder euch 
sonstwie nützlich machen.

von (prx) A. K. (prx)


Lesenswert?

Markus G. schrieb:

> Damit ist es im Sinne des Fragestellers beantwortet. Ende und gut. Kein
> weiteres gequatsche um des Kaisers Bart.

Webforen sind keine Support-Hotlines, bei denen mit der Beantwortung des 
Falles dieser geschlossen wird. Sondern sind Diskussionsforen.

Rasen mähen um Mitternacht? Deine Nachbarn würden sich freuen.

von Markus G. (Gast)


Lesenswert?

A. K. schrieb:
> Webforen sind keine Support-Hotlines, bei denen mit der Beantwortung des
> Falles dieser geschlossen wird. Sondern sind Diskussionsforen.

Deswegen kann man doch eine offensichtliche Anfängerfrage beantworten, 
ohne eine Diskussion über die grundsätzliche Vorgehensweise eines 
Compilers loszutreten. Damit ist ihm nicht geholfen.

von Markus G. (Gast)


Lesenswert?

A. K. schrieb:
> Rasen mähen um Mitternacht? Deine Nachbarn würden sich freuen.

Mein Nachbar ist Bademeister bei uns im Freibad und mäht seinen Rasen 
gewöhlich am Samstag bevor er zur Arbeit geht. Das Freibad öffnet um 
8:00 Uhr. Das ist für mich auch nicht immer lustig.

von Philipp (Gast)


Lesenswert?

nur mal so in den Raum geworfen (weil ich persönlich das echt toll finde 
^_^):
In D gibt es einen unterschied.

ACHTUNG: NICHT IN C / C++

schreibt man:
int* p1, p2, p3;

sind alle drei Zeiger.

schreibt man aber:
int *p1, p2, p3;

ist nur p1 ein Zeiger.

von (prx) A. K. (prx)


Lesenswert?

Markus G. schrieb:

> Deswegen kann man doch eine offensichtliche Anfängerfrage beantworten,

War doch mit den ersten 2 Antworten schon geschehen.

von Rolf Magnus (Gast)


Lesenswert?

Yalu X. schrieb:
> Packte man alles Typzugehörige zusammen und an den Anfang, sähe es so
> aus:
> int(*)(int) fptr;
>
> Ich finde nicht, dass das übersichtlicher ist.

Ich finde es gar nicht so schlecht. Da ist der Name zumindest nicht 
irgendwo mitten reingefummelt, sondern wie bei einfacheren 
Variablendefinitionen rechts vom Typ, schön separat und gleich 
erkennbar.
Aber Deklarationen, die einen Funktionszeiger beinhalten, sind in C 
sowieso furchtbar. Man schaue sich die Deklaration der Standard-Funktion 
signal() an, die einen Funktionszeiger übergeben bekommt und einen 
zurückgibt:
1
void (*signal(int signum, void (*handler)(int)))(int);

Ein munterer Mix aus Prefixen, Postfixen und irgendwo dazwischen noch 
Funktionsname und Parameternamen.
Wenn man das alles etwas sortieren würde, wäre das meiner Meinung nach 
schon besser zu lesen:
1
void (*)(int) signal(int signum, void(*)(int) handler);

> Die zweite Schreibweise (int(*)(int)) taucht allerding bei Cast-Opera-
> tionen auf, was aus Gründen der Einheitlichkeit dafür sprechen würde,
> sie auch für Deklarationen zu verwenden.

Das außerdem.

Markus G. schrieb:
> A. K. schrieb:
>> Webforen sind keine Support-Hotlines, bei denen mit der Beantwortung des
>> Falles dieser geschlossen wird. Sondern sind Diskussionsforen.
>
> Deswegen kann man doch eine offensichtliche Anfängerfrage beantworten,
> ohne eine Diskussion über die grundsätzliche Vorgehensweise eines
> Compilers loszutreten. Damit ist ihm nicht geholfen.

Die Frage wurde beantwortet, also ist ihm geholfen. Wem ist denn 
geholfen, wenn die Diskussion an dieser Stelle sofort abgebrochen wird?

von Klaus W. (mfgkw)


Lesenswert?

Markus G. schrieb:
> A. K. schrieb:
>> Rasen mähen um Mitternacht? Deine Nachbarn würden sich freuen.
>
> Mein Nachbar ist Bademeister bei uns im Freibad und mäht seinen Rasen
> gewöhlich am Samstag bevor er zur Arbeit geht. Das Freibad öffnet um
> 8:00 Uhr. Das ist für mich auch nicht immer lustig.

Könnten wir bitte beim Thema bleiben? :-)

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:

>> int(*)(int) fptr;
>>
>> Ich finde nicht, dass das übersichtlicher ist.
>
> Ich finde es gar nicht so schlecht. Da ist der Name zumindest nicht
> irgendwo mitten reingefummelt, sondern wie bei einfacheren
> Variablendefinitionen rechts vom Typ, schön separat und gleich
> erkennbar.

Diese Variante wird man reflexhaft von recht nach links lesen, und dann 
suggeriert sie eine Funktion, die einen Pointer auf int retourniert.

Man muss also auch hier immer noch Stricken spielen, eine links, eine 
rechts, eine fallen lassen. Nein, aus der Nummer kommt man nicht raus. 
Mit einem Mix aus Prefix und Postfix (eigentlich Infix) muss man innen 
anfangen und sich nach aussen durchkämpfen. Und grad die Cast Notation 
erschwert es auch noch zusätzlich, dieses "innen" überhaupt zu finden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

A. K. schrieb:
> Ändert nichts am Problem:/*
>    dieser = code+ist*/deaktiviert;
> */

Klar, das ergibt einen Fehler. Der Fehler entsteht man aber auch dann,
wenn der auskommentierte Codeabschnitt bereits /**/-Kommentare enthält.
Insofern hat man mit dem Postfix-* nicht viel verloren.

Die bessere Methode zum "Auskommentieren" ist ohnehin #if 0 ... #endif,
auch wenn es etwas mehr Tipparbeit ist.


Markus G. schrieb:
> Wieder mal ein typischer mikrocontroller.net Tread. Er ist wieder mal
> entgleist.

Von Entgleisung würde ich hier nicht sprechen, sondern eher von einer
Themenerweiterung. Nachdem Stefan Ernst bereits nach 3 Minuten die
knackig-korrekte Antwort gegeben hat, nämlich

> Gibt keinen.

ist es in einem Forum wie diesem durchaus legitim, anschließend noch
etwas ins Detail zu gehen und auch Hintergründe zu beleuchten. Wen nur
die Antwort auf die Frage interessiert, hört danach einfach auf zu
lesen.

Von der speziellen Frage

  "Unterschied char* a; char *a;"

zur etwas allgemeineren Frage der Platzierung des Dereferenzierungssym-
bols ist ja auch kein wirklich großer Sprung.

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.