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).
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!
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?
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.
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
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!
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])
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]
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 *.
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;
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.
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.
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?
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 ;)
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.
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.
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.
>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:POINTERTOBYTE;
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
Arraygehenso:
10
array:ARRAY[0..7]OFBYTE;
11
var:POINTERTOBYTE;
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
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:
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.
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.
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.
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.
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.
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:
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:
> 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?
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? :-)
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.
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.