OMG! Man lernt nie aus. Type-qualifiers und static in array declarators ab C99
-
Gestern blätterte ich im C99 Rationale und erblickte im Abschnitt
6.7.5.2 Array declarators
auf Seite 90 etwas mir vollkommen Unbekanntes:A new feature of C99: The static storage class specifier and any of the type-qualifiers,
restrict, const or volatile, can appear inside the [ and ] that are used to declare an
array type, but only in the outermost array type derivation of a function parameter.Sowas kam mir noch nie unter. Nachdem der erste Schock überwunden war, wurde mir der Sinn dahinter aber schnell klar. Da Arrays als Funktionsparameter in Zeiger auf ihr erstes Element zerfallen, sollte es auch möglich sein, diese mit type-qualifiers zu versehen. Der Rationale bringt im Anschluss daran ja auch gleich einige Beispiele und erklärt, was man hier mit
static
anfangen kann:The static keyword provides useful information about the intent of function parameters.
Consider:void fadd(double *a, const double *b) { int i; for (i = 0; i < 10; i++) { if (a[i] < 0.0) return; a[i] += b[i]; } return; }
It would be a significant advantage on some systems for the translator to initiate, at the
beginning of the function, prefetches or loads of the arrays that will be referenced through the
5 parameters. There is no way in C89 for the user to provide information to the translator about
how many elements are guaranteed to be available.In C99, the use of the static keyword in:
void fadd(double a[static 10], const double b[static 10]) { int i; for (i = 0; i < 10; i++) { if (a[i] < 0.0) return; a[i] += b[i]; } return; }
guarantees that both the pointers a and b provide access to the first element of an array
containing at least ten elements. The static keyword also guarantees that the pointer is not
NULL and points to an object of the appropriate effective type. It does not, however, guarantee
that a and b point to unique, non-overlapping objects. The restrict keyword is used for
that purpose as in:void fadd(double a[static restrict 10], const double b[static restrict 10]) { int i; for (i = 0; i < 10; i++) { if (a[i] < 0.0) return; a[i] += b[i]; } return; }
This function definition specifies that the parameters a and b are restricted pointers. This is
information that an optimizer can use, for example, to unroll the loop and reorder the loads and
stores of the elements referenced through a and b.The const keyword can be used to indicate that the pointer will always point to the same array
object. The function declaration:void f(double x[const], const double y[const]);
is another way of declaring:
void f(double * const x, const double * const y);
There does not appear to be much value in using volatile to qualify an array function
parameter.Was haltet ihr davon? Benutzt ihr dieses Feature? Oder sind bei euch Funktionsparameter schon immer gleich Zeiger anstatt Arrays gewesen?
-
Das sind extreme Mikrooptimierungen. Wann immer ich mal damit rumgespielt habe, war ich vom Ergebnis enttäuscht. Sprich: Es bringt wohl eher etwas in konstruierten Beispielen, in echten Programmen bei mir bisher nicht.
-
Hm.
Ich hab's nicht ausprobiert, aber ich bin überzeugt dassrestrict
- bei bestimmten Funktionen, und wenn Inlining nicht möglich ist - schon viel bringt.
Die anderen vermutlich nicht.
-
Das sind extreme Mikrooptimierungen. Wann immer ich mal damit rumgespielt habe, war ich vom Ergebnis enttäuscht. Sprich: Es bringt wohl eher etwas in konstruierten Beispielen, in echten Programmen bei mir bisher nicht.
-
hustbaer schrieb:
Ich hab's nicht ausprobiert, aber ich bin überzeugt dass
restrict
- bei bestimmten Funktionen, und wenn Inlining nicht möglich ist - schon viel bringt.Es ist nicht so, dass ich mir nicht vorstellen kann, dass es solche Fälle gibt, es ist, dass diese irgendwie nie vorkommen. Konstruiertes Beispiel eben. Wenn ich mal einen Grafikkartentreiber schreibe, mag das ganz anders aussehen, aber wer macht das schon? Meine Programme sind durch andere Probleme limitiert und meine innersten Schleifen fast immer inline, so dass es selbst theoretisch nichts bringt. Gerade letzter Punkt ist wohl wichtig: Es ist heutzutage derart einfach, die kritischen Schleifen inline zu halten, sei es durch Compilerfeatures (Optimierung mit Blick auf das ganze Programm) oder Sprachfeatures (Templates), dass eigentlich immer bekannt ist, auf welches konkrete Objekt ein Zeiger zeigt. Das ist wesentlich einfacher, als sich selber darum zu kümmern.
-
Cool, die kannte ich noch nicht!
Statt nur Optimierungsmöglichkeiten zu bieten, erlauben die qualifier doch auch compile time Warnungen/Fehler. Ich habe z.B. immerint* const a
stattint a[]
geschrieben, da ich generell alles const’e was nich geändert wird und die letztere Syntax elaubte es mir nicht den Zeiger zu const’en. Nun kann ichint a[const]
schreiben, wenn ich deutlich zu machen möchte, dass es sich um ein Array handelt und der Zeiger nicht verändert werden soll.
Und mit demstatic
dazu ermögliche ich es dem Compiler auch mich zu warnen, wennNULL
als Parameter übergeben wird und dieser nichtNULL
sein darf (leider ersetzt das nicht die Laufzeitprüfung aufNULL
). Für Angabe einer Mindestgröße halte ich es hingegen für nicht notwendig, da man eigentlich immer die Arraygröße als separaten Parameter an Funktionen übergibt (höchstens wieder für Compilerwarnungen, wenn man wirklich eine Mindestgröße braucht, mir fällt da aber auf Anhieb kein Beispiel ein).
restrict
ist genau wieconst
: immer, wenn möglich.
-
Hm, warum geht das in C++ nicht (mehr)? Konnte sich das nicht durchsetzen oder warum hat man hier auf abwärtskompatibilität verzichtet?
-
Biolunar schrieb:
leider ersetzt das nicht die Laufzeitprüfung auf
NULL
Die, die dir Schutzverletzung ausführt, wenn man drauf zugreift? Dachte, die sei recht kostenlos.
Biolunar schrieb:
Für Angabe einer Mindestgröße halte ich es hingegen für nicht notwendig, da man eigentlich immer die Arraygröße als separaten Parameter an Funktionen übergibt (höchstens wieder für Compilerwarnungen, wenn man wirklich eine Mindestgröße braucht, mir fällt da aber auf Anhieb kein Beispiel ein).
Bei Spielen wie Schach hat man [8] fest, bei manchen Datenbanken kennt man die Feldlängen zur Compilezeit, der Numbercruncher könnte Probleme zerlegen bis vielleicht [256], um ein [256] mal [256] dann handoptimiert bereitzustellen, Filesystems haben germe mal [2048] oder so. Außerdem kann man öfters mal absturzfreie Zugriffe auf [256] garantieren, obwohl das dazu übergebene len == 0 sein könnte.
Biolunar schrieb:
restrict
ist genau wieconst
: immer, wenn möglich.Wann nicht?
-
SeppJ schrieb:
hustbaer schrieb:
Ich hab's nicht ausprobiert, aber ich bin überzeugt dass
restrict
- bei bestimmten Funktionen, und wenn Inlining nicht möglich ist - schon viel bringt.Es ist nicht so, dass ich mir nicht vorstellen kann, dass es solche Fälle gibt, es ist, dass diese irgendwie nie vorkommen. Konstruiertes Beispiel eben. Wenn ich mal einen Grafikkartentreiber schreibe, mag das ganz anders aussehen, aber wer macht das schon? Meine Programme sind durch andere Probleme limitiert (...)
Ja OK da wirst du Recht haben.
In dem Code den die meisten von uns schreiben wirdrestrict
vermutlich wenig bringen.
Wenn man das ganze aber auf Libraries erweitert die von vielen verwendet werden, wäre ich mir schon nicht mehr so sicher.
z.B. BLAS Libraries.
-
Biolunar schrieb:
leider ersetzt das nicht die Laufzeitprüfung auf
NULL
So wie das im Standard formuliert ist
If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element of an array with at least as many elements as specified by the size expression.
Resultiert allein der Aufruf mit NULL in UB.