Forum: Compiler & IDEs C sorgt oft für Verwirrungen


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Möchte gerade noch mal das Beispiel aus dem anderen Thread aufgreifen.

Beitrag "Re: Was ist die Ausgabe?"

Darin wird unter 2) behauptet

" Selbst wenn man es syntaktisch zu einem C-Programm macht,
   ist die 1. Zeile laut C-Standard undefiniert. "

bezogen auf

> char* str = "abc";

Was bitte soll daran "undefiniert" sein?

Laut meiner Kenntnis ist das eine übliche Definition für einen Zeiger 
auf eine konstante Zeichenkette.

Undefiniert ist nur, wenn man versucht nachträglich den Inhalt der 
konstanten Zeichenkette zu ändern. Das ergibt beispielsweise unter 
PellesC eine Exeption.

Gegenteilige Meinungen? Bestätigungen?

von Verwirrter (Gast)


Bewertung
-2 lesenswert
nicht lesenswert
Ich wüsste schon nicht was char* beduten soll.

von Andreas (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

an der definition ist nichts konstant, natürlich kann man den String 
ändern.

Andreas

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Andreas (Gast) schrieb:

> Hallo,

> an der definition ist nichts konstant, natürlich kann man den String
> ändern.

> Andreas

Du meinst was ich schrieb ist falsch?

von Klaus W. (mfgkw)


Bewertung
1 lesenswert
nicht lesenswert
Skeptiker schrieb:
>> char* str = "abc";
>
> Was bitte soll daran "undefiniert" sein?

Die Zeile ist deshalb Quark (um es etwas volkstümlicher auszudrücken), 
weil man einen Zeiger vom Typ char * hat, über den man ein oder mehrere 
Zeichen ändern könnte, aber dieser Zeiger auf Speicher zeigt, der const 
ist (ein Stringliteral).

Solange man nicht versucht, den String über den Zeiger zu ändern, wird 
das nicht auffallen.
Aber wenn du z.B. später schreibst: *str=x dann versuchst du, das 'a' 
durch ein 'x' zu ersetzen.

Was wird passieren? Kommt ganz drauf an...

Je nach System und Compiler und Vollmondstand kann es
- klappen, das a wird überschrieben
- das Programm stürzt ab (wenn der String wirklich zur Laufzeit in einem 
schreibgeschützten Speicher steht
- die ISS stürzt ab
- ...

von Verwirrter (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Kannst Du denn mal erklären was char* bedeuten soll, oder bin ich der 
einzige der das nicht kennt?

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Laut meiner Kenntnis ist das eine übliche Definition für einen Zeiger
> auf eine konstante Zeichenkette.

"üblich" heißt nicht richtig.

Unter C-Programmierern, die wisse was sie tun, ist es üblich, so etwas 
zu schreiben:
1
    const char *str1 = "abc";
2
    char str2[4] = "abc";
3
    char str3[] = "abc";

Dann klappts auch mit der Nachbarin.

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
Verwirrter schrieb:
> Kannst Du denn mal erklären was char* bedeuten soll, oder bin ich der
> einzige der das nicht kennt?

Das kann jedes C-Buch.

Es ist ein Datentyp, und zwar "Zeiger auf char".
Nebenbei: nicht "Zeiger auf const char"...

von Mladen G. (mgira)


Bewertung
0 lesenswert
nicht lesenswert
Verwirrter schrieb:
> Kannst Du denn mal erklären was char* bedeuten soll, oder bin ich
> der
> einzige der das nicht kennt?

Ein Zeiger auf ein char, der * bedeutet immer "Zeiger".

Klaus Wachtler schrieb:
> Nebenbei: nicht "Zeiger auf const char"...

Das kann vom Compiler nicht autom. zu "const" gemacht werden, muss aber 
nicht, deswegen ist es wohl nicht definiert was passiert wenn man 
versucht das zu String literal zu aendern.

: Bearbeitet durch User
von Verwirrter (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ja sicher aber doch wohl kaum so:

> char* str = "abc";

wenn dann ja wohl so:

char *str = "abc";

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler (mfgkw) schrieb:

> Solange man nicht versucht, den String über den Zeiger zu ändern, wird
> das nicht auffallen.
> Aber wenn du z.B. später schreibst: *str=x dann versuchst du, das 'a'
> durch ein 'x' zu ersetzen.

Ich verstehe diese Argumentation nicht. Das man mit etwas richtigem 
etwas falsches anstellen kann ist doch usus in vielen 
Programmiersprachen. Ich kann auch auf irgend ein anderes (legales) 
Konstrukt falsch zufreifen und erhalte im günstigsten Fall eine 
aussagekräftige Fehlermeldung und in etlichen anderen Fällen 
undefinierte Zustände (event. eine Exeption). Wenn ich beispielsweise 
(in C) über die Feldgrenze eines Array hinaus Werte verändere begehe ich 
auch heftige Fehler (mit unkalkulierbaren Seiteneffekten). Deshalb ist 
doch aber das Array nicht daran schuld (eher die Freiheit des C 
Dialekts).

von Mladen G. (mgira)


Bewertung
0 lesenswert
nicht lesenswert
Verwirrter schrieb:
> Ja sicher aber doch wohl kaum so:
>
>> char* str = "abc";
>
> wenn dann ja wohl so:
>
> char *str = "abc";

Iss egal in C, beides legal und gleichwertig ;)

: Bearbeitet durch User
von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler (mfgkw) schrieb:

> Unter C-Programmierern, die wisse was sie tun, ist es üblich, so etwas
> zu schreiben:

>    const char *str1 = "abc";

Da stimme ich dir zu.

> Es ist ein Datentyp, und zwar "Zeiger auf char".
> Nebenbei: nicht "Zeiger auf const char"...

Stimmt auch, aber dennoch zeigt der Zeiger auf eine konstante 
Zeichenkette!

von Hans (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mladen G. schrieb:
>> wenn dann ja wohl so:
>>
>> char *str = "abc";

ja, das ist logischer. Geht aber beides.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Der Pferdefuss an
  char *str = "abc";
ist, dass es der Sprachlogik folgend eigentlich nicht zulässig sein 
sollte. Weil "abc" in einem nicht modifizierbaren Bereich des Speicher 
landet bzw. landen darf, folglich eigentlich vom Typ const char * sein 
müsste.

Wenn dies aber der Fall wäre, dann würden allerlei antike C Programme 
auf die Nase fallen, die noch aus der Zeit stammen, als es kein "const" 
gab. Folglich ist "abc" vom Typ char *, aber nur eigentlich const.

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die ursrüngliche Frage war übrigens, warum so ein Konstrukt

char *str = "abc";

per se undefiniert sein soll.

Ich halte das für eine unzutreffende Aussage.

Das man besser ein const davor schreibt ist klar, ändert aber nichts 
daran.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Ich halte das für eine unzutreffende Aussage.

Das Statement ist zulässig. Wie eben beschrieben ist eine solche 
String-Konstante aus historischen Gründen nicht "const". Dazu C99 im 
Rationale:

"String literals are not required to be modifiable. This specification 
allows implementations to share copies of strings with identical text, 
to place string literals in read-only memory, and to perform certain 
optimizations. However, string literals do not have the type array of 
const char in order to avoid the problems of pointer type checking, 
particularly with library functions, since assigning a pointer to const 
char to a plain pointer to char is not valid."

: Bearbeitet durch User
von Andreas (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

char *str = "xyz";

ist nicht undefiniert.

Ein const davor schreiben ändert nur die Definition des Pointers,
der Inhalt des Srings ist nach wie vor änderbar.

"xyz" ist  ein Literal, und nicht automatisch konstant.

Bei
const char const *str = "xyz";
wäre der String konstant.

Andreas

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Nicht zulässig ist das im verwiesenen Thread gezeigte:
  char* str = "abc";
  str[2] = 'd';      // <-----------
  printf("%s", str);
weil hier der String modifiziert wird.

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. (prx) schrieb:

> Der Pferdefuss an
>  char *str = "abc";
> ist, dass es der Sprachlogik folgend eigentlich nicht zulässig sein
> sollte. Weil "abc" in einem nicht modifizierbaren Bereich des Speicher
> landet bzw. landen darf, folglich eigentlich vom Typ const char * sein
> müsste.

Wird aber von verschiedenen Compilern ohne Warnmeldung compiliert.

Wie's beim gcc ist weiß ich gerade nicht.

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Möchte gerade noch mal das Beispiel aus dem anderen Thread aufgreifen.
>
> Beitrag "Re: Was ist die Ausgabe?"
>
> Darin wird unter 2) behauptet
>
> " Selbst wenn man es syntaktisch zu einem C-Programm macht,
>    ist die 1. Zeile laut C-Standard undefiniert. "
>
> bezogen auf
>
>> char* str = "abc";
>
> Was bitte soll daran "undefiniert" sein?

Vielleicht war das einfach nur ein Versehen, und die zweite Zeile war 
gemeint. Die versucht nämlich, diesen Zeiger zu benutzen, um den String 
zu ändern. Und das ist tatsächlich undefiniert.

Verwirrter schrieb:
> Ja sicher aber doch wohl kaum so:
>
>> char* str = "abc";
>
> wenn dann ja wohl so:
>
> char *str = "abc";

Völlig egal, ob du
1
char* str
1
char *str
1
char*str
1
char * str
oder auch
1
char
2
*
3
str
schreibst. Ist alles das gleiche.

Hans schrieb:
> Mladen G. schrieb:
>>> wenn dann ja wohl so:
>>>
>>> char *str = "abc";
>
> ja, das ist logischer. Geht aber beides.

Es ist eher unlogischer, aber es ist so, wie's die Erschaffer von C sich 
gedacht haben.

von (prx) A. K. (prx)


Bewertung
-1 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Wird aber von verschiedenen Compilern ohne Warnmeldung compiliert.

Ja. Eben das versuchte ich damit zu erklären. Hab ich mich zu 
unglücklich ausgedrückt?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Es ist eher unlogischer, aber es ist so, wie's die Erschaffer von C sich
> gedacht haben.

Das wird erst verständlich, wenn man sich vor Augen hält, dass es 
"const" erst seit ANSI C gibt. Strings gab es aber schon vorher.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Andreas schrieb:
> Ein const davor schreiben ändert nur die Definition des Pointers,
> der Inhalt des Srings ist nach wie vor änderbar.

Nein, eine Änderung des Strings ist nicht generell zulässig. C99 
erwähnt dies jedoch als gängige Erweiterung (J.5.5).

: Bearbeitet durch User
von Skeptiker (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Das Beispiel findet sich übrigens auch im "Programmieren in C", 
Kernighan/Ritchie, Zweite Ausgabe ANSI.

S. 101

char *pmessage = "now is the time";   /* ein Zeiger */

Zitat

" ... Andererseits ist pmessage ein Zeiger der so initialisiert ist, daß 
er auf eine konstante Zeichenkette zeigt; der Zeiger kann später so 
verändert werden, daß er auf etwas anderes zeigt. Versucht man aber, den 
Inhalt zu ändern, ist das Resultat undefiniert."

Ich finde das eigentlich sehr aussagekräftig.

von Garden (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mal eine Erklärung zu Zeigern in C.

http://et-tutorials.de/3368/pointer-in-c/

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Rolf Magnus schrieb:
>> Es ist eher unlogischer, aber es ist so, wie's die Erschaffer von C sich
>> gedacht haben.
>
> Das wird erst verständlich, wenn man sich vor Augen hält, dass es
> "const" erst seit ANSI C gibt. Strings gab es aber schon vorher.

Ähm, ich hab mich da auf was völlig anderes bezogen. Hans hat da wohl 
eine Zeile zu wenig zitiert. Es ging um "char* str" vs. "char *str".

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Möchte gerade noch mal das Beispiel aus dem anderen Thread aufgreifen.
>
> Beitrag "Re: Was ist die Ausgabe?"
>
> Darin wird unter 2) behauptet
>
> " Selbst wenn man es syntaktisch zu einem C-Programm macht,
>    ist die 1. Zeile laut C-Standard undefiniert. "

Sollte heißen: Die 2. Zeile in Zusammenspiel mit der 1. Zeile.

6.5.4 String Literals $7
1
It is unspecified whether these arrays are distinct provided their
2
elements have the appropriate values.  If the program attempts to
3
modify such an array, the behavior is undefined.

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. (prx) schrieb:

> Nicht zulässig ist das im verwiesenen Thread gezeigte:
>  char* str = "abc";
>  str[2] = 'd';      // <-----------
>  printf("%s", str);
> weil hier der String modifiziert wird.

Ja genau. Ergibt sowohl in PellesC als auch im VisualC++ 2008 (Express) 
eine unbehandelte Ausnahme (Exeption).

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Johann L. (gjlayde) schrieb:

bezogen auf das 1. Posting im Thread hier

> Sollte heißen: Die 2. Zeile in Zusammenspiel mit der 1. Zeile.

> 6.5.4 String Literals $7

> It is unspecified whether these arrays are distinct provided their
> elements have the appropriate values.  If the program attempts to
> modify such an array, the behavior is undefined.

Danke für die Klarstellung!

Irgendwie verleitet C/C++ generell dazu schnell in irgend ein 
Fettnäpfchen zu treten.

;-)

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Ja genau. Ergibt sowohl in PellesC als auch im VisualC++ 2008 (Express)
> eine unbehandelte Ausnahme (Exeption).

Weil der Speicher dafür read-only ist und ein schreibender Zugriff 
darauf die Exception auslöst. Bei AVRs hingegen wird es funktionieren.

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Skeptiker schrieb:
>> Ja genau. Ergibt sowohl in PellesC als auch im VisualC++ 2008 (Express)
>> eine unbehandelte Ausnahme (Exeption).
>
> Weil der Speicher dafür read-only ist und ein schreibender Zugriff
> darauf die Exception auslöst. Bei AVRs hingegen wird es funktionieren.

Allerdings kann es auch da zu unerwarteten Nebenwirkungen kommen. Denn 
das da:

A. K. schrieb:
> share copies of strings with identical text

macht GCC. Er geht auch so weit, daß z.B. "Hello World" und "World" sich 
den Speicher teilen können. Wenn also nochmal irgendwo ein identischer 
String vorkommt oder einer, der dem Ende des geänderten Strings 
entspricht, kann's halt sein, daß sich die Änderung auf den auch mit 
auswirkt.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Allerdings kann es auch da zu unerwarteten Nebenwirkungen kommen.

Natürlich.

von MaWin (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Was bitte soll daran "undefiniert" sein?

Nichts.

Der Typ hat keine Ahnung von C und phantasiert.

Er schreibt über einen char Zeiger in Speicher, in den er eugentlich 
nicht schreiben darf.

Ein guter C Compiler hätte das bemerkt (zuweisung von const char an 
char), ein guter Programmiere hätte das verhindert (durch const) und ein 
guter Rechner hätte den Speicher geschützt, aber auf einem uC kann man 
das nicht unbedingt erwarten.

Verwirrter schrieb:
> Kannst Du denn mal erklären was char* bedeuten soll, oder bin ich der
> einzige der das nicht kennt?

Meinst du dass in dem Thread jeder mitmachen muss der kein C kennt?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
MaWin schrieb:
> Ein guter C Compiler hätte das bemerkt (zuweisung von const char an
> char),

Nein. "abc" ist nicht vom Typ const char *, sondern char *.

: Bearbeitet durch User
von mar IO (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Andreas schrieb:
> Bei
> const char const *str = "xyz";
> wäre der String konstant.

Ein const reicht für das char. Eins der beiden kannst Du weg lassen. 
Falls der Zeiger auch noch const sein soll, dann muss ein const zwischen 
dem *chen und und str stehen.

Guckst Du Deklarationen in C ziemlich am Ende der Seite.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
MaWin schrieb:
> Der Typ hat keine Ahnung von C und phantasiert.

Der "Typ" ist der mit C recht gut vertraute Johann L. und hat am 
-fwritable-strings erkennbar die zweite Zeile gemeint, aber 
versehentlich von der ersten Zeile geschrieben.

> zuweisung von const char an char

Ist zulässig. Erst bei Pointern nicht mehr.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Nein. "abc" ist nicht vom Typ const char *, sondern char *.

Auch nicht, sondern vom Typ char [4].

Wo bleibt eigentlich der c_hater? So ein Thread ist doch massig Wasser
auf seine Mühlen :)

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Auch nicht, sondern vom Typ char [4].

OK, aber in dem Kontext wird char * draus, nicht const char *.

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. (prx) schrieb:

MaWin schrieb:
>> Der Typ hat keine Ahnung von C und phantasiert.

> Der "Typ" ist der mit C leidlich vertraute Johann L. und hat am
> -fwritable-strings erkennbar die zweite Zeile gemeint, aber
> versehentlich von der ersten Zeile geschrieben.

Deswegen hatte ich mich auch gewundert wenn jemand mit viel Erfahrung 
sowas schreibt und keiner widerspricht. Aber das ist jetzt aufgelärt. 
Dennoch interessant wieviel Gesprächsbedarf dieser Thread zutage 
fördert.

;)

von fibonacci (Gast)


Bewertung
0 lesenswert
nicht lesenswert
http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/pointers.html


      main()
      {
        int *Width;
  *Width = 34;
      }
was haltet ihr hiervon?

von Grotz (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
fibonacci schrieb:
> http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/pointers.html
>
>
>       main()
>       {
>         int *Width;
>   *Width = 34;
>       }
> was haltet ihr hiervon?

Nichts.

von Achim_42 (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Klaus Wachtler
>C-Buch

BINGO!

von Kaj (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Verwirrter schrieb:
> Ja sicher aber doch wohl kaum so:
>
>> char* str = "abc";
>
> wenn dann ja wohl so:
>
> char *str = "abc";
Nö, kommt auf deine Code-Convention an.
Bei uns in der Firma "gehört der Stern zum Datentyp" (char* var) und 
"nicht zur variablen" (char *var)

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
fibonacci (Gast) schrieb:

> http://home.fhtw-berlin.de/~junghans/cref/CONCEPT/pointers.html


>     main()
>     {
>        int *Width;
>  *Width = 34;
>      }
>
> was haltet ihr hiervon?

Ich würde sagen, da hat der Herr G. Junghanns von der fhtw-Berlin, der 
diesen C-Kurs aus dem englischen Original von Martin Leslie (Release 
1.07) übersetzt hat, nicht aufgepasst.

Besser sich das Original mal kurz betrachten. Das ist neuer (Rel. 1.09) 
und die Stelle ist jetzt etwas anders erklärt.

Übersicht
http://gd.tuwien.ac.at/languages/c/cref-mleslie/cref.html

http://gd.tuwien.ac.at/languages/c/cref-mleslie/CONCEPT/pointers.html

 main()
      {
          int *Width;                                 /* 1 */

          Width = (int *)malloc(sizeof(int));         /* 2 */

          *Width = 34;                                /* 3 */
      }

ABER: Dass der Autor hier eine Funktion verwendet ohne den 
erforderlichen Header einzubinden #include <stdlib.h> spricht nicht 
gerade für ihn bzw. deutet auf weitere wohl zu erwartende 
Schlampigkeiten hin.

Fazit: Es gibt bessere Lernhilfen.

von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> main()
>       {
>           int *Width;                                 /* 1 */
>
>           Width = (int *)malloc(sizeof(int));         /* 2 */
>
>           *Width = 34;                                /* 3 */
>       }

Wenn dann schon richtig richtig:
1
#include <stdlib.h>
2
3
void main(void)
4
{
5
  int *Width;
6
  Width = malloc(sizeof(int));
7
  *Width = 34;
8
9
  return 0;
10
}

Soll das echt Lernmaterial sein? Das ist ja echt schrecklich!

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon (Gast) schrieb:

> Wenn dann schon richtig richtig:

Fehlt aber im Original (aber er benutzt ja auch printf ohne die stdio.h 
einzubinden und stärt sich nicht dran).

> Soll das echt Lernmaterial sein?

Also der zentrale Informatikdienst der TU-Wien sieht das wohl so.

http://gd.tuwien.ac.at/languages/c/

von Hans (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Hans schrieb:
>> Mladen G. schrieb:
>>>> wenn dann ja wohl so:
>>>>
>>>> char *str = "abc";
>>
>> ja, das ist logischer. Geht aber beides.
>
> Es ist eher unlogischer, aber es ist so, wie's die Erschaffer von C sich
> gedacht haben.

Kernighan, Ritchie 2.Auflage, Seite 84:
1
The declaration of the pointer ip,
2
3
   int *ip;
4
5
is intended as a mnemonic; it says that the expression *ip is an int.

und das ist für mich logischer.

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


Bewertung
0 lesenswert
nicht lesenswert
Kaj schrieb:
> Bei uns in der Firma "gehört der Stern zum Datentyp" (char* var) und
> "nicht zur variablen" (char *var)

Das Problem mit dieser Schreibweise ist, dass sie dann gar nicht mehr
so logisch ist, wenn man mehrere Elemente auf einmal deklariert:
1
char* var1, var2;

"var1" ist vom Typ "Zeiger auf char", "var2" jedoch vom Typ "char".

Klar, man kann Mehrfachdeklarationen/-definitionen nun wieder per
lokalem Regelwerk verbieten, aber wirklich sinnvoll ist das auch
wieder nicht.

Bei der alternativen Schreibweise:
1
char *var1, *var2;

ist offensichtlicher, dass man den Stern für zwei Zeigervariablen auch
zweimal schreiben muss.

von D. V. (mazze69)


Bewertung
1 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Klar, man kann Mehrfachdeklarationen/-definitionen nun wieder per
> lokalem Regelwerk verbieten, aber wirklich sinnvoll ist das auch
> wieder nicht.

C-Programmierer sind von Natur aus schreibfaul - wenn man das im 
Hinterkopf behält, wird (fast) immer ein Schuh draus.

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


Bewertung
0 lesenswert
nicht lesenswert
Die Syntax der Deklarationen sollte wie die Verwendung in Expressions 
aussehen. So hatte Ritchie sich das ursprünglich gedacht. Das Ergebnis 
war und ist freilich einfach nur eine nackte Katastrophe. Mit der wir 
seit Jahrzehnten leben müssen.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Kaj schrieb:
> Bei uns in der Firma "gehört der Stern zum Datentyp" (char* var) und
> "nicht zur variablen" (char *var)

"Gehören" tut in
1
char * var;

der "*" nach dem C-Standard eindeutig zu "var" und nicht zu "char".
"char" ist dabei der type-specifier und "* var" der declarator.

Diese Syntaxdefinition macht sich – wie bereits von Jörg aufgezeigt –
besonders bei Mehrfachdeklarationen bemerkbar.

Außerdem:

Wenn man den "*" an das "char" klebt
1
char* var;

müsste man konsequenterweise bei Array- und Funktionsdeklarationen die
Größenangabe bzw. die Argumentliste ebenfalls direkt hinter das "char"
schreiben, also nicht so
1
char array[4];
2
char function(int n);

sondern so:
1
char[4] array;
2
char(int n) function;

Diese Schreibweise hätte tatsächlich den Vorteil der klaren Trennung
zwischen Typ und Indentifier:
1
//    ___ Typ ___    _ Identifier _
2
//   /           \  /              \ ;
3
         char*            var        ;
4
        char[4]          array       ;
5
      char(int n)       function     ;

Aber so ist die C-Syntax nun einmal nicht spezifiziert, und die beiden
letzten Zeilen sind im Gegensatz zur ersten nicht nur syntaktisch
ungenau, sondern sogar fehlerhaft. Also verfährt man am besten in allen
Fällen einheitlich und packt die ganze Symbolik aus *, [] und () zum
Identifier, genau so, wie es weiland Meister Ritchie schon gemacht hat,
ob es nun schön aussieht oder nicht.

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


Bewertung
1 lesenswert
nicht lesenswert
Wenn man das nicht mag, muss man halt Pascal nehmen. ;-)
1
var name1, name2, name3: type;

Da ist es wirklich sauber getrennt.

Aber es war halt nicht Niklaus Wirth, der damals in der Situation war,
den Assembler auch für die unteren Ebenen eines Betriebssystems ablösen
zu wollen, sondern es waren Dennis Ritchie und seine Kollegen.  Sie
hatten vielleicht nicht so akademisch ausgefeilte Vorstellungen von 
einer
angenehmen Syntax der Sprache, dafür aber ganz offensichtlich recht
gute Ideen, wie man so eine Sprache aufbauen muss, damit sie auch
wirklich eine Alternative zur Assemblernutzung auch in diesem Bereich
ist, ohne dass man zu viel Performance einbüßt.  Konstrukte wie *cp++
oder --*sp ließen sich direkt auf die PDP-11 abbilden.

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> Wenn dann schon richtig richtig:

richtig richtig richtig wäre es, wenn man den Rückgabewert von malloc() 
prüfen würde und entsprechend ragiert ....

von Mark B. (markbrandis)


Bewertung
-1 lesenswert
nicht lesenswert
D. V. schrieb:
> C-Programmierer sind von Natur aus schreibfaul

Schlechte Programmierer sind von Natur aus schreibfaul.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die Typ-Deklarations-Syntax von C und umsomehr C++ ist leider ziemlich 
verkorkst. Seit C++11 kann man sich da ein bisschen behelfen wie z.B. so 
http://ideone.com/mxUPZt - die Verschachtelung der einzelnen "Modifier" 
wie cnst, ary etc. macht eher klar was wann kommt und wie 
zusammengesetzt ist als die übliche Aneinanderreihung von *&[]() ...

von D. V. (mazze69)


Bewertung
0 lesenswert
nicht lesenswert
Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt 
nicht nach Syntax-Unklarheiten. Umso mehr wunderst es mich, dass hier so 
oft nachgefragt wird. Sind das nur "Abschreiber" oder Copy&Paster...

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
D. V. schrieb:
> Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt
> nicht nach Syntax-Unklarheiten.
Auch wenn man sowas sieht:
1
const int& (*f) (const int* [7], int* (&y) [7]);
sofort absolut klar was gemeint ist?!

von Bastler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mark Brandis schrieb:
> D. V. schrieb:
>> C-Programmierer sind von Natur aus schreibfaul
>
> Schlechte Programmierer sind von Natur aus schreibfaul.

Nur bedeutet viel zu schreiben nicht immer gut programmieren.

von Bastler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> D. V. schrieb:
>> Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt
>> nicht nach Syntax-Unklarheiten.
> Auch wenn man sowas sieht:const int& (*f) (const int* [7], int* (&y)
> [7]);sofort absolut klar was gemeint ist?!

Es gibt auch andere Sprachen, die nicht jeder beherrscht. Ich behaupte 
auch nicht, Französisch wäre Scheiße, blos weil ich vor 35 Jahren zu 
faul war, Vokabeln zu lernen. Wem C nicht gefällt, der könnte doch 
einfach einer der vielen Alternativen verwenden und solche Threads 
lassen!

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Bastler schrieb:
> Es gibt auch andere Sprachen, die nicht jeder beherrscht. Ich behaupte
> auch nicht, Französisch wäre Scheiße, blos weil ich vor 35 Jahren zu
> faul war, Vokabeln zu lernen.
Man kann aber behaupten dass französisch scheiße weil unnötig 
kompliziert ist. Man kann auch akzeptieren dass einige Dinge in C und 
C++ vermurkst sind und sie dennoch verwenden, und wenn man ein Problem 
hat im Forum fragen...
> Wem C nicht gefällt, der könnte doch
> einfach einer der vielen Alternativen verwenden und solche Threads
> lassen!
Für C, vielleicht. Für C++ gibt es keine ähnlich mächtige Alternative.

von MaWin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> "abc" ist nicht vom Typ const char *, sondern char *.

Das bestimmt der Compiler.

von Nicht"Gast" (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> D. V. schrieb:
>> Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt
>> nicht nach Syntax-Unklarheiten.
> Auch wenn man sowas sieht:const int& (*f) (const int* [7], int* (&y)
> [7]);sofort absolut klar was gemeint ist?!

Ja, es ist völlig klar, dass das absoluter Blödsinn ist. Wer solche 
Funktionszeiger in C++ benutzt hat den Schuss nicht gehört ;)

Grüsse

von Mark B. (markbrandis)


Bewertung
0 lesenswert
nicht lesenswert
Bastler schrieb:
> Mark Brandis schrieb:
>> D. V. schrieb:
>>> C-Programmierer sind von Natur aus schreibfaul
>>
>> Schlechte Programmierer sind von Natur aus schreibfaul.
>
> Nur bedeutet viel zu schreiben nicht immer gut programmieren.

Richtig. Die Wahrheit liegt, wie so oft, in der Mitte.

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Das wird erst verständlich, wenn man sich vor Augen hält, dass es
> "const" erst seit ANSI C gibt. Strings gab es aber schon vorher.

Huch, hab ich da was verpaßt?
Nee, hab ich nicht. C kennt keine Strings.
Es kennt nur ein Agreement, wonach bei einem Array aus char's dann 
Schluß ist, wenn ein char mit dem numerischen Wert 0 auftaucht. Also 
KEINE Strings, auch nicht bei ANSI-C.

Gäbe es bei C einen Datentyp 'String', dann könnte man ihn auch zuweisen 
und sowohl als Argument als auch als Resultat als auch in 
Ergibtanweisungen verwenden verwenden. Aber so wie es ist, werden 
milliardenfach immer wieder Arrays nach einem Nullbyte durchsucht, immer 
wieder und wieder. Und selbst bei konstanten Literalen, wo man deren 
Size bereits zur Compilezeit hätte festlegen können. strlen läßt 
milliardenfach grüßen.


Jörg Wunsch schrieb:
> Das Problem mit dieser Schreibweise ist, dass sie dann gar nicht mehr
> so logisch ist, wenn man mehrere Elemente auf einmal deklariert:
> char* var1, var2;

Ja, Jörg, das ist mal wieder eine der vielen Kurzsichtigkeiten von K&R 
gewesen. Trotzdem ist

  typbez* bezeichner;

die weitaus logischere Schreibweise (die ich auch bevorzuge), denn im 
Klartext steht (und soll ja so auch stehen): "Hier deklariere ich einen 
Zeiger auf den Typ 'typbez' und nenne ihn 'bezeichner'." und da gehört 
der Stern zum 'typbez' dazu. Das, was du da als Stolperfalle aufzeigst, 
ist eigentlich ein schwerer Bug, denn eine Typfestlegung sollte für 
alle aufgezählten Variablen gelten und nicht nur für den ersten und dann 
nur noch partiell für die nachfolgenden. Jahaha, ich weiß, "es ist eben 
so, basta!" aber der Logiker in mir erklärt die Urheber dieser Sprache 
für Leute, die überhaupt keinen Weitblick hatten.

Deine Vorliebe kann ich ja verstehen, aber sie ist eher unlogischer, 
denke mal an so eine recht beliebte Sentenz:

  *bezeichner++ = *andererbezeichner++;

da ist klar, daß der Stern am Anfang des Bezeichners zum 
Dereferenzieren benutzt wird, also zum eigentlichen Erfüllen der 
Zeige-Funktion eines Zeigers. Aber bei der Deklaration eines Zeigers 
will man ja einen Zeiger vereinbaren und nicht einen Zeiger 
dereferenzieren. Hab ich mich klar genug ausgedrückt oder nicht?

Abgesehen davon frage ich mich, wozu solche Konstrukte wie

 char* str = "abc";

überhaupt hingeschrieben werden. Ein

 const char str[] = {"abc"};

sollte doch ausreichen, um damit ein sinnvolles Programm schreiben zu 
können.

W.S.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
MaWin schrieb:
>> "abc" ist nicht vom Typ const char *, sondern char *.
>
> Das bestimmt der Compiler.

Den Datentyp lexikalischer Strings definiert der C Standard eindeutig 
als char[], nicht const char[].

C99+C11: "For character string literals, the array elements have type 
char"

C99 Rationale: "However, string literals do not have the type array of 
const char in order to avoid the problems of pointer type checking"

Der Compiler bestimmt, was passiert, wenn man drauf zu schreiben 
versucht. Andere Baustelle. Das ist nicht schön, aber historisch 
bedingt.

: Bearbeitet durch User
von Hans (Gast)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Hab ich mich klar genug ausgedrückt oder nicht?

Haha, was ein Pamphlet. Ich glaube Du hast Dich genau so ausgedrückt, 
wie Du auch verstehst.

von D. V. (mazze69)


Bewertung
0 lesenswert
nicht lesenswert
Wer C mit dem antiqiertem Overhead nicht klarkommt, und das scheinen die 
meisten Newcomer hier zu sein, der orientiere sich bitte anderwärtig. Es 
bibt genug Alternativen, als sich mit den antiquaren-Make-Files 
runzuärgern.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Huch, hab ich da was verpaßt?

Offenbar.

> C kennt keine Strings.

Wenn der C99 Standard explizit "6.4.5 String literals" anführt, dann 
gibt es offensichtlich Strings. Als lexikalisches Element, nicht als 
Datentyp. Von einem entsprechenden Datentyp war auch nie die Rede.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Trotzdem ist
>
>   typbez* bezeichner;
>
> die weitaus logischere Schreibweise (die ich auch bevorzuge), denn im
> Klartext steht (und soll ja so auch stehen): "Hier deklariere ich einen
> Zeiger auf den Typ 'typbez' und nenne ihn 'bezeichner'." und da gehört
> der Stern zum 'typbez' dazu.

Wenn man deine in Anführungszeichen geschriebene Aussage wortwörtlich in
(wenn auch syntaktisch nicht ganz korrektes C) zurückübersetzen würde,
stände da
1
&typbez bezeichner;

Das wäre für mich eine logisch klingende Variablendeklaration.

Die von Ritchie gewählte Schreibweise
1
typbez *bezeichner;

ist eben so zu lesen: "Hier deklariere ich eine Variable mit Namen
'bezeichner', die dereferenziert den Typ 'typbez' ergibt.

Im obigen Fall deklariere ich die Variable explizit, d.h. ich sage dem
Compiler, was die Variable sein soll, nämlich ein Zeiger.

Im unteren Fall deklariere die Variable implizit, d.h. ich sage dem
Compiler, was ich mit der Variable tun können möchte, nämlich
dereferenzieren.

Beide Alternativen sind für mich logisch konsistent, wenngleich die
implizite Deklaration nach Ritchie in Prosa übersetzt etwas holprig
klingt.

Bei der Stroustrup-Schreibweise hingegen
1
typbez* bezeichner;

ändert das *-Symbol plötzlich alle seine Eigenschaften: Aus dem Präfix
wird ein Postfix, und aus der Bedeutung "dereferenziere" wird "Zeiger
darauf". Und der Versuch, diese Schreibweise auf Funktions- oder
Array-Zeiger anzuwenden,
1
typbez (*bezeichner)(void);
2
typbez (*bezeichner)[4];

ist vollends zum Scheitern verurteilt, weil das Klammerpaar den * ganz
klar an 'bezeichner' und nicht an 'typbez' bindet. Da hilft auch das
Verschieben des Leerzeichens wenig.

Die Stroustrup-Schreibweise ist für mich einfach nur eine Vergewaltigung
der C-Syntax. Diese mag vielleicht hässlich sein, man muss sie deswegen
aber nicht auch noch zum Krüppel schlagen.

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> daß der Stern am Anfang des Bezeichners zum Dereferenzieren benutzt
> wird, also zum eigentlichen Erfüllen der Zeige-Funktion eines Zeigers.
> Aber bei der Deklaration eines Zeigers will man ja einen Zeiger
> vereinbaren und nicht einen Zeiger dereferenzieren.

Ja, und?  Deklaration und Benutzung sind zwei völlig verschiedene
Dinge.  Auch Pascal benutzt für beides ein @.  Dort ist nur die
Trennung des Typs vom Variablennamen dank des Doppelpunktes und der
anderen Reihenfolge übersichtlicher.  Außerdem kann man dort nur
Variablendeklarationen aneinanderreihen, die alle den gleichen Typ
benutzen.

D. V. schrieb:
> Wer C mit dem antiqiertem Overhead nicht klarkommt, und das scheinen die
> meisten Newcomer hier zu sein, der orientiere sich bitte anderwärtig. Es
> bibt genug Alternativen, als sich mit den antiquaren-Make-Files
> runzuärgern.

Wer C und Makefiles durcheinanderwürfelt, mit dem muss man eigentlich
hier nicht weiter diskutieren.  Das einzige, was diese beiden Sprachen
gemeinsam haben, ist der Zuweisungsoperator "=".

Mit dem ursprünglichen Thema hat das alles sowieso nichts mehr zu tun.

von markus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hi, koenntet Ihr ein paar Links auf die C Standarts posten, damit man 
genau nachlesen kann?

von Nase (Gast)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Abgesehen davon frage ich mich, wozu solche Konstrukte wie
>
>  char* str = "abc";
>
> überhaupt hingeschrieben werden. Ein
>
>  const char str[] = {"abc"};

Weil das erste Konstrukt etwas völlig anderes bedeutet, als das zweite?

von Ralf G. (ralg)


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> TriHexagon schrieb:
>> Wenn dann schon richtig richtig:
>
> richtig richtig richtig wäre es, wenn man den Rückgabewert von malloc()
> prüfen würde und entsprechend ragiert ....

... und zum Schluss wieder frei gibt.

von Hans (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Die Stroustrup-Schreibweise ist für mich einfach nur eine Vergewaltigung
> der C-Syntax. Diese mag vielleicht hässlich sein, man muss sie deswegen
> aber nicht auch noch zum Krüppel schlagen.

In C++ ist die C-Syntax ja leider nicht konsequent weitergeführt worden, 
denn bei einer Referenz gilt diese "implizite Schreibweise" nicht mehr:
1
typbez &bezeichner;
Wenn ich im Code &bezeichner schreibe, ergibt sich ein typbez* und nicht 
ein typbez.

Abgesehen von Mehrfachdeklarationen, auf die man problemlos verzichten 
kann, gibt es als C++-Programmierer aus meiner Sicht keinen Grund, die 
Schreibweise
1
typbez *bezeichner;
gegenüber
1
typbez* bezeichner;
zu bevorzugen.

Man braucht die Typen ja nicht nur bei alleinstehenden 
Variablendeklarationen, sondern z.B. auch als Templateparameter oder bei 
Casts. Da ist imo
1
typbez* bezeichner = (typbez*) bezeichner2;
besser lesbar als ein
1
typbez *bezeichner = (typbez *) bezeichner2;

Da es wie gesagt in C++ für mich keinen logischen Grund für oder gegen 
eine der Schreibweisen gibt, nehme ich die besser lesbare und benutze 
sie auch in "reinem C".

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
markus schrieb:
> Hi, koenntet Ihr ein paar Links auf die C Standarts posten, damit man
> genau nachlesen kann?

Die Standards gibts gegen Einwurf kleiner Münzen. Beliebter sind daher 
Entwürfe dazu, die dem ziemlich nahe kommen.

Zu C99:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf

Zu C11:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf‎

: Bearbeitet durch User
von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hans schrieb:
> Kernighan, Ritchie 2.Auflage, Seite 84:
> The declaration of the pointer ip,
>
>    int *ip;
>
> is intended as a mnemonic; it says that the expression *ip is an int.
>
> und das ist für mich logischer.

Ich kenne die Argumentation. Ob das nun logischer ist, ist wohl 
Ansichtssache. Für mich heißt das Sternchen dort schlicht "Pointer auf 
das, was links davon steht". int ist ein Integer, int* ist ein 
Integer-Pointer. Und da der Typ eben "Integer-Pointer" und der Name "ip" 
ist und nicht etwa der Typ "Integer" und der Name "Pointer-ip", gehört 
der Stern zum int. Er ist Teil des Typs und nicht Teil des Namens.
Für mich hat der Stern hier eine ganz andere Bedeutung als bei *ip = 5. 
Einmal heißt es "mach einen Pointer auf den Typ, der links davon steht" 
und einmal heißt es "dereferenziere das, was rechts davon steht".

A. K. schrieb:
> Die Syntax der Deklarationen sollte wie die Verwendung in Expressions
> aussehen. So hatte Ritchie sich das ursprünglich gedacht. Das Ergebnis
> war und ist freilich einfach nur eine nackte Katastrophe. Mit der wir
> seit Jahrzehnten leben müssen.

Da gibts ja so einige Sachen, die sie für intuitiv gehalten haben, über 
die aber tatsächlich inzwischen etliche Generationen von 
C-Programmierern beim Erlenen der Sprache reihenweise gestolpert sind.

Yalu X. schrieb:
> müsste man konsequenterweise bei Array- und Funktionsdeklarationen die
> Größenangabe bzw. die Argumentliste ebenfalls direkt hinter das "char"
> schreiben, also nicht so
> char array[4];
> char function(int n);
>
> sondern so:
> char[4] array;
> char(int n) function;

Das sieht aus heutiger Sicht natürlich etwas gewöhnungsbedürftig aus, 
wäre aber meiner Ansicht nach besser gewesen.

Dr. Sommer schrieb:
> Auch wenn man sowas sieht:const int& (*f) (const int* [7], int* (&y)
> [7]);sofort absolut klar was gemeint ist?!

Die Definition der Standard-C-Funktion signal() sieht ohne Zuhlifenahme 
einer Typedef-Krücke auch schon nett aus:
1
void ( *signal(int signum, void (*handler)(int)) ) (int);

W.S. schrieb:
> milliardenfach immer wieder Arrays nach einem Nullbyte durchsucht, immer
> wieder und wieder. Und selbst bei konstanten Literalen, wo man deren
> Size bereits zur Compilezeit hätte festlegen können. strlen läßt
> milliardenfach grüßen.

Also wenn ein Compiler das nicht optimiert bekommt, such dir einen 
anderen.

Yalu X. schrieb:
> Bei der Stroustrup-Schreibweise hingegen
> typbez* bezeichner;
>
> ändert das *-Symbol plötzlich alle seine Eigenschaften: Aus dem Präfix
> wird ein Postfix, und aus der Bedeutung "dereferenziere" wird "Zeiger
> darauf".

Das war er schon immer. Bei der Definition wird nichts dereferenziert, 
sondern das Sternchen sagt dem Compiler, daß er einen Zeiger machen muß. 
Eine Dereferenzierung ist hier nur hypothetisch vorhanden a la "was 
wäre, wenn ich dereferenzieren würde".

markus schrieb:
> Hi, koenntet Ihr ein paar Links auf die C Standarts posten, damit man
> genau nachlesen kann?

http://www.iso.org/iso/home/store/catalogue_tc/catalogue_detail.htm?csnumber=57853
http://webstore.ansi.org/RecordDetail.aspx?sku=ISO%2fIEC+9899%3a2011

Yalu X. schrieb:
> Die Stroustrup-Schreibweise ist für mich einfach nur eine Vergewaltigung
> der C-Syntax. Diese mag vielleicht hässlich sein, man muss sie deswegen
> aber nicht auch noch zum Krüppel schlagen.

Das hat C schon selbst hinbekommen. Bei einem konstanten Zeiger paßt die 
Sache dann nämlich auch schon nicht mehr:

int i;
int * const ip = &i;

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Das sieht aus heutiger Sicht natürlich etwas gewöhnungsbedürftig aus,
> wäre aber meiner Ansicht nach besser gewesen.

Linear statt mit einer verschachtelten Kombination aus Präfix und 
Postfix Operatoren und Operator-Vorrang. Vorzugsweise indem man mit der 
Variablen anfängt und sich zum Grundtyp durchschlägt, orientiert an 
Pascal
  var a: array [10] of pointer to integer;
oder wenns sein muss kryptischer als
  auto a [10]*int;

Der C Philosophie folgend wärs die umgekehrte Reihenfolge:
  int*[10]a;
die allerdings mit dem Problem kämpft, dass wie im real existierenden C 
der Typname als Keyword für den Anfang einer Deklaration steht 
(ersatzweise extern/static/...). Am Anfang war das harmlos, bis typedef 
erfunden wurde und der Logik hinter der Syntax einen Tritt in den 
Allerwertesten verpasste. Mit obligatorischer storage class hätte man 
dies immer noch retten können, also
  auto int*[10]a;

Wobei da aber immer noch die für die erste Übersicht des Quellcodes
wichtige Schlüsselinformation bestens versteckt wird, nämlich der Name. 
Structs so zu schreiben, dass sie sauber aussehen, ist ja nicht grad 
einfach. Je länger der Typ wird, desto weiter rechts rutscht der Name, 
was schaurig aussieht, wenn man die Namen streng untereinander schreiben 
will.

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


Bewertung
0 lesenswert
nicht lesenswert
D. V. schrieb:
> Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt
> nicht nach Syntax-Unklarheiten.

Kannst es auch anders ausdrücken: Wer zählt schon all die Verletzten und 
Leichen, die unterwegs beim Versuch, in C und erst recht in C++ voll 
durchzublicken, aus den diversen Kurven flogen.

Ironisch ist das deshalb, weil diese Verschwurbelung der Typen der 
Motivation entsprang, es dem Anwender einfach zu machen. Aber "gut 
gemeint" ist bekanntlich das Gegenteil von "gut gemacht".

Übrigens wirst du hier im Forum feststellen können, dass nicht nur 
Anfänger über C stolpern, sondern auch Fortgeschrittene und Experten 
sich über mache Aspekte trefflich streiten und Neues lernen können.

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


Bewertung
0 lesenswert
nicht lesenswert
D. V. schrieb:
> Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt
> nicht nach Syntax-Unklarheiten.

Wo wir grad bei C++ sind. Was bedeutet eigentlich
  class T { ... };
  T(a);
Ist das
(1) die Deklaration der Variablen a vom Typ T,
(2) oder der Aufruf eines Konstruktors von T,
und weshalb?

Beides ist etwas unperfekt. In (1) sind die Klammern unnötig und in (2) 
fliegt das Ergebnis gleich wieder weg. Aber beide Interpretationen sind 
von der Syntax her zulässig.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> D. V. schrieb:
>> Egal ob C, oder C++ - wer diese Programmiersprachen beherrscht, fragt
>> nicht nach Syntax-Unklarheiten.
>
> Wo wir grad bei C++ sind. Was bedeutet eigentlich
>   class T { ... };
>   T(a);
> Ist das
> (1) die Deklaration der Variablen a vom Typ T,
> (2) oder der Aufruf eines Konstruktors von T,
> und weshalb?

Es handelt sich um die Definition einer Variablen. Bei der Erzeugung 
wird automatisch der Konstruktor aufgerufen. Explizit einen Konstruktor 
aufrufen kann man in C++ nicht. Er wird immer als Teil der 
Objekterzeugung implizit aufgerufen.
Interessanter finde ich diesen Fall:
1
T t(x);

Ist das die Definition eines Objekts vom Typ T, das mit einer Variablen 
namens x initialisiert wird oder die Deklaration einer Funktion, die ein 
T zurückgibt und einen Parameter von Typ x erhält?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Explizit einen Konstruktor aufrufen kann man in C++ nicht.

Aber sicher kann man das.
1
struct T { T(int); int operator+ (T); };
2
int f(int a, int b)
3
{
4
  return T(a) + T(b);
5
}
enthält 2 Konstruktoraufrufe. Von mir aus kannst du das freilich auch 
die Erzeugung von 2 temporären Variablen mit Konstruktoraufruf nennen.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Rolf Magnus schrieb:
>> Explizit einen Konstruktor aufrufen kann man in C++ nicht.
>
> Aber sicher kann man das.struct T { T(int); int operator+ (T); };
> int f(int a, int b)
> {
>   return T(a) + T(b);
> }
> enthält 2 Konstruktoraufrufe. Von mir aus kannst du das freilich auch
> die Erzeugung von 2 temporären Varablen

temporären Objekten

> mit Konstruktoraufruf nennen.

So nenne ich es. So nennt es auch ISO C++.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Egal wie umständlich du das nennst,
  T(a);
ist folglich syntaktisch(!) eine gültige expression, wie auch
  int(a);
oder
  a;

Dass es eine Deklaration ist, ist reine Vereinbarung. Weil es in C 
eindeutig ist.

> temporären Objekten

Stimmt.

> Interessanter finde ich diesen Fall:

Auch nicht übel, weil komplett ohne Redundanz. Es wird aber eindeutig, 
weil C++ wie C Typnamen nicht als normale Identifier behandeln kann und 
damit von x bereits auf syntaktischer Ebene bekannt ist, ob es ein 
Typname ist oder nicht.

Die lexikalische Klassifizierung von Namen in nomale Identifier und in 
Typnamen ist in C und C++ sowohl von der Bedeutung des Namens als auch 
vom syntaktischen Kontext abhängig. Das ist nix für Freunde sprachlicher 
Eleganz.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Egal wie umständlich du das nennst,
>   T(a);
> ist folglich syntaktisch(!) eine gültige expression, wie auch
>   int(a);
> oder
>   a;
>
> Dass es eine Deklaration ist, ist reine Vereinbarung.

Nein, denn es wird schließlich der Speicher für das Objekt zur Verfügung 
gestellt, und das macht nicht der Konstruktor. Der initialisiert es nur 
bzw. anderes ausgedrückt: Seine Aufgabe ist es, aus dem ihm zur 
Verfügung gestellten rohen Speicher ein Objekt zu machen. Und die 
einzige Möglichkeit, diese beiden Aktionen überhaupt getrennt 
auszuführen, ist "placement new".

> Auch nicht übel, weil komplett ohne Redundanz. Es wird aber eindeutig,
> weil C++ wie C Typnamen nicht als normale Identifier behandeln kann und
> damit von x bereits auf syntaktischer Ebene bekannt ist, ob es ein
> Typname ist oder nicht.

Spätestens bei Templates und von template-Parametern abhänigen Namen muß 
C++ dann aber doch kapitulieren:
1
template <typename T>
2
void func()
3
{
4
    T t(T::x);
5
}

Um zu kennzeichnen, daß x ein Typ sein soll, muß man dem Compiler auf 
die Sprünge helfen:
1
    T t(typename T::x);

Und bei Templates gibt's noch viel lustigere Sachen. Da ist C echt 
Kinderkram dagegen.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> einzige Möglichkeit, diese beiden Aktionen überhaupt getrennt
> auszuführen, ist "placement new".

Ich glaube wir reden aneinander vorbei. Mir ging es nur darum, dass 
"T(a);" syntaktisch auch eine gültige expression ist. Das aber ist 
völlig unabhängig davon, ob du Objekterzeugung und Initialisierung 
getrennt betrachtest oder vereinfachend zusammenziehst, weshalb mich 
dieser spezielle Aspekt nicht interessierte.

> Und bei Templates gibt's noch viel lustigere Sachen. Da ist C echt
> Kinderkram dagegen.

Yep.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Ich glaube wir reden aneinander vorbei. Mir ging es nur darum, dass
> "T(a);" syntaktisch auch eine gültige expression ist. Das aber ist
> völlig unabhängig davon, wie man das Kind tauft.

Das hab ich schon verstanden. Aber du hast auf meine Aussage, daß man 
Konstruktoren nicht explizit aufrufen kann, geantwortet, daß man das 
sehr wohl könne. Was du angegeben hast, ist natürlich syntaktisch 
korrekt, aber es ist kein expliziter Konstruktor-Aufruf, sondern die 
Erzeugung eines Objekts, was natürlich unter anderem einen 
Konstruktor-Aufruf beinhaltet, aber eben nicht auf diesen beschränkt 
ist.

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. (prx) schrieb:

> Übrigens wirst du hier im Forum feststellen können, dass nicht nur
> Anfänger über C stolpern, sondern auch Fortgeschrittene und Experten
> sich über mache Aspekte trefflich streiten und Neues lernen können.

Den Eindruck habe ich auch. Dass alleine die Zuordnung des Asterisk (zur 
Variablen oder zum Datentyp) so viel Diskussionsbedarf auslöst ...

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus (Gast) schrieb:

W.S. schrieb:
>> milliardenfach immer wieder Arrays nach einem Nullbyte durchsucht, immer
>> wieder und wieder. Und selbst bei konstanten Literalen, wo man deren
>> Size bereits zur Compilezeit hätte festlegen können. strlen läßt
>> milliardenfach grüßen.

> Also wenn ein Compiler das nicht optimiert bekommt, such dir einen
> anderen.

Naja, ich finde sein Argument durchaus nachvollziehbar. Auch ein 
optimierter Vorgang wird das Nullbyte nicht "erraten" können, sondern 
muss danach suchen und das immer und immer wieder. Pascal hat die Anzahl 
der Zeichen im ersten Byte gespeichert (zumindest im Ur-Pascal; 
Inzwischen gibts auch andere Ausführungen des Stringtyps). Was war daran 
so schlecht, außer dass sie dafür leider nur ein Byte spendiert hatten 
und Strings damit nur 255 Zeichen lang sein konnten? Warum nicht die 
Länge gleich mit ablegen, z.B. in den ersten 4 Bytes? Das müsste doch 
einiges an Ausführungszeit bei der Verarbeitung von Zeichenketten 
einsparen können oder nicht? Gibt es Programmiersprachen die so 
vorgehen?

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Gibt es Programmiersprachen die so
> vorgehen?
Alle außer C... C++, ruby, PHP, Python, Java, etc. etc. ... insbesondere 
alle OOP-Sprachen, in denen man sich leicht seine eigene String-Klasse 
bauen kann, die die Länge und alles elegant kapselt. In diesen Sprachen 
kann man dann auch typischerweise sowas schreiben:
1
String str1 = "abc";
2
String str2 = "def";
3
String str3 = str1 + "foobar" + str2;
Was in C nicht das bewirkt was man vorhatte...

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Warum nicht die Länge gleich mit ablegen, z.B. in den ersten 4 Bytes? Das
> müsste doch einiges an Ausführungszeit bei der Verarbeitung von
> Zeichenketten einsparen können oder nicht?

Kommt drauf an, wie of man wirklich die Länge braucht. Beim bloßen 
Iterieren durch den String kann die C-Methode sogar etwas effizienter 
sein. Generell wäre es nicht nur bei Strings, sondern allgemein bei 
Arrays nicht verkehrt gewesen, die Länge gleich mitzugeben. Das ist 
nämlich ein weiterer großer Stolperstein für C-Anfänger. Ich weiß gar 
nicht, wie oft ich schon so eine Frage in Foren und Newsgroups gelesen 
habe:

"Ich wollte die Größe des Arrays mit sizeof ermitteln, aber bekomme 
immer 4 raus, obwohl das Array 100 Elemente hat:
1
void func(int arr[])
2
{
3
    printf("%d\n", (int)sizeof(arr));
4
}
". Gefühlt gehört sie zu den Top-Fragen von C-Anfängern überhapt. Hätte 
man aber die Länge mit ins Array verpackt, wäre natürlich die 
fast-Gleichbehandlung von Arrays und Zeigern generell nicht mehr zu 
halten gewesen. Das hätte der Klarheit der Sprache aber vermutlich sogar 
geholfen.
Der Zusammenhang (und auch die Unterschiede) zwischen Arrays und Zeigern 
gehören zu den am schwersten zu verstehenden Kapiteln beim Erlernen von 
C.

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


Bewertung
-1 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Auch ein optimierter Vorgang wird das Nullbyte nicht "erraten" können

Doch, bei einem Stringliteral schon.

von Skeptiker (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Jörg Wunsch (dl8dtl) (Moderator) schrieb:

Skeptiker schrieb:
>> Auch ein optimierter Vorgang wird das Nullbyte nicht "erraten" können

> Doch, bei einem Stringliteral schon

Erklär bitte mal ..

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Skeptiker schrieb:
>>> Auch ein optimierter Vorgang wird das Nullbyte nicht "erraten" können
>> Doch, bei einem Stringliteral schon
> Erklär bitte mal ..

In
  char *s = "abc";
  int n = strlen(s);
kann ein Compiler, der strcmp verinnerlicht hat, ohne grosse Not direkt
  int n = 3;
draus machen, wenn sich s nicht zwischendrin ändert.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Der Compiler kann z.B. strlen ("hallo") durch 5 ersetzen wie im 
folgenden Beispiel:
1
#include <string.h>
2
3
static inline int
4
starts_with (const char *str, const char *prefix)
5
{
6
    return 0 == strncmp (str, prefix, strlen (prefix));
7
}
8
9
extern void hallo (void);
10
extern void bye (void);
11
12
void process_string (const char *str)
13
{
14
    if (starts_with (str, "hallo"))
15
        hallo();
16
    else if (starts_with (str, "bye"))
17
        bye();
18
}

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
PS: Aus
  int f(void)
  {
    char *s = "abc";
    return strlen(s);
  }
macht avr-gcc
  ldi r24,lo8(3)
  ldi r25,0
  ret

: Bearbeitet durch User
von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. (prx) schrieb:

Skeptiker schrieb:
>>> Auch ein optimierter Vorgang wird das Nullbyte nicht "erraten" können
>> Doch, bei einem Stringliteral schon
> Erklär bitte mal ..

> In
>  char *s = "abc";
>  int n = strlen(s);
> kann ein Compiler, der strcmp verinnerlicht hat, ohne grosse Not direkt
>  int n = 3;
> draus machen, wenn sich s nicht zwischendrin ändert.

Liegts etwa nur am eingeschalteten Debugger, dass trotz eingeschalteter 
Optimierung beim Aufruf von strlen zig mal ein 'byte ptr [edx+eax],0' 
ausgeführt wird?

So jedenfalls finde ich das nicht gerade effizient.

;)

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
cmp byte ptr [edx+eax],0

meinte ich.

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


Bewertung
-1 lesenswert
nicht lesenswert
1
$ cat > foo.c
2
#include <string.h>
3
4
int
5
main(void)
6
{
7
  return strlen("foo");
8
}
9
$ gcc -O -S foo.c 
10
$ cat foo.s
11
        .file   "foo.c"
12
        .text
13
        .globl  main
14
        .type   main, @function
15
main:
16
.LFB0:
17
        .cfi_startproc
18
        movl    $3, %eax
19
        ret
20
        .cfi_endproc
21
.LFE0:
22
        .size   main, .-main
23
        .ident  "GCC: (FreeBSD Ports Collection) 4.7.3 20130302 (prerelease)"
24
        .section        .note.GNU-stack,"",@progbits

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
@ Jörg Wunsch (dl8dtl) (Moderator)

Mir ist schon klar, dass ein C-Compiler bereits beim Übersetzen 
ermitteln kann wie lang ein String Literal ist und dann einfach die 
Länge nach eax lädt und mit einem ret die Funktion beendet. Ich hab nur 
spassenshalber das mal in der IDE vom PellesC laufen lassen (debug mode) 
und siehe da, er klappert Zeichen für Zeichen ab, führt jedesmal ein 
compare aus, vergleicht mit jne usw. Trotz eingeschalterer Optimierung. 
Und so schlecht ist der Pelles C Compiler nicht (auch wenn er nicht 
gegen den gcc ankommt).

Oder ist das in der Release anders?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Ein Compiler darf das optimieren, aber er muss nicht.
Von PellesC habe ich noch nie gehört.

gcc tut es, clang ebenso. Pelles offenbar nicht.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
-1 lesenswert
nicht lesenswert
Skeptiker schrieb:
> Oder ist das in der Release anders?

Da musst du schon deinen Compiler befragen.

GCC kann auch im Debugmodus (fast) alle Optimierungen aktivieren.
Andere Compiler können das ggf. nicht.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Von Pelles C ist zu lesen, dass der vom Princeton LCC abstammt. Und der 
hat grad mal gute 10K Zeilen Quellcode. Viel Optimierung würde ich von 
dem nicht erwarten. Da ist strlen schlicht eine Funktion wie jede 
andere.

: Bearbeitet durch User
von lalala (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Explizit einen Konstruktor
> aufrufen kann man in C++ nicht.

Und das ist doch auch gut so! Es kommt ja gar nicht so selten vor, dass 
in einem Konstruktor dynamisch Speicher angefordert wird, der mit dem 
Destruktor wieder freigegeben wird. Und wenn jetzt jeder Konstruktor so 
geschrieben werden müsste, dass es sicher wäre ihn ein 2tes Mal aufrufen 
zu können ....

von Skeptiker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. (prx) schrieb:

> Von Pelles C ist zu lesen, dass der vom Princeton LCC abstammt. Und der
> hat grad mal gute 10K Zeilen Quellcode. Viel Optimierung würde ich von
> dem nicht erwarten. Da ist strlen schlicht eine Funktion wie jede
> andere.

Pelle Orinius hat da schon einiges reingepackt über die Jahre

http://www.smorgasbordet.com/pellesc/about.htm

den Debugger z.B. finde ich klasse und die IDE sowieso. Ist halt ein 
reiner C Compiler, also nix mit C++, ist schnell installiert und frei 
verwendbar. Ideal um eben mal schnell was in C auszuprobieren, gerade 
wenn man sich noch in der ewigen "C-Lernphase" befindet oder auch weil 
er mehr kann (C99, C11), als der C-Compiler vom VisualC. Und er ist 
schnell installiert.

http://www.smorgasbordet.com/pellesc/index.htm

Jörg Wunsch (dl8dtl) (Moderator) schrieb:

> GCC kann auch im Debugmodus (fast) alle Optimierungen aktivieren.
> Andere Compiler können das ggf. nicht.

Aktiviert hatte ich die Compileroption schon. Das geht in der IDE sehr 
schön übersichtlich. Der Unterschied war auch zu merken, aber so wie der 
gcc hat er sich dennoch nicht verhalten. Naja, ist nicht schlimm. Der VC 
C++ 2008 Express hat mir die Optimierung sogar verweigert, weil sie sich 
(laut Fehlermeldung) mit einer anderen Optimierung nicht verträgt. Muss 
erst mal schauen woran das jetzt wieder genau liegt.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
lalala schrieb:
> Und das ist doch auch gut so! Es kommt ja gar nicht so selten vor, dass
> in einem Konstruktor dynamisch Speicher angefordert wird, der mit dem
> Destruktor wieder freigegeben wird. Und wenn jetzt jeder Konstruktor so
> geschrieben werden müsste, dass es sicher wäre ihn ein 2tes Mal aufrufen
> zu können ...
Kann man eben doch, wie oben angedeutet, mit Placement new. Den 
Destruktor im übrigen auch. Schlau ist das natürlich im allgemeinen 
nicht; manchmal ists es aber gut für Optimierungen und "Low-Level" 
Container - eine typische Implementation für z.B. std::vector verwendet 
so etwas um blockweise Speicher allokieren zu können.

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> eine typische Implementation für z.B. std::vector verwendet
> so etwas um blockweise Speicher allokieren zu können.

Alle Standard-Container nutzen das, da die ihren Speicher immer über 
Allokatoren anfordern. Die kümmern sich aber eben nur um den Speicher.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Alle Standard-Container nutzen das, da die ihren Speicher immer über
> Allokatoren anfordern. Die kümmern sich aber eben nur um den Speicher.
Stimmt ja, bei der Verwendung von Allokatoren bleibt einem ja gar nichts 
anderes übrig.

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
>> Auch ein optimierter Vorgang wird das Nullbyte nicht "erraten" können
>
> Doch, bei einem Stringliteral schon.

Jörg, du hast die Sache WIRKLICH NICHT verstanden.
Sinn und Zweck von Strings aller Art ist deren Verwendung - und nicht 
die Rückgabe eines einzelnen strlen Ergebnisses wie in deiner 
Demonstration.

Normalerweise werden Strings als Argumente weitergegeben oder aneinander 
gereiht oder sonstwie verarbeitet. Da ist dann immer strlen mit von der 
Partie, entweder direkt im Usercode oder innerhalb strcat und Konsorten.

ich mach dir mal nen Vorschlag:

Programmiere doch mal in C ein Äquivalent zu sowas

const
  foo1 = 'hallo';
  foo2 = ' du da'#13#10;

procedure Drucke_Aus ( S: String);
begin
  ...
end;

begin
  Drucke_Aus(foo1+foo2);
end.

Und dann unterhalten wir uns über die Anzahl der expliziten und/oder 
impliziten Aufrufe von strlen. OK?

W.S.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Und was soll das jetzt? Wenn die Länge nicht bekannt ist, kann kann die 
Compiler sie auch nicht per Kristallkugel bestimmen.  Glaubst du, Jörg 
wäre das nicht klar?

Wenn dir sein minimales Beispiel nicht zur Demonstration recht, was 
gemeint war: Ich habe ein Beispiel gegeben, was etwas näher an der 
Praxis ist: Testen eines Strings auf eine bestimmte Prefix.

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


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Jörg, du hast die Sache WIRKLICH NICHT verstanden.

Und du hast meine Antwort aus dem Kontext gerissen, in dem sie
gegeben war.  Was soll's also?  Wolltest du einfach nur unbedingt
was erwidern?

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Sinn und Zweck von Strings aller Art ist deren Verwendung - und nicht
> die Rückgabe eines einzelnen strlen Ergebnisses wie in deiner
> Demonstration.

strlen ist nur das einfachste Beispiel. Ein etwas besseres Beispiel:
  strcpy(..., "abc");
Hier kann ein Compiler, der strcpy verinnerlicht hat, den String 
kopieren ohne zur Laufzeit nach dem Ende suchen zu müssen.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Programmiere doch mal in C ein Äquivalent zu sowas

Hab ich grad getan:
1
#include <stdlib.h> 
2
#include <string.h> 
3
4
static const char *foo1 = "hallo";
5
static const char *foo2 = " du da\n";
6
7
void print(const char *);
8
9
int main(void)
10
{
11
  char *buf = alloca(strlen(foo1) + strlen(foo2) + 1);
12
  strcpy(buf, foo1);
13
  strcat(buf, foo2);
14
  print(buf);
15
  return 0;
16
}

Sämtliche String-Funktionen verschwinden bei gcc -O2:
1
main:
2
  pushq  %rbp
3
  movabsq  $2921832436294688, %rax
4
  movq  %rsp, %rbp
5
  subq  $32, %rsp
6
  leaq  15(%rsp), %rdi
7
  andq  $-16, %rdi
8
  movq  %rax, 5(%rdi)
9
  movl  $1819042152, (%rdi)
10
  movb  $111, 4(%rdi)
11
  call  print
12
  xorl  %eax, %eax
13
  leave
14
  ret
Die Strings selbst verschwinden übrigens auch, d.h. sind nur in den 
Immediates wiederzufinden.

Damit du nicht ganz leer ausgehst, lässt clang dir ein strlen übrig:
1
main:                                   # @main
2
  pushq  %rbp
3
  movq  %rsp, %rbp
4
  pushq  %rbx
5
  subq  $24, %rsp
6
  movw  $111, -20(%rbp)
7
  movl  $1819042152, -24(%rbp)  # imm = 0x6C6C6168
8
  leaq  -24(%rbp), %rbx
9
  movq  %rbx, %rdi
10
  callq  strlen
11
  movabsq  $2921832436294688, %rcx # imm = 0xA616420756420
12
  movq  %rcx, -24(%rbp,%rax)
13
  movq  %rbx, %rdi
14
  callq  print
15
  xorl  %eax, %eax
16
  addq  $24, %rsp
17
  popq  %rbx
18
  popq  %rbp
19
  ret

: Bearbeitet durch User
von Xeraniad X. (xeraniad)


Bewertung
0 lesenswert
nicht lesenswert
(Nicht unterbrechen lassen, hier folgt eine Zwischenbemerkung zum Thema 
Geschichte zu Zeichenketten und PASCAL.)

Es wurden zuvor Vorzüge bei Pascal bezüglich explizit gespeicherter 
String-Länge im ersten Byte erwähnt.

Im "Ur" -PASCAL (z. B. 1974, 1984, ISBN 3-540-96048-1) gab es nur 
"PACKED ARRAY [1 .. ?] OF CHAR", also Zeichenketten fester Länge 
[üblicherweise mit Leerzeichen am ungenutzen rechten Ende {space 
padded}, kompatibel zu z. B. COBOL-Datentyp "PIC X(?)" oder alte 
FORTRAN: "CHARACTER"...], also ohne explizit gespeicherte Länge. Dies 
war gut für konstanten Offset in Records {Lochkarten, Files}. {Früher 
verschwendete man Speicher durch Leerzeichen, heute mit XML.}
Das String-Konzept fehlte gemäss meiner Ansicht in dieser Sprache Pascal 
eher noch mehr als bei C.
Für Konvertierungen oder Datenbank-Importe mussten diese trailing spaces 
(von rechts her) abgeschitten werden. Dies erinnert an die übliche 
C-Suche (von links her) nach dem '\0'-Terminator bei C.

Praktischerweise, aber ohne standard, fügten Hersteller Erweiterungen 
(auch mit unsicheren Konkatenationsoperatoren) hinzu, z. B. wie folgt.
° Borland mit Turbo Pascal, Datentyp "STRING" (== "PACKED ARRAY [*0* .. 
255] OF CHAR"), erstes Byte speichert explizite Länge, die Kapazität war 
255.
° VAX-PASCAL bot zusätzlich einen eigenen Datentyp "STRING" an.

Wirth nutzte mit MODULA-2 ebenfalls den CHR(0)-Terminator (sofern das 
letzte verfügbare Zeichen nicht besetzt ist) und bot String-Operationen 
in einem Standard-Modul an.

Generell: Für mich ist die String-Geschichte nicht abgeschlossen. Wenn 
ich mitbekomme, welch unbeachteter Wirbelwind in Java manchmal im 
Hintergrund bei String-Operationen stattfindet, wundert es mich nicht, 
dass dann gelegentlich von Perfomance-Problemen berichtet wird (-> 
"StringBuilder" verwendet?).

OK, rechte Klammer geschlossen zu diesem Thema :).
schoenen Abend


EDIT:
Hi *A. K.*, wollte Deinen interessanten Beitrag nicht unterbrechen, hab 
gerade an meinem lange rumgeschrieben und dies abgesendet, dann Deinen 
Beitrag inzwischen erst gesehn. Deine Tipps werd ich bereits morgen in 
meiner eigenen C-Umgebung anwenden können, Danke!

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


Bewertung
0 lesenswert
nicht lesenswert
Xeraniad X. schrieb:
> Im "Ur" -PASCAL (z. B. 1974, 1984, ISBN 3-540-96048-1) gab es nur
> "PACKED ARRAY [1 .. ?] OF CHAR", also Zeichenketten fester Länge

Noch schöner: Die Referenzimplementierung hatte eine Vorliebe für den 
Typ ALFA, vordefiniert als PACKED ARRAY [1..10] OF CHAR. Grund: 6-Bit 
Zeichensatz und eine 60-Bit CDC6600.

von Xeraniad X. (xeraniad)


Bewertung
0 lesenswert
nicht lesenswert
Danke @ A. K. für den Hinweis auf diese historische Architektur 
http://en.wikipedia.org/wiki/CDC_6600 !
"... The Central Processor had eight general purpose 60-bit registers X0 
through X7, ...".  Ein CERN-User wird damals kreativ inspiriert worden 
sein.

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


Bewertung
0 lesenswert
nicht lesenswert
Xeraniad X. schrieb:
> Danke @ A. K. für den Hinweis auf diese historische Architektur

Wenns interessiert: Eine bis runter auf Transistorebene gehende 
Beschreibung gibts in 
http://www.bitsavers.org/pdf/cdc/cyber/books/DesignOfAComputer_CDC6600.pdf

von Karl H. (kbuchegg) (Moderator)


Bewertung
1 lesenswert
nicht lesenswert
Wer seine C bzw. C++ Kentnisse an diversen Bugs überprüfen und schärfen 
möchte, dem lege ich die Site
http://www.gimpel.com/html/bugs.htm
nahe.
Jede Menge kurze Codestücke, mit einer kleinen Fehlerbeschreibung. Jeder 
Bug dreht sich um irgendeine Besonderheit, die man gerne übersieht.
Darunter dann immer die Lösung in der Form, wie sie von der von gimpel 
verkauften Lint Lösung gemeldet wird.
Also: nicht schummeln - erst selber über dem Code brüten und dann 
nachsehen, ob man richtig lag.
Mit einem meinen Ex-Kollegen war das lange Zeit ein monatlicher 
'Wettkampf': Wer findet als erster die Problemstelle und kann auch 
begründen, worin das Problem liegt?

von Kindergärtner (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Karl Heinz schrieb:
> Mit einem meinen Ex-Kollegen war das lange Zeit ein monatlicher
> 'Wettkampf': Wer findet als erster die Problemstelle und kann auch
> begründen, worin das Problem liegt?
Das Problem ist da wohl oft der verkorkste Code in dem sich hervorragend 
Bugs verstecken lassen... Würde man von vorneherein ordentlich Coden 
sähe das oft anders aus.

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Kindergärtner schrieb:
> Würde man von vorneherein ordentlich Coden
> sähe das oft anders aus.

Ja, oh ja,
dein Wort in die Ohren all der immer wieder nachwachsenden 
Programmier-Schüler!

Aber die Realität sieht ganz anders aus.
Siehe:

  for (const char * p = "Das ist ein Test!\n" ; c = *p; p++) {
    SPI.transfer (c);
  }

(heute hier gefunden: "Beitrag "Verständnisproblem";)

A. K. schrieb:
> strcpy(..., "abc");
> Hier kann ein Compiler, der strcpy verinnerlicht hat, den String
> kopieren ohne zur Laufzeit nach dem Ende suchen zu müssen.

Du schreibst wirr. Kein Compiler kann zur Laufzeit irgend etwas tun - 
mit Ausnahme von Skriptsprachen, die ja sowieso jedesmal zur Laufzeit 
übersetzt werden müssen.

Ansonsten liegst du aber auch ein bissel daneben. Hätte ich nicht

  Drucke_Aus(foo1+foo2);

sondern

  Drucke_Aus(IntToStr(linenumber)+': '+foo1+IntToStr(aVariable)+foo2);

geschrieben, wäre der von unserem Mod erwähnte Sonderfall außen vor 
geblieben. Jörg neigt zu Kommentaren "ich kenne da aber einen 
Sonderfall, wo das alles viel einfacher geht.. weil ich schlauer bin..." 
- aber das ist eben ein Sonderfall und nicht die Regel.

Ansonsten haben wir ja das Thema C mal wieder soweit abgegrast und 
wissen mal wieder, was wir eigentlich schon lange gewußt haben: C hat 
mehr Stolpersteine als Features und jedes Jahr auf's neue fällt deswegen 
eine neuer Generation von Programmieranfängern auf die Nase. Bei manchen 
fällt es gleich auf, das ist gut, aber bei vielen fällt das erst viel 
später auf.

Wie geht's weiter? Wie würden wir uns wünschen, daß es weiterginge?
Die Inflation an Skriptsprachen in den letzten Jahren zeigt eigentlich 
ganz deutlich, daß es durchaus einen erheblichen Leidensdruck bei den 
Programmierern gibt, aber aus der eingetrichterten C-Denke kommen die 
Leute offenbar nicht wieder heraus.

Und? Weiter auf dem bewährten C-Weg? Mit immer mächtigerer Hardware 
immer komplexere Software weiter in C und seinen Geschmacksvarianten 
schreiben? In 100 Jahren immer noch strlen und Pointerdeklarationen, die 
die meisten Programmierer nicht mehr verstehen?

W.S.

von Kindergärtner (Gast)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Aber die Realität sieht ganz anders aus.
Augenkrebs... Ach ja, C++ ist schön:
1
std::string myString = "Cheese!";
2
for (char c : myString) {
3
  tuwas(c);
4
}

W.S. schrieb:
> Mit immer mächtigerer Hardware
> immer komplexere Software weiter in C und seinen Geschmacksvarianten
> schreiben? In 100 Jahren immer noch strlen und Pointerdeklarationen, die
> die meisten Programmierer nicht mehr verstehen?
Was anderes wird dem gemeinen E-Techniker ja nicht beigebracht, daher 
hat er keine Wahl...

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
>> Hier kann ein Compiler, der strcpy verinnerlicht hat, den String
>> kopieren ohne zur Laufzeit nach dem Ende suchen zu müssen.
>
> Du schreibst wirr. Kein Compiler kann zur Laufzeit irgend etwas tun

Also gut, die korrekte Fassung: Hier kann ein Compiler, der strcpy 
verinnerlicht hat, Code erzeugen, der den String kopiert ohne nach dem 
Ende suchen zu müssen.

> Ansonsten liegst du aber auch ein bissel daneben. Hätte ich nicht

Klar doch. Die C Stringdarstellung und -verarbeitung ist nicht wirklich 
gut. Aber ich hatte doch nur exakt das getan, was du vorgeschlagen 
hattest. ;-)

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> wäre der von unserem Mod erwähnte Sonderfall außen vor geblieben

Nochmal: schau dir den Kontext an, aus dem du meine Bemerkung heraus
gerissen hast.  Nur um den ging es, und nicht darum, was wo wie alles
noch anders sein kann.

Davon abgesehen, die Überschrift heißt "C", dein Beispiel kann gar
kein C sein.  Dass man mit C++-Klassen alles eine Portion komplexer
hat, ist logisch.  Aber auch da sind die Compiler mittlerweile nicht
so schlecht mit dem Optimieren.

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Nochmal: schau dir den Kontext an, aus dem du meine Bemerkung heraus
> gerissen hast.

Ach Jörg, du hast manchmal ne ausgesprochen rechthaberische Art drauf. 
Ja, ich habe den gesamten Kontext gelesen, habe ihn jedoch zwecks 
Platzersparnis nicht komplett zitiert - den restlichen Platz brauch ich 
üblicherweise für meinen eigenen Text...

Um es nochmal auf den Punkt zu bringen: Texte aller Art, auch konstante 
Texte, werden in fast allen Programmen heutzutage reichlich verwendet 
und ebenso reichlich werden die darin enthaltenen Chars nach dem 
Nullchar durchkämmt - immer wieder und immer wieder und zwar zur 
Laufzeit. Sämtliche hier angeführten Sonderfälle fristen ein 
bescheidenes Dasein weit abseits dessen, womit der Mainstream an 
Programmen die vorhandene Rechenleistung vergeudet. Ja, wir benutzen C 
auch weiterhin, weil es sich inzwischen zur Monokultur entwickelt hat 
und für die meisten Einsatzfälle keine wirkliche Alternative existiert. 
Und so werden auch dieses Jahr wieder massenweise Programmier-Eleven auf 
ihre Nasen fallen und mittels C schlechte Programme schreiben.

Aber das alles heißt noch lange nicht, diese Krücke auch noch schön zu 
finden oder sich zu bemühen, sie sich schönzusaufen..

Apropos: in ein paar Tagen ist mal wieder Weinmesse in Berlin. Ich 
meine, das ist zur Abwechslung mal ein angenehmeres Thema als sich über 
krötige Details von C zu streiten.

Prost

W.S.

von Hans (Gast)


Bewertung
2 lesenswert
nicht lesenswert
W.S. schrieb:
> Ach Jörg, du hast manchmal ne ausgesprochen rechthaberische Art drauf.

Ha, Ha... Alter .... W.S. Du machst Dich immer mehr lächerlich.

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Um es nochmal auf den Punkt zu bringen: Texte aller Art, auch konstante
> Texte, werden in fast allen Programmen heutzutage reichlich verwendet
> und ebenso reichlich werden die darin enthaltenen Chars nach dem
> Nullchar durchkämmt - immer wieder und immer wieder und zwar zur
> Laufzeit.

Um es mal auf den Punkt zu bringen: Na und?

Oliver

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


Bewertung
0 lesenswert
nicht lesenswert
Xeraniad X. schrieb:
> Früher
> verschwendete man Speicher durch Leerzeichen, heute mit XML.

Jepp.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Oliver schrieb:
>> und ebenso reichlich werden die darin enthaltenen Chars nach dem
>> Nullchar durchkämmt - immer wieder und immer wieder und zwar zur
>> Laufzeit.
>
> Um es mal auf den Punkt zu bringen: Na und?

Aufwand und Zeit. Es ist schon vorteilhaft, vorher zu wissen, wie lang 
ein String ist. Zumindest wenn der Prozessor deutlich aufwändiger ist 
als ein AVR oder ARM7.

von Oliver (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Aufwand und Zeit. Es ist schon vorteilhaft, vorher zu wissen, wie lang
> ein String ist. Zumindest wenn der Prozessor deutlich aufwändiger ist
> als ein AVR oder ARM7.

Ach je, und das bei einer Sprache, die für ihren gringen Overhead und 
die Systemnähe bekannt ist.

Ja, die "Nicht-Strings" in C sind unbestritten nicht perfekt, und bei 
der Programmierung fehleranfällig. Das ist deren größtes Problem.

Aber ob da nun in den Bibliotheken der Stringfunktionen auf Null am Ende 
gerüft, oder mit bekannter Länge gearbeitet wird, ist sowas von egal, 
egaler geht's nicht.

Oliver

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


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:

> Ach Jörg, du hast manchmal ne ausgesprochen rechthaberische Art drauf.
> Ja, ich habe den gesamten Kontext gelesen, habe ihn jedoch zwecks
> Platzersparnis nicht komplett zitiert - den restlichen Platz brauch ich
> üblicherweise für meinen eigenen Text...

Dann zitiere ich ihn nochmal:

===========================================================

>> Auch ein optimierter Vorgang wird das Nullbyte nicht "erraten" können

> Doch, bei einem Stringliteral schon.

===========================================================

Weil danach eine Rückfrage kam, wie das denn funktionieren sollte, hatte
ich ein Beispiel geliefert.

Um nicht mehr und nicht weniger als genau diese Aussage handelte sich
das Beispiel.  Nirgends habe ich dabei eine Wertung abgegeben, ob
vielleicht eine andere Implementierung praktischer gewesen wäre oder
nicht, ob ich die in C so eingebürgerte gut finde oder nicht oder
etwas dergleichen.

Von mir aus hätte die Welt auch durch einen Pascal-Nachfolger gerettet
werden dürfen, ich habe zu CP/M-Zeiten viel damit gemacht und fand es
(in seiner Inkarnation als Turbo-Pascal) so schlecht nicht.  Aber es
hat sich eben nicht so ergeben, also lebe ich mit dem Resultat, wie es
ist.

Davon abgesehen: wenn wir heute alle in einem Pascal-Dialekt arbeiten
würden, ich bin mir sicher, es hätte auch einen Obfuscated Pascal
Contest gegeben und eine Sammlung von diversen Dingen, die dort nicht
so ganz optimal gelaufen sind.  Das fängt ja schon mit der etwas
ungeschickten Operatorenrangordnung an, die es einem nicht gestattet,
dass man schreibt:
1
  if a > 3 and a < 10 then
2
  begin
3
    { ... }
4
  end;

sondern man muss dort zwingend klammern, denn das "and" bindet stärker
als die Vergleiche.

: Bearbeitet durch Moderator
von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Wäre es nicht mal Zeit einen C-Standard zu entwerfen (oder C ähnliche 
Sprache), die diese ganzen Fallstricke und Designfehler mal behebt? 
Kompatibilität ist gut, aber deswegen Fehler Jahrzehnte lang 
herumschleppen nicht.

von Mark B. (markbrandis)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> Wäre es nicht mal Zeit einen C-Standard zu entwerfen (oder C ähnliche
> Sprache), die diese ganzen Fallstricke und Designfehler mal behebt?
> Kompatibilität ist gut, aber deswegen Fehler Jahrzehnte lang
> herumschleppen nicht.

Programmiersprachen gibt's wie Sand am Meer, auch C ähnliche Sprachen. C 
ist aber nun mal sowas wie ein de facto Standard. Welche andere Sprache 
ist für praktisch jeden x-beliebigen Prozessor verfügbar?

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


Bewertung
0 lesenswert
nicht lesenswert
Mark Brandis schrieb:
> Welche andere Sprache
> ist für praktisch jeden x-beliebigen Prozessor verfügbar?

Nicht nur das. Man kann wirklich alles damit machen. Ich brauche
nichts anderes (ok, ab und zu mal Assembler). Es gibt sehr brauchbare
Libs (zB SQLite) uvam.

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


Bewertung
0 lesenswert
nicht lesenswert
Mark Brandis schrieb:
> Programmiersprachen gibt's wie Sand am Meer, auch C ähnliche Sprachen.

D beispielsweise.  Verbreitung?  Eher gering.

Am ehesten sind es dann noch Scriptsprachen wie Python, Perl oder Ruby,
die sich als Alternativen durchsetzen konnten.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Google Go hat ein paar nette Aspekte.

von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die vorhandenen C ähnlichen Sprachen unterscheiden sich aber grundlegend 
vom Konzept. Nur C ist nativ, einfach (kleiner Sprachumfang) und bemüht 
sich um 100% Kontrolle und Freiheit. Nur C++ kommt an weitesten heran, 
ist aber komplex. Ansonsten läuft alles auf einer VM (Java, C#...), hat 
keine 100% Kontrolle (GCC, keine Zeigerarithmetik, ...) oder ist komplex 
(C++).

Es gibt also keine Sprache die C in diesem Bereich Konkurrenz machen 
kann (außer C++). Und so gesehen, gibt es keine C ähnliche Sprache, nur 
die Syntax ist ähnlich.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> Es gibt also keine Sprache die C in diesem Bereich Konkurrenz machen
> kann

Wenn du alle an C orientierten Sprachen ausschliesst, weil sie aus guten 
Gründen nicht genau wie C sind, dann wirst du auch keine finden. ;-)

von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Wenn du alle an C orientierten Sprachen ausschliesst, weil sie aus guten
> Gründen nicht genau wie C sind, dann wirst du auch keine finden. ;-)

Da hast du natürlich recht :). Ein C Standard der sich endlich von den 
Altlasten befreit, wäre mir aber auch ehrlich gesagt lieber.

von Fred (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Joachim Drechsel schrieb:
> Nicht nur das. Man kann wirklich alles damit machen.

Webprogrammierung macht damit bestimmt mächtig Spaß. Auch 
Benutzeroberflächen sind die reine Freude.

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
> Altlasten befreit...

Nur, daß es ohne die Altlasten halt dann kein C mehr ist.
Wenn die Leute nicht mit Zeigern sinnvoll umgehen können,
könnte man ein C ohne Zeiger machen - aber ist es dann C?

Ganz abgesehen davon, daß alle, die schlechte C-Programme
schreiben, auch in anderen Sprachen keine genialen
Programme machen.
Ihre Fehler fallen dann nur später auf ...

: Bearbeitet durch User
von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
Fred schrieb:
> Auch Benutzeroberflächen sind die reine Freude.

Dafür gibt es dann auch C++.

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


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Wenn die Leute nicht mit Zeigern sinnvoll umgehen können,

sollen sie es lernen. Punkt.

von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
>> Altlasten befreit...
>
> Nur, daß es ohne die Altlasten halt dann kein C mehr ist.
> Wenn die Leute nicht mit Zeigern sinnvoll umgehen können,
> könnte man ein C ohne Zeiger machen - aber ist es dann C?

Nein das verstehst du falsch. Mit Altlasten meine ich nicht Zeiger etc.. 
C soll weiterhin seinem Konzept treu bleiben. Aber es gibt in C einige 
Fallstricke (z.B. Operatorpriorität) und Designfehler (z.B. umstrittene 
break Geschichte in switch case), die nur noch aus kompatibilitäts 
Gründen bestehen. Ein weiteres Beispiel wäre, dass ein Stringliteral 
nicht const char* ist. Soll der Typ, der seinen 30 Jahren alten Code 
kompilieren möchte, doch einfach mit jenem Standard kompilieren.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> Aber es gibt in C einige Fallstricke (z.B. Operatorpriorität)

Die unglücklich gewählte Vorrangreglen, die die Vergleichsoperatoren
(==, > usw.) Vorrang über die Bit-Operatoren (&, | und ^) stellen, sind
ein schönes Beispiel dafür, wie sich solche Fehlentscheidungen manchmal
über Jahrzehnte festsetzen, ohne dass es jemand wagt, etwas daran zu
ändern.

Die ursprüngliche Entscheidung in C (1972) war ja noch wenigstens ein
Bisschen nachvollziehbar:

  http://www.lysator.liu.se/c/dmr-on-or.html

C++ und Objective-C (1983) sollten weitgehend abwärtskompatibel zu C
werden, weswegen hier die Fehlentscheidung übernommen werden musste.

Spätestens bei der Entwicklung von Java (1995), das weder zu C noch zu
C++ abwärtskompatibel ist, hätte diesbezüglich aufgräumt werden können.
Die nächste Chance ist bei der Entwicklung von C# (2000) verpasst
worden, aber selbst D (2001) verwendet noch die Vorrangregeln von C.

Von den C-ähnlichen Programmiersprachen hat erst Go (2009) mit dieser
Tradition gebrochen.

37 Jahre hat's also gedauert :)

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> Ein weiteres Beispiel wäre, dass ein Stringliteral
> nicht const char* ist.
Es gibt noch mehr... z.B.:
* Integer-Literale ohne Typangabe (ala "7") sind immer mindestens "int" 
groß, auch wenn sie in kleinere Typen passen würden.
* Das Ergebnis eines beliebigen arithmetischen Operators ist ebenfalls 
immer mindestens ein int; ((char) 7) ist vom Typ char, +((char) 7) ist 
int...
* Die Funktion "void foo ()" nimmt beliebig viele Parameter; das braucht 
man quasi gar nicht mehr und verursacht nur Probleme
* keine Funktions-Überladungen
etc...

von Bastler (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Man muß ja nicht unbedingt K&R verwenden, es gibt auch moderneres!
"verursacht nur Probleme", die man durch C99 ersparen kann.

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Kindergärtner schrieb:
> Ach ja, C++ ist schön:
> std::string myString = "Cheese!";
> for (char c : myString) {
>   tuwas(c);
> }

Aber auch noch nicht so lange. Davor sah das alles andere als schön aus:
1
for (std::string::const_iterator it = myString.begin(); it != myString.end(); ++it)
2
{
3
    tuwas(*it);
4
}

Dr. Sommer schrieb:
> Es gibt noch mehr... z.B.:
> * Integer-Literale ohne Typangabe (ala "7") sind immer mindestens "int"
> groß, auch wenn sie in kleinere Typen passen würden.
> * Das Ergebnis eines beliebigen arithmetischen Operators ist ebenfalls
> immer mindestens ein int; ((char) 7) ist vom Typ char, +((char) 7) ist
> int...

Ich sehe da kein Problem drin.

> * Die Funktion "void foo ()" nimmt beliebig viele Parameter; das braucht
> man quasi gar nicht mehr und verursacht nur Probleme

Das wurde in C99 zwar für obsolet erklärt, aber immer noch unterstützt.

> * keine Funktions-Überladungen

Die würden bei jedem Compiler, der das unterstützen soll, eine 
ABI-Änderung erzwingen, und damit wären sämtliche Bibliotheken nicht 
mehr binärkompatibel.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Aber auch noch nicht so lange. Davor sah das alles andere als schön aus:
Das stimmt. aber jetzt gehts.
> Ich sehe da kein Problem drin.
Ich schon - C++ musste diese Regeln wohl oder übel erben, und daher 
ergeben sich dort lustige Überraschungen:
1
std::cout << '0' << "    " << (+'0');
Das überträgt sich dann auch auf andere Funktionen die sich Aufgrund des 
Paramtertyps unterscheiden:
1
void test (int) {}
2
void test (char) {}
3
4
int main () {
5
  test ('0'); // Aufruf von test(char)
6
  test (+'0'); // Aufruf von test(int)
7
}
> Das wurde in C99 zwar für obsolet erklärt, aber immer noch unterstützt.
und generiert daher immer noch Verwirrung.
>> * keine Funktions-Überladungen
>
> Die würden bei jedem Compiler, der das unterstützen soll, eine
> ABI-Änderung erzwingen, und damit wären sämtliche Bibliotheken nicht
> mehr binärkompatibel.
Ja sowas nachträglich zu ändern ist natürlich schwierig; man hätte es 
halt gleich richtg machen sollen...

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Ja sowas nachträglich zu ändern ist natürlich schwierig; man hätte es
> halt gleich richtg machen sollen...

Das ist leicht hingesagt, wenn der Compiler in einige zig Kilo(!)bytes 
passen soll und manche Plattformen externe Namen auf 6-8 Zeichen 
begrenzen.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Das ist leicht hingesagt, wenn der Compiler in einige zig Kilo(!)bytes
> passen soll und manche Plattformen externe Namen auf 6-8 Zeichen
> begrenzen.
Dann eben als optionales Feature mit standardisierten Symbolnamen, damit 
die "größeren" Compiler das kompatibel hätten machen können... 
Alternativ ohne die Namensfummelei und stattdessen mit 
Binärinformationen. Naja egal, ist ein paar Jahre zu spät jetzt :)

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
Naja, was soll's: wer Überladung haben will, nimmt halt C++ und lässt 
den Rest davon weg, wenn er sich dran stört.
Einfach statt *.c *.cpp schreiben, und schon fertig.

von Johann L. (gjlayde) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Sowas wie Überladung gibt's in C11 per _Generic.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Naja, was soll's: wer Überladung haben will, nimmt halt C++ und lässt
> den Rest davon weg, wenn er sich dran stört.
> Einfach statt *.c *.cpp schreiben, und schon fertig.
Das versuch ich den Leuten auch immer zu sagen wenn sie C verwenden und 
ein bestimmtes C++ Feature ihre Probleme lösen würde; aber dann kommen 
immer so Antworten dass C++ grundsätzlich ineffizient und zu kompliziert 
sei und so was komisches :-/

von working directory (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Google Go hat ein paar nette Aspekte.

In der Tat hat es die.

Noch eine kleine Anmerkung:
Es ist insbesondere gegenüber den zahlreichen Nicht-Google-Mitarbeitern, 
die an Go, den Implementierungen und der Standardbibliothek mitarbeiten 
nicht sonderlich nett, wenn die Sprache ständig als 'Google Go' 
bezeichnet wird. Go ist, im Gegensatz zu etwa Android, kein 
Google-Projekt und den Unterschied merkt man auch.

Rob Pike, Ken Thompson und Robert Griesemer arbeiteten bei Google als 
sie mit der Entwicklung der Sprache begonnen haben. Viele Go-Entwickler 
arbeiten bei Google. Trotzdem ist die Sprache kein Google-Produkt.

Weiß auch nicht, woher das immer kommt. Auch vor der Standardisierung 
hat doch niemand C als 'Bell Labs C' bezeichnet, nur weil Dennis Ritchie 
dort gearbeitet hat.

In Anbetracht dessen, dass es sogar Bücher zu 'Google Go'(*) gibt, ist 
dir da sicher kein Vorwurf zu machen. Dennoch wollte ich es mal 
anmerken.

(*) die übrigens ähnlich "gut" sind, wie die ganzen "C von A-Z in 24 
Stunden"-Dinger

Dr. Sommer schrieb:
>
1
void test (int) {}
2
> void test (char) {}
3
> 
4
> int main () {
5
>   test ('0'); // Aufruf von test(char)
6
>   test (+'0'); // Aufruf von test(int)
7
> }
Ist das nicht eher ein Beispiel dafür, dass das Überladen von Funktionen 
der Code-Lesbarkeit nicht unbedingt zuträglich ist? Gerade wenn dann wie 
im Fall von C++ auch noch benutzerdefinierte Typkonvertierungsoperatoren 
ins Spiel kommen? Ebenso finde ich es schön, wenn eine Funktion 'f' auch 
tatsächlich dem Symbol 'f' (meinetwegen <namespace>.f) im generierten 
Objektcode entspricht und nicht bis zur Unkenntlichkeit gemanglet wurde.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
working directory schrieb:
> Dr. Sommer schrieb:
> Ist das nicht eher ein Beispiel dafür, dass das Überladen von Funktionen
> der Code-Lesbarkeit nicht unbedingt zuträglich ist?
Der Fehler liegt hier nicht in der Überladung, sondern an der 
sinnlosen Integer Konvertierung an dieser Stelle. Überladungen sind 
etwas sehr sinnvolles das in vielen Sprachen vorhanden ist; selbst in 
der minimalistischen Anfänger-Sprache Java.
> Gerade wenn dann wie
> im Fall von C++ auch noch benutzerdefinierte Typkonvertierungsoperatoren
> ins Spiel kommen?
Man muss bei deren Definition eben aufpassen, und nur sinnvolle solche 
Operatoren definieren. Wenn man alles in alles konvertieren lässt hat 
man was falsch gemacht.
> Ebenso finde ich es schön, wenn eine Funktion 'f' auch
> tatsächlich dem Symbol 'f' (meinetwegen <namespace>.f) im generierten
> Objektcode entspricht und nicht bis zur Unkenntlichkeit gemanglet wurde.
Das findet der Linker aber nicht.

Wenn du mit objdump GCC-erzeugten Code disassemblisierst, kannst du "-C" 
angeben, um statt "_ZN7CANopen4RPDO9onReceiveEN5STM323CAN6RXFifoE" im 
Ausgabe-Code "CANopen::RPDO::onReceive(STM32::CAN::RXFifo)" zu erhalten; 
das ist doch ganz lesbar.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> working directory schrieb:
>> Dr. Sommer schrieb:
>> Ist das nicht eher ein Beispiel dafür, dass das Überladen von Funktionen
>> der Code-Lesbarkeit nicht unbedingt zuträglich ist?
> Der Fehler liegt hier nicht in der Überladung, sondern an der
> sinnlosen Integer Konvertierung an dieser Stelle. Überladungen sind
> etwas sehr sinnvolles das in vielen Sprachen vorhanden ist; selbst in
> der minimalistischen Anfänger-Sprache Java.

Ich würde sagen, das Problem liegt eher darin, dass man in C und C++ mit
Zeichen rechnen kann. Ein Zeichen ist nun einmal etwas völlig anderes
als eine Integer-Zahl. Ausdrücke wie
1
'a' + 'b'
2
3 * 'a'
3
'a' / 5
4
-'a'

und damit eben auch +'a' sind doch völliger Unfug. Ich kenne auch keine
nicht von C abgeleitete Sprache, in der diese Form von Arithmetik
erlaubt wäre.

Halbwegs sinnvoll sind in meinen Augen allenfalls folgende arithmetische
Operationen:
1
char + int  -> char
2
int  + char -> char
3
char - char -> int

also bspw.
1
int n;
2
char ziffer;
3
4
n = 3;
5
ziffer = '0' + n;
6
7
ziffer = '8';
8
n = ziffer - '0';

Bei der Zeigerarithmetik gibt es entsprechende Einschränkungen, warum
also nicht auch bei den Zeichen? Als Integer-Datentyp für gewöhnliche
Berechnungen hätte man zusätzlich zu "long" und "int" noch das "byte"
einführen können.

Auf der anderen Seite:

Wer kommt schon auf die Idee,
1
  test (+'0');

zu schreiben?

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


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Halbwegs sinnvoll sind in meinen Augen allenfalls folgende arithmetische
> Operationen:

Auch davon wäre im Sinn dieser Betrachtungen abzuraten, weil es zu sehr 
suggeriert, dass man mit 'I'+1 zu 'J' kommt. Mit expliziten 
Konvertierungen wird man da deutlicher drauf gestossen.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
working directory schrieb:
> Weiß auch nicht, woher das immer kommt. Auch vor der Standardisierung
> hat doch niemand C als 'Bell Labs C' bezeichnet, nur weil Dennis Ritchie
> dort gearbeitet hat.

Möglicherweise hängt das damit zusammen, dass es vor Go schon eine
andere Programmiersprache mit fast gleichem Namen gegeben hat:

  http://en.wikipedia.org/wiki/Go!_%28programming_language%29

Go! kennt zwar fast keiner (ich selber kenne es auch nur aus dem
entsprechenden Hinweis im Wikipedia-Artikel zu Go), aber es scheint
damals bei der Namensgebung von Go eine heftige Diskussion zwischen
dem Go!-Entwickler und Google gegeben zu haben.


A. K. schrieb:
> Yalu X. schrieb:
>> Halbwegs sinnvoll sind in meinen Augen allenfalls folgende arithmetische
>> Operationen:
>
> Auch davon wäre im Sinn dieser Betrachtungen abzuraten, weil es zu sehr
> suggeriert, dass man mit 'I'+1 zu 'J' kommt.

Richtig. Deswegen habe ich auch "halbwegs sinnvoll" geschrieben :)

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Ich würde sagen, das Problem liegt eher darin, dass man in C und C++ mit
> Zeichen rechnen kann.
Das löst aber nicht das gezeigte Problem aus. Wenn man damit nicht 
rechnen könnte müsste man halt ständig casten, denn Eingaben aus Dateien 
sind immer "char"...

und ich bleibe dabei, dass das Ursache für das gezeigte Problem die 
komischen C-Regeln für Operatoren sind; warum muss die Summe zweier 
8bit-Zahlen immer 32bit sein, während die Summe zweier 32bit-Zahlen 
nur 32bit ist (auf x86 mit char=8bit, int=32bit)? Wäre es nicht viel 
sinnvoller wenn das Ergebnis (im 1. Fall) wieder 8bit wäre? Wenn man 
einen Overflow erwartet müsste man halt vorher casten - genauso wie man 
das bei Datentypen >= int (zB 32bit) muss.
Ja, ändern kann man das jetzt nicht mehr, aber die Regeln sind trotzdem 
bescheuert und sorgen für Überraschungen, wenn der Typ eines Ausdrucks 
eine Rolle spielt (wie eben bei Überladungen, Template-Type-Inference 
wie sehr oft von der Standard Library verwendet, decltype, auto).

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> warum muss die Summe zweier 8bit-Zahlen immer 32bit sein

Muss sie nicht. Sie muss mindestens 16 Bits sein, weil das die 
offizielle untere Grenze von "int" ist.

Warum? Vermutlich weil die PDP-11 so arbeitete. Deren 8-Bit Operationen 
arbeiteten mit impliziter Vorzeichenerweiterung auf 16 Bits. Und es 
waren hauptsächlich PDP-11 Rechner, auf denen C und Unix anfangs 
verbreitet wurde. Deshalb sind ja auch chars mit Vorzeichen recht 
verbreitet, eine eigentlich recht bizarre Idee.

Ritche hatte sicherlich nicht geplant, dass diese bewusst einfach 
konzipierte Sprache über Jahrzehnte zur dominanten Programmiersprache 
für viele Anwendungsbereiche wird.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Yalu X. schrieb:
>> Ich würde sagen, das Problem liegt eher darin, dass man in C und C++ mit
>> Zeichen rechnen kann.
> Das löst aber nicht das gezeigte Problem aus. Wenn man damit nicht
> rechnen könnte müsste man halt ständig casten, denn Eingaben aus Dateien
> sind immer "char"...

Deinen Einwand verstehe ich jetzt nicht ganz. Wenn Zeichen und Zahlen
grundsätzlich unterschiedliche Datentypen wären, gäbe es doch auch beim
Aufruf überladener Funktionen keine Verwechslungsmöglichkeit mehr?

> warum muss die Summe zweier 8bit-Zahlen immer 32bit sein, während die
> Summe zweier 32bit-Zahlen nur 32bit ist (auf x86 mit char=8bit,
> int=32bit)? Wäre es nicht viel sinnvoller wenn das Ergebnis (im 1.
> Fall) wieder 8bit wäre?

Ja, da stimme ich dir zu. Man hätte sich dadurch die ganzen – nicht ganz
leicht zu merkenden – Integer-Promotion-Rules sparen können. Aber den
Grund für diese Regel hat A.K. ja schon genannt.

Trotzdem: Wer sollte auf die Idee kommen, +'0' statt '0' zu schreiben?

Oder anders ausgedrückt: Mit dem Voranstellen eines Vorzeichens teilt
der Programmier dem Compiler doch mit, dass er in diesem Fall das
Zeichen als eine Zahl intepretiert haben möchte. Deswegen ist es nur
konsequent, wenn von den beiden alternativen Implementationen von "test"
diejenige genommen wird, die eine Zahl als Argument erwartet.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Deinen Einwand verstehe ich jetzt nicht ganz. Wenn Zeichen und Zahlen
> grundsätzlich unterschiedliche Datentypen wären, gäbe es doch auch beim
> Aufruf überladener Funktionen keine Verwechslungsmöglichkeit mehr?

Und man müsste sich bei Zeichen nicht mit den 3 verschiedenen Datentypen
  char
  signed char
  unsigned char
rumärgern, die grad bei Überladung stets für grosse Freude sorgen.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Muss sie nicht. Sie muss mindestens 16 Bits sein, weil das die
> offizielle untere Grenze von "int" ist.
Lies doch was ich schreibe; da steht x86 als Beispiel.

Yalu X. schrieb:
> Deinen Einwand verstehe ich jetzt nicht ganz. Wenn Zeichen und Zahlen
> grundsätzlich unterschiedliche Datentypen wären, gäbe es doch auch beim
> Aufruf überladener Funktionen keine Verwechslungsmöglichkeit mehr?
Okay, mein Beispiel war missverständlich, mit "char" meinte ich halt 
"einen kleinen integer", nicht "ein zeichen", und mit '0' einen 
Integer diesen Typs. Ich wollte verdeutlichen dass der Rückgabetyp von 
+x überraschenderweise "int" ist, (u.a.) wenn x ein "char" ist; somit 
hat man seltsamerweise eben int-Ergebnisse aus einer char-Rechnung, 
wodurch unbeabsichtigt z.B. falsche Overloads aufgerufen werden.

Yalu X. schrieb:
> Trotzdem: Wer sollte auf die Idee kommen, +'0' statt '0' zu schreiben?
Ja direkt schreibt das natürlich keiner! Leider gibt es genug Code der 
+x enthält als "kurznotation" für "cast nach int", aber man könnte ja 
auch aus Versehen dinge schreiben wie;
1
void test (short x) { test2 (x + short { 7 }); }
 - und hier wird test2 mit einem int aufgerufen, nicht mit short 
(was eben Probleme gibt wenn test2 Überladungen für int und short hat, 
oder ein Template ist); korrekt wäre
1
void test (short x) { test2 (static_cast<short> (x + short { 7 })); }
Offensichtlich ist anders.
Manchmal gibts zum Glück Warnungen:
1
short test (short x) {
2
  return { x + 3 };
3
}
Aber auch nur wenn man {} aus C++11 verwendet.

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Ich würde sagen, das Problem liegt eher darin, dass man in C und C++ mit
> Zeichen rechnen kann.

Richtig. Das sehe ich genauso. Wenn char genau gleich definiert wäre, 
wie jetzt, außer daß man damit nicht rechnen könnte, dann würde kein 
Mensch sich dafür interessieren, ob das jetzt signed oder unsigned ist. 
Wenn man mal irgendwo rechnen muß, müßte man halt in einen dafür 
sinnvollen Typ konvertieren. Das ist an sich ja heute schon so, nur 
wollen das viele nicht einsehen. Sie wollen lieber wider besseren Rates 
direkt mit char rechnen und fangen daher an, über irgendwelche 
Vorzeichen nachzudenken.

> Bei der Zeigerarithmetik gibt es entsprechende Einschränkungen, warum
> also nicht auch bei den Zeichen? Als Integer-Datentyp für gewöhnliche
> Berechnungen hätte man zusätzlich zu "long" und "int" noch das "byte"
> einführen können.

So sehe ich das auch. Den "kleinen Integer" statt char byte nennen und 
genau wie die anderen Integertypen behandeln. Dann noch einen separaten 
'char', mit dem keine Rechenoperationen erlaubt sind. Wer die will, muß 
dafür halt nach unsigned byte konvertieren. Ist das nicht in manchen von 
C abgeleiteten Sprachen auch so?

> Auf der anderen Seite:
>
> Wer kommt schon auf die Idee,
>   test (+'0');
>
> zu schreiben?

Das erinnert mich an bool in C++, mit dem man leider auch rechnen kann. 
Das führt dann zu so lustigen Stilblüten wie dem Konvertierungs-Operator 
nach void* in den iostreams. Eigentlich soll der einen booleschen Wert 
zurückliefern, der zeigt, ob der Stream ok ist oder ob EOF erreicht bzw. 
ein Fehler aufgetreten ist. Es wird aber eben kein bool mit true und 
false zurückgegeben, sondern ein void*, der entweder NULL ist oder 
irgendwas anderes. Und das nur, weil man mit void* im Gegensatz zu bool 
nicht rechnen kann und es dann einen Fehler bei sowas wie:
1
if (std::cin + 3)
gibt.

Dr. Sommer schrieb:
> Yalu X. schrieb:
>> Ich würde sagen, das Problem liegt eher darin, dass man in C und C++ mit
>> Zeichen rechnen kann.
> Das löst aber nicht das gezeigte Problem aus. Wenn man damit nicht
> rechnen könnte müsste man halt ständig casten, denn Eingaben aus Dateien
> sind immer "char"...

Sie sind es bei Stringoperationen wie fgets(). fread(), das eher für 
Binärdaten gedacht ist, nutzt einen void*.

> und ich bleibe dabei, dass das Ursache für das gezeigte Problem die
> komischen C-Regeln für Operatoren sind; warum muss die Summe zweier
> 8bit-Zahlen immer 32bit sein, während die Summe zweier 32bit-Zahlen
> nur 32bit ist (auf x86 mit char=8bit, int=32bit)? Wäre es nicht viel
> sinnvoller wenn das Ergebnis (im 1. Fall) wieder 8bit wäre?

Was mich eher stört ist, daß z.B. eine Multiplikation int * int nicht 
den nächstgrößeren Typ für das Ergebnis nutzt. Die 
Multiplikations-Instruktionen der Prozessoren liefern das in der Regel 
aber. Wenn ich einen Überlauf verhindern will, muß ich in C erst die 
Eingangs-Werte auf den nächsthöheren Typ casten, damit auch das Ergebnis 
diesen Typ hat.

A. K. schrieb:
> Muss sie nicht. Sie muss mindestens 16 Bits sein, weil das die
> offizielle untere Grenze von "int" ist.
>
> Warum? Vermutlich weil die PDP-11 so arbeitete. Deren 8-Bit Operationen
> arbeiteten mit impliziter Vorzeichenerweiterung auf 16 Bits. Und es
> waren hauptsächlich PDP-11 Rechner, auf denen C und Unix anfangs
> verbreitet wurde.

Das ist auch heute nicht so unüblich. ARMe haben auch keine 
8-Bit-Rechenoperationen, sondern arbeiten immer auf 32 Bit. Sie haben 
nur für den Speicherzugriff Instruktionen, die ein Byte in ein 
32-Bit-Reigster laden. Das entspricht also exakt der in C definierten 
impliziten Konvertierung nach int vor der Berechnung.

> Deshalb sind ja auch chars mit Vorzeichen recht verbreitet, eine
> eigentlich recht bizarre Idee.

Bizarr ist nicht das Vorzeichen, sondern daß man überhaupt mit dem Typ 
für Text rechnen kann.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Okay, mein Beispiel war missverständlich, mit "char" meinte ich halt
> "einen kleinen integer", nicht "ein zeichen", und mit '0' einen
> Integer diesen Typs. Ich wollte verdeutlichen dass der Rückgabetyp von
> +x überraschenderweise "int" ist, (u.a.) wenn x ein "char" ist; somit
> hat man seltsamerweise eben int-Ergebnisse aus einer char-Rechnung,
> wodurch unbeabsichtigt z.B. falsche Overloads aufgerufen werden.

Ok, jetzt weiß ich, worauf du hinaus wolltest.

Ja, die schon nicht ganz einfachen Integer-Promotion-Regeln in
Verbindung mit den noch komplizierteren Regeln für die Anwendung
überladener Funktionen öffnen der Verwirrung Tür und Tor. Ich versuche
deswegen, Überladungen immer klar (also ohne die Anwendung komplizierter
Regeln) unterscheidbar zu halten, also Funktionen, die sich
ausschließlich in den Datentypen numerischer Argumente unterscheiden, zu
vermeiden.

: Bearbeitet durch Moderator
von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Die Frage ist doch, warum man nicht einen C und C++ Standard etabliert, 
der mal ordentlich ausmistet. Weg mit den Designfehlern und weg mit dem 
undefinierten Verhalten. Den alten Quellcode lässt sich dann immer noch 
mit den alten Standards kompilieren.

Aber davor bin ich Kaiser von China. Deshalb hoffe ich auf Sprachen wie 
Go und Rust. Gerade Rust ist interessant, da GC optional(stdlib) und es 
RAII mithilfe von Smartpointer unterstützt. Der unique_ptr ist sogar in 
der Syntax der Sprache und shared_ptr in der stdlib. Leider ist Rust 
noch nicht fertig, die Entwicklung geht allerdings schnell voran. Man 
kann schon einiges damit machen, schwer macht es aber die stdlib die 
noch ein bisschen verbuggt ist. Man darf sich nur nicht von der Syntax 
abschrecken lassen, sie ist neben C auch ein wenig an Lisp, glaube ich, 
angelegt.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Ist das nicht in manchen von
> C abgeleiteten Sprachen auch so?
Jo, Java.

Rolf Magnus schrieb:
> Das erinnert mich an bool in C++, mit dem man leider auch rechnen kann.
Nein, kann man nicht. Er wird nur automatisch 'int' konvertiert, 
entweder nach 1 oder 0, was oft sehr praktisch ist und einem Mengen an 
expliziten Casts erspart; und sich außerdem so ähnlich verhält wie 
"C-Booleans" d.h. ein "int" mit den Werten 1 oder 0.

Rolf Magnus schrieb:
> Das führt dann zu so lustigen Stilblüten wie dem Konvertierungs-Operator
> nach void* in den iostreams.
Dank 'explicit' braucht man das zum Glück seit C++11 nicht mehr, und 
siehe da der void* operator ist weg und es gibt jetzt den bool operator:
http://en.cppreference.com/w/cpp/io/basic_ios/operator_bool

Yalu X. schrieb:
> also Funktionen, die sich
> ausschließlich in den Datentypen numerischer Argumente unterscheiden, zu
> vermeiden.
Selbst Java macht sowas: 
http://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html#println%28boolean%29

TriHexagon schrieb:
> Die Frage ist doch, warum man nicht einen C und C++ Standard etabliert,
> der mal ordentlich ausmistet.
Bei Java hat man zB etwas zu viel ausgemistet, die Sprache kann gar 
nichts mehr.

TriHexagon schrieb:
> Weg mit den Designfehlern und weg mit dem
> undefinierten Verhalten. Den alten Quellcode lässt sich dann immer noch
> mit den alten Standards kompilieren.
Und wie soll dann alt-Standard-kompilierte Software mit der neuen 
kompatibel sein, wenn zB die Header sich unterschiedlich verhalten?

von Karl Käfer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Verwirrter schrieb:
> Kannst Du denn mal erklären was char* bedeuten soll, oder bin ich der
> einzige der das nicht kennt?

Ja, da bist Du der Einzige.

Grüße,
Karl

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Achja, und:
TriHexagon schrieb:
> und weg mit dem undefinierten Verhalten.
D.h. es soll ein bestimmtes Verhalten vorgeschrieben werden, was dann 
aber nur auf einer Architektur so ist, s.d. C++ auf anderen nicht mehr 
läuft bzw. das Verhalten kompliziert in Software emuliert werden muss? 
Nene, das soll Sprachen wie Java vorbehalten sein, C++ ist portabel.

von TriHexagon (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
>> Weg mit den Designfehlern und weg mit dem
>> undefinierten Verhalten. Den alten Quellcode lässt sich dann immer noch
>> mit den alten Standards kompilieren.
> Und wie soll dann alt-Standard-kompilierte Software mit der neuen
> kompatibel sein, wenn zB die Header sich unterschiedlich verhalten?

Mhm ok, das habe ich nicht beachtet, Headerdateien sämtlicher 
Bibliotheken wären dann natürlich fehlerhaft.

Dr. Sommer schrieb:
>> und weg mit dem undefinierten Verhalten.
> D.h. es soll ein bestimmtes Verhalten vorgeschrieben werden, was dann
> aber nur auf einer Architektur so ist, s.d. C++ auf anderen nicht mehr
> läuft bzw. das Verhalten kompliziert in Software emuliert werden muss?
> Nene, das soll Sprachen wie Java vorbehalten sein, C++ ist portabel.

Hab jetzt erst gesehen, dass es da eine Unterscheidung zwischen 
undefinierten und unspezifiziertes Verhalten gibt. Ich meine dann 
natürlich unspezifiziertes Verhalten wie:
1
a = 500;
2
function(a*=12, a+=100);

Aber ist auch egal, C und C++ werden wohl so bleiben. Eine andere 
Sprache wirds hoffentlich mal richten.

von Mark B. (markbrandis)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Nene, das soll Sprachen wie Java vorbehalten sein

Inwiefern?

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mark Brandis schrieb:
> Inwiefern?
Java hat vieles solches undefiniertes Verhalten nicht, wie eben z.B. 
integer over/under -flows und Integer-Größen; auf Plattformen die sich 
aber so wie von Java gewünscht nicht verhalten muss die JVM das 
Verhalten dann emulieren ( -> ineffizient). Java verwendet zB für 
Array-Größen/Indices und alles mögliche andere immer 32bit-Integer, kann 
somit die Vorteile von 64bit-Architekturen nicht (vollständig) nutzen 
und generiert zusätzlichen Overhead auf zB 16bit-Architekturen. In C++ 
kann man Code schreiben (was aber nicht ganz einfach ist) der 
unabhängig von solchen Unterschieden immer funktioniert und auch keine 
unnötigen Performance-Einbußen hat (wie zB die Verwendung von 
16bit-Integern für Indices auf AVR).

von Klaus F. (kfalser)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> a = 500;
> function(a*=12, a+=100);
>
> Aber ist auch egal, C und C++ werden wohl so bleiben. Eine andere
> Sprache wirds hoffentlich mal richten.

Grad bei so einem Beispiel frage ich mich schon, welchen großen Vorteil 
es hätte, wenn das Verhalten in so einem Fall definiert oder 
spezifiziert (was auch immer) wäre.
Wer so etwas schreibt, in welcher Sprache auch immer, sollte sowieso in 
der Programmiererhölle schmoren, zumindest für einige Zeit....

: Bearbeitet durch User
von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
TriHexagon schrieb:
> Die Frage ist doch, warum man nicht einen C und C++ Standard etabliert,
> der mal ordentlich ausmistet. Weg mit den Designfehlern und weg mit dem
> undefinierten Verhalten. Den alten Quellcode lässt sich dann immer noch
> mit den alten Standards kompilieren.

Neue Versionen der ISO-C und -C++-Norm ersetzen immer die alte Version. 
Offiziel gibt es die dann quasi gar nicht mehr. Und wenn man so 
"ausmistet", wie du es vorschlägst, wäre das Verhalten doch sehr anders, 
und es wäre eine neue Sprache, die dann auch einen neuen Namen verdient 
(und sei es nur C+=2 ;-) ).

Dr. Sommer schrieb:
> Rolf Magnus schrieb:
>> Das erinnert mich an bool in C++, mit dem man leider auch rechnen kann.
> Nein, kann man nicht. Er wird nur automatisch 'int' konvertiert,

Ja, ok. Das Resultat ist aber das selbe.

> entweder nach 1 oder 0, was oft sehr praktisch ist und einem Mengen an
> expliziten Casts erspart;

Ehrlich gesagt fallen mir so auf die Schnelle nicht wirklich "Mengen" 
von Fällen ein, wo ich einen Cast bräuchte. Genau genommen fällt mir 
kein einziger ein. Hast du mal ein Beispiel?

Dr. Sommer schrieb:
> Rolf Magnus schrieb:
>> Das führt dann zu so lustigen Stilblüten wie dem Konvertierungs-Operator
>> nach void* in den iostreams.
> Dank 'explicit' braucht man das zum Glück seit C++11 nicht mehr,

Ja. Warum das davor nur für Konvertierkonstruktoren, aber nicht für 
Konvertieroperatoren verfügbar war, hab ich eh nicht verstanden. Ist 
wohl einfach nur vergessen worden.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> Ehrlich gesagt fallen mir so auf die Schnelle nicht wirklich "Mengen"
> von Fällen ein, wo ich einen Cast bräuchte. Genau genommen fällt mir
> kein einziger ein. Hast du mal ein Beispiel?
Öhm. Ich hatte so diverse in meinem Projekt. Jetzt weiß ich natürlich 
auf die schnelle keinen davon auswendig. z.B. aber sowas:
1
bool enabled = ...
2
int powerInput = ...
3
...
4
int powerOutput = powerInput * enabled;
oder
1
bool counterEnabled = ...
2
int counter = ...
3
counter += counterEnabled;
erspart halt den Cast.

von Klaus W. (mfgkw)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> und sei es nur C+=2

"C++" ist sowieso der falsche Name!

Das hieße ja, daß man C weiterentwickelt bzw. die Version erhöht, dann 
aber doch die alte Version verwendet.
Schlauer wäre "++C" gewesen, dann hätte man die Weiterentwicklung auch 
nutzen können und nicht mit C weiterarbeiten müssen.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Das hieße ja, daß man C weiterentwickelt bzw. die Version erhöht, dann
> aber doch die alte Version verwendet.

Und? Wenn ich mir den Trend des Forums vor Augen führe, dann ist genau 
das passiert. Da hat jemand schon vor Jahrzehnten die Sprache 
weiterentwickelt, aber fast alle verwenden immer noch C. ;-)

von Rolf Magnus (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> bool enabled =
> ...
> int powerInput = ...
> ...
> int powerOutput = powerInput * enabled;
> oder
> bool counterEnabled = ...
> int counter = ...
> counter += counterEnabled;
>
> erspart halt den Cast.

Hmm, solchen Code schreibe ich nicht. Einen booleschen Wert benutze ich 
auch tatsächlich als solchen und multipliziere oder addiere ihn nicht 
mit irgendwas. Ich finde, daß der Code dadurch nicht unbedingt an 
Übersichtlichkeit gewinnt. Und daß ein
1
counter += counterEnabled;
vom Compiler in was signifikant schnelleres umgesetzt wird als
1
if (counterEnabled)
2
    counter++;
glaube ich auch nicht.

von Oliver S. (oliverso)


Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Öhm. Ich hatte so diverse in meinem Projekt. Jetzt weiß ich natürlich
> auf die schnelle keinen davon auswendig. z.B. aber sowas:

Sowas ist genau das, wofür C so geliebt wird. Man kann wirklich jeden 
Unsinn damit anstellen...

Oliver

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Rolf Magnus schrieb:
> vom Compiler in was signifikant schnelleres umgesetzt wird als
Das sowieso nicht, aber es ist halt kürzer, und wenn man von den 
Konvertierungsregeln weiß versteht man es auch.

Oliver S. schrieb:
> Sowas ist genau das, wofür C so geliebt wird. Man kann wirklich jeden
> Unsinn damit anstellen...
zB seinen Job sichern indem man verhindert dass ein dahergelaufener 
Java-Programmierer den Code versteht :P außerdem gehts um C++.

von Yalu X. (yalu) (Moderator)


Bewertung
2 lesenswert
nicht lesenswert
Bei solchem Code sollte man sich vor dem Einsatz auf lausigen
Mikrocontrollern vergewissern, dass der Compiler tatsächlich die
Multiplikation wegoptimiert:
1
int powerOutput = powerInput * enabled;

Der AVR-GCC tut dies nämlich nicht und halst einen ATtiny unnötigerweise
eine vollständige 16-Bit-Multiplikation in Software auf.

Folgendes ist auch nicht viel mehr zu tippen und IMHO mindestens genauso
verständlich:
1
int powerOutput = enabled ? powerInput : 0;

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Der AVR-GCC tut dies nämlich nicht und halst einen ATtiny unnötigerweise
> eine vollständige 16-Bit-Multiplikation in Software auf.

Ich meine mal, daß dies eine wirlich gerechte Strafe ist für Leute, die 
SO programmieren.

> Folgendes ist auch nicht viel mehr zu tippen und IMHO mindestens genauso
> verständlich:
> int powerOutput = enabled ? powerInput : 0;

Erstens ist es unleserlich, zweitens kann man heutzutage schon drauf 
vertrauen, daß ein guter Compiler aus
if (enabled) blablabla; else wasanderes;
einen ordentlichen Code macht und drittens ist sowas die Krätze. Man 
sollte die Compiler so umschreiben, daß sie Typangelegenheiten und 
Vorbelegungen beim Variablendeklarieren schlichtweg als Fehler 
rausschmeißen - um all diesen tollen Programmierern zwangsweise 
ordentliches Benehmen beizubringen.
Also eher

int powerOutput;
if (enabled) poweroutput = 1; else poweroutput = 0;

Sowas ist deutlich lesbarer und deutlich besser wartbar.

W.S.

von W.S. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
.. ja ja Casesensivität..
W.S.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
>> int powerOutput = enabled ? powerInput : 0;
>
> Erstens ist es unleserlich,

Aber höchstens für den, der noch nie ein C-Buch in der Hand hatte und
deswegen nicht weiß was das '?' und der ':' bedeuten. Ich bin auch kein
Freund des ?:-Operators, wenn die drei Operanden komplizierte Ausdrücke
sind, so dass man Anfang und Ende jedes Operators erst mühevoll suchen
muss. Aber hier besteht jeder Operand gerade einmal aus einem einzigen
Symbol, einfacher geht's nun wirklich nicht.

> Man sollte die Compiler so umschreiben, daß sie Typangelegenheiten und
> Vorbelegungen beim Variablendeklarieren schlichtweg als Fehler
> rausschmeißen - um all diesen tollen Programmierern zwangsweise
> ordentliches Benehmen beizubringen.

Wenn man den tollen Programmierern unbedingt Benehmen beibringen möchte,
sollte man eher das Gegenteil von dem tun, was du vorschlägst, nämlich
die Intialisierung bei der Variablendefinition explizit vorschreiben.
Dadurch wird gleich eine ganze Klasse von Fehlern, nämlich die der
uninitialisierten Variablen, vermieden, die oft erst entdeckt werden,
wenn die Software bereits ausgeliefert und im produktiven Einsatz ist.

> int powerOutput;
> if (enabled) poweroutput = 1; else poweroutput = 0;

Aber dann bitteschön nicht in einer Zeile, sondern – wie es sich gehört
– in vieren:
1
if (enabled)
2
  poweroutput = 1;
3
else
4
  poweroutput = 0;

Die Variante mit '?' und ':' finde ich aber trotzdem besser, da man dort
auf den ersten Blick sieht, dass nur eine einzige Variable (poweroutput)
einen neuen Wert zugewiesen bekommt.

von (prx) A. K. (prx)


Bewertung
0 lesenswert
nicht lesenswert
Leicht unleserlich aber garantiert effizient wäre
  int powerOutput = -enabled & powerInput;
insbesondere wenn man "enabled" passend negiert speichert. Solange 
niemand das Einerkomplementsystem wiedererfindet wäre es wohl auch 
sicher.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Wenn man den tollen Programmierern unbedingt Benehmen beibringen möchte,
> sollte man eher das Gegenteil von dem tun, was du vorschlägst, nämlich
> die Intialisierung bei der Variablendefinition explizit vorschreiben.

Bin ich kein Freund davon.  Eine "aus Prinzip" immer vorgenommene
Wertzuweisung kann auch mal einen echten Bug kaschieren, der sonst
als "may be used uninitialized"-Warnung aufgeflogen wäre.  Typisches
Beispiel sind geschachtelte Schleifen mit verwechseltem "i" und "j"
oder sowas.

Eine uninitialisierte Verwendung nichtdynamischen Speichers kann wohl im 
dritten Jahrtausend hoffentlich jeder Compiler anmosern, genauso wie
das typisch verwechselte "=" mit "==".  Die zuweilen propagierte Form
1
  if (KONSTANTE == variable) ...

empfinde ich jedenfalls als außerordentlich schwer zu lesen, da unser
natürlicher Lesefluss eben eine Variable auf ihren Inhalt vergleichen
mag, nicht einen Inhalt auf eine Variable.

: Bearbeitet durch Moderator
von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Bin ich kein Freund davon.  Eine "aus Prinzip" immer vorgenommene
> Wertzuweisung

Man sollte die variable eben erst genau dann definieren, wenn man auch 
einen wert für sie hat. So kann man nie auf eine wert-"lose" variable 
zugreifen.  OOP-Sprachen treiben das sogar noch weiter indem sie durch 
konstruktoren eine automatische vorbelegung ermöglichen/erzwingen (wenn 
argumente verlangt).
Und wenn man gerade dabei ist definiert man die Variablen in den 
kleinsten&innersten Scope wie möglich, denn dann sieht man viel besser 
aud was die Variable Einfluss hat und was man mit ihr machen darf.
Die Zeiten wo alle benötigten Variablen an den Anfang einer Funktion 
geschrieben werden mussten sind lang vorbei.

von der mechatroniker (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> Man sollte die variable eben erst genau dann definieren, wenn man auch
> einen wert für sie hat.

Geht nicht immer:
1
bool dings(int &a) {
2
    // Liefer mir einen Wert für a
3
    a = 42;
4
    return false;
5
}
6
7
int main(int argc, char*[] argv)
8
{
9
    int a;
10
    if (dings(a)) {
11
        tu_was_mit(a);
12
    }
13
}

Jeder Initialwert bei der Definition von a wäre zwangsläufig ein 
Dummywert, der verhindern würde, dass man eine ordentliche 
Compilerwarnung bekommt, wenn man im Kontrollfluss den Aufruf von dings 
überspringt.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
der mechatroniker schrieb:
> Geht nicht immer:
Richtig. Aber oft genug.
> bool dings(int &a) {
>     // Liefer mir einen Wert für a
>     a = 42;
>     return false;
> }
Wer so codet hat eh was falsch gemacht.
1
std::pair<bool, int> dings () { return { false, 42 }; }
2
int main () {
3
  auto r = dings ();
4
  if (r.first) tuWasMit (r.second);
5
}
Oder mit entsprechender Klassendefinition:
1
optional<int> dings () {
2
  return bedingung ? optional<int>::nothing () : optional<int> { 42 };
3
}
4
int main () {
5
  dings ().doIf ([](int x) { tuWasMit (x); });
6
  // oder
7
  auto r = dings ();
8
  if (r) tuWasMit (r.value ());
9
}
Referenz-Parameter als Ausgabe ist ziemlich hässlich - dafür sind 
Rückgabewerte da.

von Oliver S. (oliverso)


Bewertung
0 lesenswert
nicht lesenswert
der mechatroniker schrieb:
> <old style C++>

Dr. Sommer schrieb:
> <C++ Vodoo>

Oliver S. schrieb:
> Sowas ist genau das, wofür C so geliebt wird. Man kann wirklich jeden
> Unsinn damit anstellen...

Auch wenns jetzt C++ ist, icb bleib dabei.

Oliver

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Oliver S. schrieb:
> Dr. Sommer schrieb:
>> <C++ Vodoo>
>
> Oliver S. schrieb:
>> Sowas ist genau das, wofür C so geliebt wird. Man kann wirklich jeden
>> Unsinn damit anstellen...

Das, was du als "C++ Voodoo" bezeichnest, ist kein Unsinn, sondern sehr
saubere Programmierung (klar definierte Funktionen ohne versteckte
Nebeneffekte). Leider sind C++ und erst recht C zu wenig ausdrucksstark,
um solche Dinge elegant umzusetzen.

In anderen Sprachen, bspw. Python, geht das sehr viel übersichtlicher.
Hier ist Dr. Sommers Beispiel mit dem "pair" als Funktionswert:
1
def dings():
2
  return False, 42
3
4
def main():
5
  ok, wert = dings()
6
  if ok:
7
    tuWasMit(wert)

Und hier das Beispiel mit "optional":
1
def dings():
2
  if bedingung:
3
    return None
4
  else:
5
    return 42
6
7
def main():
8
  r = dings()
9
  if r != None:
10
    tuWasMit(r)

von Oliver S. (oliverso)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Das, was du als "C++ Voodoo" bezeichnest, ist kein Unsinn, sondern sehr
> saubere Programmierung (klar definierte Funktionen ohne versteckte
> Nebeneffekte). Leider sind C++ und erst recht C zu wenig ausdrucksstark,
> um solche Dinge elegant umzusetzen.

Das das Unsinn ist, habe ich ja nie behauptet. Mir ging es um was 
anderes.

Dieser thread begann mal mit einer Frage zu
> char* str = "abc";

Das, was ich als C++-Vodoo bezeichnet habe, ist davon so weit entfernt 
wie der Pluto von der Sonne. Und trotzdem ist das alles C++.

Alles kann, nichts muß...

Oliver

von lalala (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Kindergärtner schrieb:
> std::string myString = "Cheese!";
> for (char c : myString) {
>   tuwas(c);
> }

Das finde ich prima. Kann mir jemand sagen seit wann (welcher Standard) 
das unterstützt wird? Gibt es ein gutes Buch in dem solche Sachen 
erklärt sind? (in meinem Stroustrup  nix gefunden, vielleicht zu alt)

von Udo S. (urschmitt)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> bool enabled = ...
> int powerInput = ...
> ...
> int powerOutput = powerInput * enabled;oderbool counterEnabled = ...
> int counter = ...
> counter += counterEnabled;erspart halt den Cast.

Wenn mir jemand sowas in den Code hunzen würde, dem würde ich solange 
Bücher wie "writing solid code" oder "Clean code" um die Ohren hauen bis 
er den Inhalt auswendig weis.

Dr. Sommer schrieb:
> B seinen Job sichern indem man verhindert dass ein dahergelaufener
> Java-Programmierer den Code versteht :P

Wenn du zu solchem Schweinkram greifen musst damit du deinen Job 
behältst tust du mir leid. Bei uns wäre sowas eher ein Kündigungsgrund.
Der hergelaufene Java-Programmierer ist dir dann zumindest im Bezug auf 
les- und wartbaren Code schreiben Lichtjahre voraus.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
lalala schrieb:
> Kann mir jemand sagen seit wann (welcher Standard)
> das unterstützt wird?

  http://en.wikipedia.org/wiki/C%2B%2B11#Range-based_for_loop

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> sondern sehr
> saubere Programmierung (klar definierte Funktionen ohne versteckte
> Nebeneffekte)
Wow, der erste der das hier von meinem Code sagt.

Yalu X. schrieb:
> In anderen Sprachen, bspw. Python
Python ist aber dynamisch typisiert... Mit ein "bisschen" 
impliziter-konvertierungs-magic könnte man es vielleicht so wie dein 2. 
Beispiel in C++ hinbekommen. Beides geht aber interessanterweise in der 
ebenfalls streng statisch typisierten Sprache Scala.

lalala schrieb:
> Das finde ich prima. Kann mir jemand sagen seit wann (welcher Standard)
> das unterstützt wird?
C++11.
> Gibt es ein gutes Buch in dem solche Sachen
> erklärt sind? (in meinem Stroustrup  nix gefunden, vielleicht zu alt)
Dann nimm den neuesten Stroustrup... http://www.amazon.de/dp/0321563840

Udo Schmitt schrieb:
> Dr. Sommer schrieb:
> Wenn mir jemand sowas in den Code hunzen würde, dem würde ich solange
> Bücher wie "writing solid code" oder "Clean code" um die Ohren hauen bis
> er den Inhalt auswendig weis.
Wer sich über sowas aufregt hat auch komische Prioritäten.
> Wenn du zu solchem Schweinkram greifen musst damit du deinen Job
> behältst tust du mir leid. Bei uns wäre sowas eher ein Kündigungsgrund.
In der Kündigung steht dann "Büße für den Frevel der impliziten 
bool-integer-Konvertierung!"
> Der hergelaufene Java-Programmierer ist dir dann zumindest im Bezug auf
> les- und wartbaren Code schreiben Lichtjahre voraus.
Jaa. Nur weil einer Java kann muss er noch lange nicht "wartbaren" Code 
schreiben. Mein Lieblingsbeispiel (gesehen in einem realen Projekt):
1
class Base {
2
  public int getID () {
3
    if (this instanceof Derived1) return 1;
4
    else if (this instanceof Derived2) return 2;
5
    else if (this instanceof Derived3) return 3;
6
  }
7
}
8
class Derived1 {};
9
class Derived2 {};
10
class Derived3 {};
Es gibt doch weitaus ekligere Möglichkeiten hässlichen Code zu 
schreiben, insbesondere in Bezug auf die Struktur/Modellierung des 
gesamten Codes, als an einer stelle "lokal" das ? : zu sparen. Denn - 
die Konvertierung zu "reparieren" erfordert das Ändern einer einzigen 
Stelle, sowas wie das da oben zu reparieren schon 4 Stellen.

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Yalu X. schrieb:
>> In anderen Sprachen, bspw. Python
> Python ist aber dynamisch typisiert... Mit ein "bisschen"
> impliziter-konvertierungs-magic könnte man es vielleicht so wie dein 2.
> Beispiel in C++ hinbekommen. Beides geht aber interessanterweise in der
> ebenfalls streng statisch typisierten Sprache Scala.

Oder in Haskell :)

Trotz der statischen Typisierung und der strengen Typprüfung braucht man
dort dank Typ-Inferenz die Typdeklarationen nicht einmal explizit
hinschreiben.

Das erste Beispiel sieht von der Struktur her ganz ähnlich wie in Python
aus:
1
dings = (False, 42)
2
3
main = let (ok, wert) = dings
4
       in  when ok (tuWasMit wert)

Im zweiten Beispiel muss der Funktionswert von dings in einen Maybe-Typ
verpackt werden, damit valide und invalide Ergebnisse den gleichen
Datentyp haben.

Der parametrisierte Typ "Maybe" aus der Standardbibliothek entspricht
dabei ziemlich genau deiner Template-Klasse "optional". Das Verpacken
geschieht mit den Konstruktoren Just (valid) und Nothing (invalid):
1
dings = if   bedingung 
2
        then Nothing
3
        else Just 42
4
5
main = let r = dings
6
       in  when (isJust r) (tuWasMit (fromJust r))

Das "main" in diesem Beispiel kann etwas übersichtlicher auch so
geschrieben werden:
1
main = case dings of
2
         Just wert -> tuWasMit wert
3
         Nothing   -> return ()

Ich finde es irgendwie schon beeindruckend, wie man in Haskell Code
ähnlich elegant wie in Python schreiben kann, dabei aber der Compiler
Fehlerprüfungen vornimmt, die weit über diejenigen von C++ hinausgehen.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Ich finde es irgendwie schon beeindruckend, wie man in Haskell Code
> ähnlich elegant wie in Python schreiben kann, dabei aber der Compiler
> Fehlerprüfungen vornimmt, die weit über diejenigen von C++ hinausgehen.
Das wird hier wohl möglich gemacht durch Extraction und Return Type 
Inferring... Beides Dinge die auch für C++ geplant sind ;-)

von Yalu X. (yalu) (Moderator)


Bewertung
0 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Das wird hier wohl möglich gemacht durch Extraction und Return Type
> Inferring... Beides Dinge die auch für C++ geplant sind ;-)

Meinst du die Erweiterung in C++14

  http://en.wikipedia.org/wiki/C%2B%2B1y#Function_return_type_deduction

oder gibt es auch schon Pläne für spätere C++-Standards? Ich konnte dazu
leider nichts finden.

In Haskell kann bspw. bei einem Ausdruck nicht nur von den Typen der
Operanden bzw. der Funktionsargumente auf den Ergebnistyp geschlossen
werden, sondern auch umgekehrt. Das erlaubt es sehr oft, die komplette
Signatur einer Funktion (einschließlich der Argumenttypen) zu bestimmen.
Da aus Gründen der Flexibilität nicht nur fixe Typen, sondern auch
Typklassen unterstützt werden, ist der Algorithmus für die Typinferenz
alles andere als trivial:

  http://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system

Ich kann mir nicht vorstellen, dass in C++ auch nur annähernd so weit
gegangen werden kann, da es bspw. implizite Typkonvertierungen und
überladene Funktionen beliebig schwer machen, nachträglich einen
weitreichenden Typinferenzmechanismus aufzusetzen. Denn diese beiden
Features setzen ja voraus, dass man die Operandentypen bereits kennt. Um
diese aber zu ermitteln, müsste man vorher schon wissen, welche
Typkonvertierung bzw. welche Variante einer überladenen Funktion
verwendet wird. Da beißt sich die Katze in den Schwanz.

Ein Erfolg wäre es IMHO aber schon, wenn man sich wenigstens einen
Großteil der hässlichen Template-Argumentlisten ersparen könnte. Schon
die in C++11 implementierte Typinferenz – auch wenn sie noch recht
primitiv ist – hat hier schon einiges zum Positiven gewendet.

Da muss aber noch mehr gehen ;-)

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Meinst du die Erweiterung in C++14
>
>   http://en.wikipedia.org/wiki/C%2B%2B1y#Function_re...
Jo.
> Ich kann mir nicht vorstellen, dass in C++ auch nur annähernd so weit
> gegangen werden kann
Ja das kann sein.
> Da beißt sich die Katze in den Schwanz.
C++ funktioniert halt anders herum, hauptsächlich von innen nach aussen.
> Da muss aber noch mehr gehen ;-)
Naja das Ziel von C++ ist ja nicht Haskell zu imitieren...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.