Der C99-Standard sagt in 6.5.16: 2:
Ein Zuweisungsoperator muss einen modifizierbaren Wert als linken Operanden haben.
und in 6.3.2.1:1:
Ein modifizierbarer Wert ist ein Wert, der keinen Array-Typ hat, keinen unvollständigen Typ hat, keinen const-qualifizierten Typ hat und, wenn es sich um eine Struktur oder Vereinigung handelt, kein Mitglied hat (einschließlich rekursiv eines Mitglieds) oder Element aller enthaltenen Aggregate oder Gewerkschaften) mit einem const-qualifizierten Typ.
Betrachten wir nun ein Nicht- const
struct
mit- const
Feld.
typedef struct S_s {
const int _a;
} S_t;
Standardmäßig ist der folgende Code undefiniertes Verhalten (UB):
S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;
Das semantische Problem dabei ist, dass die einschließende Entität ( struct
) als beschreibbar (nicht schreibgeschützt) betrachtet werden sollte, gemessen am deklarierten Typ der Entität ( S_t s1
), aber nicht als beschreibbar durch den Wortlaut des Standards (die 2 Klauseln) oben) wegen des const
Feldes _a
. Der Standard macht es für einen Programmierer, der den Code liest, unklar, dass die Zuweisung tatsächlich eine UB ist, da es unmöglich ist, dies ohne die Definition des struct S_s ... S_t
Typs zu sagen .
Darüber hinaus wird der schreibgeschützte Zugriff auf das Feld ohnehin nur syntaktisch erzwungen. Es gibt keine Möglichkeit, dass einige const
Felder, die nicht vorhanden const
struct
sind, wirklich schreibgeschützt gespeichert werden. Ein solcher Wortlaut von Standard verbietet jedoch den Code, der das const
Qualifikationsmerkmal von Feldern in Accessor-Prozeduren dieser Felder absichtlich wegwirft ( Ist es eine gute Idee, die Strukturfelder in C zu qualifizieren? ):
(*)
#include <stdlib.h>
#include <stdio.h>
typedef struct S_s {
const int _a;
} S_t;
S_t *
create_S(void) {
return calloc(sizeof(S_t), 1);
}
void
destroy_S(S_t *s) {
free(s);
}
const int
get_S_a(const S_t *s) {
return s->_a;
}
void
set_S_a(S_t *s, const int a) {
int *a_p = (int *)&s->_a;
*a_p = a;
}
int
main(void) {
S_t s1;
// s1._a = 5; // Error
set_S_a(&s1, 5); // OK
S_t *s2 = create_S();
// s2->_a = 8; // Error
set_S_a(s2, 8); // OK
printf("s1.a == %d\n", get_S_a(&s1));
printf("s2->a == %d\n", get_S_a(s2));
destroy_S(s2);
}
Aus irgendeinem Grund reicht es aus, ein Ganzes struct
als schreibgeschützt zu deklarierenconst
const S_t s3;
Aber damit ein Ganzes struct
nicht schreibgeschützt ist, reicht es nicht aus, es ohne zu deklarieren const
.
Was ich für besser halte, ist entweder:
- Einschränkung der Erstellung von Nichtstrukturen
const
mitconst
Feldern und Ausgabe einer Diagnose in einem solchen Fall. Das würde deutlich machen, dass diestruct
enthaltenden schreibgeschützten Felder selbst schreibgeschützt sind. - Definieren des Verhaltens beim Schreiben in ein
const
Feld, das zu einerconst
Nichtstruktur gehört, um den obigen Code (*) mit dem Standard kompatibel zu machen.
Ansonsten ist das Verhalten nicht konsistent und schwer zu verstehen.
Was ist der Grund für C Standard, const
-ness rekursiv zu betrachten , wie es heißt?