Forum: Compiler & IDEs Strings- der blanke Hass in 'C'


von flyingwolf (Gast)


Lesenswert?

Hallo Leute
Es gibt praktisch keine einzige Funktion für Strings, die ich aus C
nutzen kann, ohne dass mir der Compiler hunderte von warnings und
errors rausschmeist.
Ein ganz billiges strlen(demostring) reicht für ein
`strlen' discards qualifiers from pointer target type
Es muss doch möglich sein unter C einen String auswerten zu können,
ohne hunderte von Intergervariablen zur Zeichenzählung mitschleppen zu
müssen und jeden String Zeichenweise zu fuss auswerten zu müssen.
...
also als ganz einfaches Beispiel und besonders für die ewigen
"Kernighand und Ritchie"-Verweiser.
Was muss man tun, damit strlen keine warnungen mehr erzeugt.
irgendwo habe ich einen thread gefunden der vor kurzem das gleiche
Problem hatte nur das PROGMEM bei mir noch mehr Fehler und warnungen
erzeugt.

von Der T. (Gast)


Lesenswert?

Bitte ein konketes Beispiel! :)

von Christoph _. (chris)


Lesenswert?

> `strlen' discards qualifiers from pointer target type

Das deutet darauf hin, dass dein char-Pointer entweder const oder
volatile besitzt, strlen aber weniger erwartet. const wird es nicht
sein, da gerade Leute, die mit C oder C++ beginnen, das nur seltenst
benutzen, wenn es nicht absolut notwendig ist (sprich: bei dessen
Fehlen ein Compile-Fehler auftritt). Außerdem wird strlen als Parameter
"const char*" haben.

Also ist es wahrscheinlich volatile. Du hast dann zwei Möglichkeiten:
a) Die Daten des Strings nicht mehr als volatile deklarieren
b) Ein eigenes strlen schreiben, dass "const volatile char*"
übernimmt.
c) Die Warnung mit (const char*)(p) unterdrücken bzw. const_cast<const
char*>(p) in C++ (IIRC).

Nein, ich habe mich nicht verzählt.
a) Kommt in Frage, wenn der String nicht in interrupts o.ä., also
"spontan" aus Compiler-Sicht, modifiziert wird. Vielleicht hast du
den String auch nur falsch deklariert und meinst eigentlich "char *
volatile" anstelle "volatile char*".
b) Würde mehr Code erzeugen
c) Kommt nur in Frage, wenn du 100% absolut sicher ausschließen kannst,
dass der String während des strlen-Aufrufs etwa in Interrupts
modifiziert wird. Sowas solltest du nur mit ausreichenden Kommentaren
einsetzen, da das IMHO eine sehr unsaubere Methode ist.


Falls es kein volatile-Problem ist, muss ich meine Glaskugel wohl mal
wieder in Reparatur geben. Dabei kam die gerade erst zurück.

von Peter D. (peda)


Lesenswert?

"Es gibt praktisch keine einzige Funktion für Strings, die ich aus C
nutzen kann, ohne dass mir der Compiler hunderte von warnings und
errors rausschmeist."


Der ist wirklich gut !
Damit kannst Du auch den besten Supportmitarbeiter zur Weißglut
treiben.

Kein Beispiel mit kompletter Fehlermeldung, nur Wischiwaschi.

Sei aber versichert, der Fehler liegt bei Dir.


strlen() ist aber ne Funktion, die ich eher selten benutze, die meisten
String-Funktionen wissen selber, wo das Ende ist.

Bei konstanten Strings ist auch sizeof() besser, da setzt der Compiler
gleich die Zahl ein und braucht nicht erst zur Laufzeit abzählen.


Peter

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


Lesenswert?

> Bei konstanten Strings ist auch sizeof() besser, da setzt der
> Compiler gleich die Zahl ein und braucht nicht erst zur Laufzeit
> abzählen.

strlen() degradiert zu sizeof()-1 bei einem konstanten String. ;-)
(Sofern man nicht -ffreestanding benutzt, natürlich.)

von xXx (Gast)


Lesenswert?

"Der ist wirklich gut !
Damit kannst Du auch den besten Supportmitarbeiter zur Weißglut
treiben.

Kein Beispiel mit kompletter Fehlermeldung, nur Wischiwaschi.

Sei aber versichert, der Fehler liegt bei Dir."

@PeDa
Da braucht man sich nicht wundern, wenn dann sowas passiert.

http://www.mikrocontroller.net/forum/read-7-328980.html

von Wegstabenverbuchsler (Gast)


Lesenswert?

@xXx

Der Einwand des Ursprungsposters war:
"Es gibt praktisch keine einzige Funktion für Strings, die ich aus C
nutzen kann, ohne dass mir der Compiler hunderte von warnings und
errors rausschmeist."

In der Annahme daß "der Compiler" (welchen auch immer er nutzte)
nicht total buggy ist, ist Peter Dannegers "Sei aber versichert, der
Fehler liegt bei Dir." meines Erachtens sachlich korrekt und
vollständig gerechtfertigt.

Die Frage ist halt, wie kann man dem Ursprungsposter unterstützen in
der Identifikation was da schief läuft? Da sollte sich meines Erachtens
der Ursprungsposter besser in einer defensiven Rolle bewegen als in
einer offensiven ("blanker Hass", ... "es muß doch möglich
sein"..).  Und wie man in den Wald reinruft, so schallt es heraus.

PS: Christophs Antwort auf die "Frage" des Ursprungsposters halte ich
aber ehrlich auch entspannender als Peters Breitseite...

von Dirk (Gast)


Lesenswert?

Hi,

PeDa weisst nur nochmal dringlicher daraufhin genaue Angaben zum
Problem zugeben.

Ohne konkrete Angaben z.B. Fehlermeldung und Sourcecode kann man immer
nur stochern und man muss stundenlang den Threadoeffner die
Informationen aus der Nase poppeln.

Bitte achtet darauf soviele Informationen zugeben wie es moeglich ist.
Lieber ein paar Zeilen mehr posten als Sie zuverstecken.

Gruß,

Dirk

von flyingwolf (Gast)


Lesenswert?

erst mal danke an alle die die Glaskugel bemüht haben. Ich war mir nicht
im klaren darüber das die Frage so ungenau ist...
Das der Fehler bei mir liegt ist schon klar... nur wo...

Es ist in der Tat ein volatile char und soll ein Zeichen von
UART-Interrupt angehangen bekommen...

Bei genauerem überlegen ist das Argument der unsauberen Programmierung
berechtigt, wenn die Interruptroutine fröhlich Zeichen in einen String
reinschreibt während der gerade in Bearbeitung ist...

Da ist wohl noch etwas Arbeit am Quellcode von Nöten ...

von peter dannegger (Gast)


Lesenswert?

@xXx,

daß Du anonym bleibst, ebenso, wie der Autor Deines Links (bist Du es
selber ?), spricht nicht gerade für Dich.


Ich stehe jedenfalls zu meinen Worten.

Und ich finde es grob unhöflich, wenn man Ergüsse seiner vagen
Vermutungen plaziert, ohne dem Leser die Möglichkeit zu geben, sie auch
nachzuvollziehen.

Daher war ich so freundlich, denjenigen darauf hinzuweise, daß es dem
Posting an Grundlegendem mangelt.

Obendrein habe ich noch ausgeführt, daß viele Programmierer die
Funktionen erfolgreich nutzen, also der Fehler nicht im Compiler fußt.


Es sind also alles nur freundlich gemeinte Hinweise, sein Programm bzw.
seine Frageweise nochmal zu überdenken.


Peter

von peter dannegger (Gast)


Lesenswert?

@flyingwolf,

"Bei genauerem überlegen ist das Argument der unsauberen
Programmierung berechtigt, wenn die Interruptroutine fröhlich Zeichen
in einen String reinschreibt während der gerade in Bearbeitung
ist..."


So unsauber ist das garnicht.

Ich benutze auch einen UART-Puffer, den ich auswerte, wärend schon
fleißig die nächsten Bytes reinkommen können. Wenn das sauber
programmiert ist (kritische Stellen mit Interruptsperre kapseln),
klappt das prima. Ansonsten wären das auch nur logische Fehler, die der
Compiler nicht erkennen kann und daher nicht anmeckert.

Dein Problem dürfte also woanders liegen.


Peter

von flyingwolf (Gast)


Lesenswert?

das geht

 extern volatile char instring[100], stringend;
// extern volatile int stringcounter;

SIGNAL(SIG_UART0_RECV)
{
serial_in = UDR0;
strcat((char*)&instring,(char*)&serial_in);
if (serial_in == 0x0d) stringend =1;
}

das geht nicht
 extern volatile char instring[100], stringend;
// extern volatile int stringcounter;

SIGNAL(SIG_UART0_RECV)
{
serial_in = UDR0;
strcat(instring,serial_in);
if (serial_in == 0x0d) stringend =1;
}
Christoph hat es, glaube ich, ganz gut auf den Punkt gebracht, wenn ich
auch nur Fragmente davon verstanden habe..
Aber in einer ruhigen minute werde ich mich mal hinsetzen und versuchen
rauszubekommen, was der Hinergrund des Ganzen ist

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


Lesenswert?

> das geht

> strcat((char*)&instring,(char*)&serial_in);

Nein, es maskiert das Problem nur.  Der Typecast sagt dem Compiler:
,,Lieber Compiler, von dem, was ich hier schreibe, weiß ich genau, was
ich gerade mache.''  Eigentlich weißt du's aber wohl eher nicht.
;-)

Lass doch das volatile am Zeichen-Puffer einfach weg.  Der Code wird
nicht besser und nicht schlechter damit.  Wenn du ein volatile
brauchst, dann irgendwo an einer Semaphore-Variablen im Sinne dessen,
was Peter geschrieben hat, damit Interruptservice und main thread im
Konfliktfall gegeneinander verriegelt werden.

von Mark de Jong (Gast)


Lesenswert?

@flyingwolf:

Warum benutzt Du volatile eigentlich?
Volatile ist dafür da, damit der compiler es nicht weg optimized.

versuch mal folgendes:
/* Start of code */
extern char instring[100];
extern int stringcounter;

char serial_in;

SIGNAL(SIG_UART0_RECV)
{
serial_in = UDR0;
strncat( (char *)&instring, (char *)&serial_in, 1);
if (serial_in == 0x0d) stringend =1;
}
/*end of code */

Ich würde strncat nehmen, weil du nur ein char an ein string kleben
möchtest, oder nicht.

Mit strcat würdest Du einen string an instring kleben, also einen
zeichen reihe der mit 0x00 eindet.

Ich würde auch noch testen ob ich noch platz im instring buffer habe,
sonst landen die zeichen irgendwo anders, die variable die nach
instring im specher steht.

Dies ist ein oft gemachte fehler.

Grüße Mark,

von pittbull_nt@yahoo.com (Gast)


Lesenswert?

>> Wenn das sauber programmiert ist (kritische Stellen mit
>> Interruptsperre kapseln), klappt das prima...

naja, manchmal geht das aber besser man verwendet lock-free algorithmen

von Karl H. (kbuchegg)


Lesenswert?

>> das geht
>> strcat((char*)&instring,(char*)&serial_in);
>
> Nein, es maskiert das Problem nur.  Der Typecast sagt dem Compiler:
> ,,Lieber Compiler, von dem, was ich hier schreibe, weiß ich genau,
> was ich gerade mache.''  Eigentlich weißt du's aber wohl eher
> nicht. ;-)

(gedacht fuer flyingwolf, Jörg weiss das eh alles)

Er weiss es sogfar mit Sicherheit nicht.
Auch wenn er die Deklaration von serial_in nicht gezeigt
hat, so kann man doch davon ausgehen, dass dies ein
einzelner char ist. Also sowas:

  char serial_in;

oder meinetwegen auch

  volatile char serial_in;

Egal welche Version, es ist in jedem Fall falsch. Den strcat
erwartet als zweites Argument nun mal kein einzelnes char,
sondern einen Pointer auf ein Array von Charactern, in
denen ein String (also eine Zeichenkette die mit '\0'
abgeschlossen ist) abgelegt ist. Und die ist nun mal
nicht da. Daher wird strcat() da auf Teufel komm raus
versuchen an instring Zeichen anzuhaengen. Je nachdem
was sich da noch zusaetzlich im Speicher abspielt ist
es ohne weiters moeglich, dass instring kraeftig ueberlaufen
wird und du dir ne Menge anderen Speicher zerschiesst.

Fazit: Die String-Funktionen (die str.. Familie) sind
eigentlich sehr einfach zu benutzen. Nur muss man ein
paar Grundlagen kennen. Beachtet man diese nicht, dann
ist das Ergebnis frustrierend, da mann auf keinen grünen
Zweig kommt. Woher hat man diese Grundlagen: Literaturstudium.

von flyingwolf (Gast)


Lesenswert?

@jörg
>>Lass doch das volatile am Zeichen-Puffer einfach weg.

ich habe mal gelesen, (hier im Forum) das Variablen, die in
Interruptroutinen verknotet werden eine volatile davor bekommen, damit
sie die Interruptroutine nicht hinterrücks verbiegen kann ohne dass das
laufende Pragramm davon was mitbekommt.

Ehrlich gesagt schleppe ich die volatile eigentlich nur noch aus einem
Denkfehler heraus mit mir rum. Ich hatte vorher nur das Zeichen
übergeben und schon während des Empfangs verarbeitet (braucht weniger
speicher) damit das Zeichen nicht doppelt verarbeitet wurde, musste es
nach der Verarbeitung 0x00 gesetzt werden...

zur Optimierung der Geschichte werde ich mir die Vorschläge hier alle
jedenfalls mal zu Gemüte führen und ausprobieren ...

von peter dannegger (Gast)


Lesenswert?

@pittbull_nt@yahoo.com

"naja, manchmal geht das aber besser man verwendet lock-free
algorithmen"

Das geht aber eben nicht immer.


Ich verwende für die UART immer einen linear-Puffer, dann kann ich
gleich den Parser über den Puffer jagen und muß nicht doppelten
Speicherplatz belegen und umständlich umkopieren.

Am Ende des Kommandos muß ich nur das Kommado entfernen, d.h. alle
zwischenzeitlich empfangenen Bytes an den Anfang verschieben. Und
dieses Verschieben muß unter UART-Interruptsperre erfolgen, sonst
knallt mir die UART die Zeichen sonstwohin.


Peter

von Unbekannter (Gast)


Lesenswert?

Leute, was ihr hier mit den Buffern in den Interrupt-Funktionen macht,
führt früher oder später zu Bugs.

Wichtige Regel (ohne Ausnahme!):

  Werden globale Variablen von Interrupt-Funktionen gelesen
  oder gerschrieben, so müssen diese Variablen 'volatile'
  deklariert werden.

Es gibt keine Ausnahme.

Und das hat natürlich auch zur Folge, dass stdlib-Funktionen nicht
verwendet werden dürfen. Denn die wissen ja nicht dass sie auf einen
volatile-Buffer arbeiten sollen. Der Compiler warnt also vollkommen
korrekt und der Compiler darf in diesem Fall falschen Code
produzieren.

Die Lösung ist aber auch nicht weiter kompliziert, und sieht ungefähr
so aus:

  #define BUFFER_SIZE 100
  volatile char buffer[BUFFER_SIZE];
  volatile char volatile * buffer_next = buffer;

  int is_buffer_empty()
  {
    return buffer_next == buffer;
  }

  int is_buffer_full()
  {
    return buffer_next == buffer + sizeof(buffer);
  }

  int buffer_length()
  {
    return buffer_next - buffer;
  }

  int buffer_putc(char c)
  {
    if (is_buffer_full())
      return -1;
    *buffer_next++ = c;
    return 0;
  }

  char buffer_getc()
  {
    if (is_buffer_empty())
      return 0;
    char c = *buffer;
    for(char volatile * p = buffer + 1; p < buffer_next; p++)
      *(p-1) = *p;
    buffer_next--;
    return c;
  }

von Unbekannter (Gast)


Lesenswert?

Achso, buffer_clear() ist natürlich auch trivial:

  void buffer_clear()
  {
    buffer_next = buffer;
  }

Und anstatt der 'int' für die is_buffer_xyz() ist es auf
Mikrocontroller besser 'char' zu verwenden.

Dann kann man noch Teile als 'static' und/oder 'inline'
deklarieren.

Wie gesagt, das ist nur das Gerüst. Und keine Angst, das doppelte
'volatile' usw. ist alles korrekt. ;-)

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


Lesenswert?

> Wichtige Regel (ohne Ausnahme!):

>  Werden globale Variablen von Interrupt-Funktionen gelesen oder
>  gerschrieben, so müssen diese Variablen 'volatile' deklariert
>  werden.

Das halte ich für eine Übertreibung.  Der Compiler ist sehr wohl daran
gebunden, die Werte globaler Variablen irgendwann zurückzuschreiben.
Notgedrungen muss er das spätestens am Ende einer (nicht inline
implementierten) Funktion erledigt haben, d. h. beim Verlassen der ISR
muss der globale Puffer mit den empfangenen Zeichen aktualisiert
worden sein.

Der auswertende Teil darf sich zwar die gelesenen Werte cachen (der
Standard schreibt, er darf davon ausgehen, dass sie sich außerhalb
seines Codes die Werte nicht ändern), aber irgendwann muss auch er
sich die Initialwerte erst einmal aus dem Speicher gelesen haben.

Wenn man nun also erreichte, dass durch eine (sehr wohl `volatile'
deklarierte) Semaphor-Variable sichergestellt ist, dass Aktion 1
(vollständiges Schreiben der in der ISR gelesenen Daten in das globale
Array) vor Aktion 2 (Beginn des ersten Auslesens aus dem globalen
Array) erfolgt, kann man durchaus ohne den übertriebenen Aufwand
deines Vorschlags auskommen.  Schließlich hamm wir's hier nicht
dicke,
wir arbeiten auf einem Microcontroller, auf dem Ressourcen knapp sind.
Wiederbenutzung von RAM-Puffern ohne unnützes Umkopieren ist daher in
vielen Fällen eine grundlegende Forderung (sRAM ist der teuerste Teil
der heutigen Microcontroller).

von Christoph _. (chris)


Lesenswert?

> Und keine Angst, das doppelte 'volatile' usw. ist alles korrekt.
;-)
> [...]
> volatile char volatile * buffer_next

Mag sein, dass ich mich irre, weil ich schon einige Zeit nicht mehr mit
solch rohen Zeigern programmiert habe, aber meiner Meinung nach ist das
zweite volatile an der falschen Stelle; es sollte hinter dem * sein,
damit der Zeiger selbst volatile ist:
volatile char * volatile buffer_next

Außerdem hast du immer noch eine race condition drin, wenn ich mich
nicht täusche.
Angenommen, der Puffer enthält genau zwei Zeichen, buffer_next ist also
buffer + 2 (sei der Puffer "AB"). Angenommen, es wurde gerade die
Schleife in buffer_getc() ausgeführt.
Da buffer_next == buffer + 2 ist, wurde die Schleife exakt ein Mal
durchlaufen, die ersten beiden Zeichen des Puffers sehen nun also so
aus: "BB". Natürlich ist nur das erste 'B' gültig, aber da
buffer_next noch nicht erniedrigt wurde, enthält es zu diesem Zeitpunkt
immer noch die Information "buffer + 2".
Nun tritt ein Interrupt auf, buffer_putc('C') wird aufgerufen. Was
jetzt passiert, ist denke ich klar. buffer_putc schreibt das Zeichen an
die Stelle *(buffer+2), sodass der Puffer nun "BBC" enthält. Dann wird
buffer_next um eins erhöht, Interrupt kehrt zurück, buffer_getc wird
weiter ausgeführt, buffer_next wird wieder um eins erniedrigt. Der
Puffer enthält nun "BBC", buffer_next behauptet, die ersten beiden
Zeichen wären gültig, das heißt buffer_getc wird nacheinander 'B',
'B' und dann nur noch 0 liefern.

von Unbekannter (Gast)


Lesenswert?

Die volatile Semaphor-Veriable wird aber überhaupt nix helfen. Das Lesen
oder Schreiben einer volatile-Variable "syncronsisiert" den Speicher
eben nicht. Andere Teile oder andere Variablen einer Funktion können
dennoch mit lokalen Kopien der globalen Variablen arbeiten.

Es ist richtig, dass die Funktion irgendwann die globalen Variablen
lesen oder schreiben musst. Aber der Compiler kann das machen, wie er
lustig ist.

Gerade Dein Beispiel mit den statisch Funktionen ist ein gutes
Beispiel. Statische Funktionen bestimmter Maximalgröße oder die nur von
einer Stelle aus aufgerufen werden, kann der Compiler auch völlig
korrekt selbst 'inline' erzeugen.

Dann kommen auch noch solche Geschichten wie Tail-Calls und umsortieren
der Statements usw. dazu.

Der springende Punkt ist der, dass Du mit der Semaphor-Variable das
Verhalten des Compilers bezüglich Nicht-Semaphor-Variablen in keinster
Weise beeinflussen kannst.

von Unbekannter (Gast)


Lesenswert?

@Christoph:

Wegen Race-Condition: Natürlich hast Du recht. Die Interrupts müssen
korrekt gesperrt sein. Das müssen sie immer. Es gibt in C eben kein
atomares Read-Modify-Write.

Mit dem volatile-Schlüsselwort könntest Du auch recht haben. Mich haben
auch Zweifel beschlichen, als ich auf "Submit" geklickt habe. Also das
müsste man wirklich nochmals nachlesen...

von Karl H. (kbuchegg)


Lesenswert?

> Also das müsste man wirklich nochmals nachlesen...

Im Grunde ist es ganhz einfach. volatile ist ein 'modifier'
so wie auch 'const'.
Und fuer die gilt: sie wirken immer auf das was links von
ihnen steht. Es sei denn, da ist nichts mehr, dann wirken
sie auf das was rechts von ihnen steht.

Also:

  char const a;      a ist ein konstanter character
  const char a;      a ist ein konstanter character

  char const * a;    a ist ein pointer auf einen const character
  const char * a;    a ist ein pointer auf einen const character

  char * const a;    a ist ein Pointer. Dieser Pointer ist const
                     und zeigt auf einen character

  char const * const a;    a ist ein Pointer. Dieser Pointer ist
                           const und zeigt auf einen const character

  const char * const a;    a ist ein Pointer. Dieser Pointer ist
                           const und zeigt auf einen char, der selbst
                           wieder const ist.

  const char const * a;    a ist ein Pointer. Der Pointer zeigt
                           auf etwas das const ist. Naemlich einen
                           char, der const ist.
                           (Hier hast Du also 2-mal dasselbe aus-
                            gedrueckt. Naemlich dass der char als
                            const anzusehen ist).

Selbiges fuer volatile, bzw. Kombinationen aus const und volatile.

Vereinfacht ausgedrueckt: Fang beim Variablennamen zu lesen an
und arbeite dich nach links durch.

von peter dannegger (Gast)


Lesenswert?

Ich sehe das genauso wie Jörg.

Wenn ich eine Funktion getchar() oder get_command() habe, dann muß
diese den Pufferzeiger einlesen und auswerten ganz ohne volatile.

Und nur die Funktion clear_command() macht eine Interruptsperre, wärend
sie das nächste Kommando wieder an den Anfang des Puffers kopiert.


Wenn ein Interrupthandler viel mit einer Variable hantiert, kann es
sogar effektiver sein, dies nicht als volatile zu deklarieren und dafür
eine extra Funktion zum Auslesen zu nehmen. Diese Funktion darf dann
natürlich nicht inline wegoptimiert werden.


Peter

von Mark de Jong (Gast)


Lesenswert?

@unbekanter:

was meinst Du mit:
"Die volatile Semaphor-Veriable wird aber überhaupt nix helfen."

Das volatile keyword sorgt nur dafür das der compiler denn code nicht
optimiert, weil es durch externe einflusse geändert werden kann.

beispiel 1:
volatile unsigned char *baseAddr;

lsb    = *handle->baseAddr;
middle = *handle->baseAddr;
msb    = *handle->baseAddr;

kann ohne das volatile optimiert werden zu:

lsb = middle = msb = *handle->baseAddr;

mit volatile werde alle drei zuweizungen komplett aufgeführt.

beispiel 2:
volatile int x;
for(x=0; x<100; y++);

ohne volatile kann der compiler denken das der code keine funktion hat,
wenn "x" nicht weiter benutzt wird, und die schleifen
raus-optimieren.

Ansonsten mal suchen nach "volatile" bei google.

Grüße Mark,

Grüße Mark,

von SuperUser (Gast)


Lesenswert?

@Karl-Heinz
Danke für deinen Beitrag. Ich hatte nämlich immer Probleme mir zu
Merken worauf welches const/volatile denn nun wirkt.

Eigenentlich ganz einfach. Ich glaube ich persöhnlich werde in Zukunft
vermeiden, rechts der Typen den "modifier" zu schreiben. (habe ich
bisher immer gemacht) Dann ist die Logic sofort klar...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.