Qual è la ragione per cui lo standard C deve considerare la costanza in modo ricorsivo?

9

Lo standard C99 dice in 6.5.16: 2:

An assignment operator shall have a modifiable lvalue as its left operand.

e in 6.3.2.1:1:

A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

Ora, consideriamo un% nonconst struct con un campo const .

typedef struct S_s {
    const int _a;
} S_t;

Per standard, il codice seguente è un comportamento non definito (UB):

S_t s1;
S_t s2 = { ._a = 2 };
s1 = s2;

Il problema semantico con questo è che l'entità che la racchiude ( struct ) dovrebbe essere considerata scrivibile (non di sola lettura), a giudicare dal tipo dichiarato dell'entità ( S_t s1 ), ma non dovrebbe essere considerata scrivibile dal testo dello standard (le 2 clausole in alto) a causa del const campo _a . Lo standard rende poco chiaro per un programmatore che legge il codice che l'assegnazione è in realtà un UB, perché è impossibile dire che w / o la definizione di struct S_s ... S_t tipo.

Inoltre, l'accesso in sola lettura al campo viene applicato solo sintatticamente in ogni caso. Non c'è alcun modo in cui alcuni campi di const di% nonconst% di co_de vadano realmente posizionati in una memoria di sola lettura. Ma una tale formulazione di standard mette fuori legge il codice che deliberatamente elude il struct qualificatore di campi nelle procedure di accesso di questi campi, come in questo modo ( È una buona idea per qualificare i campi della struttura in C? ):

(*)

#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);
}

Quindi, per qualche motivo, per un intero const a sola lettura è sufficiente dichiararlo struct

const S_t s3;

Ma per un intero const non è un read-only, non è sufficiente dichiararlo senza struct .

Quello che penso sarebbe meglio, è:

  1. Per limitare la creazione di strutture non%% co_de con i campi const e emettere una diagnostica in questo caso. Ciò renderebbe chiaro che il const contenente i campi di sola lettura è di sola lettura.
  2. Per definire il comportamento in caso di scrittura in un campo const appartenente a una struttura non struct in modo da rendere il codice sopra (*) conforme allo standard.

Altrimenti il comportamento non è coerente e difficile da capire.

Quindi, qual è la ragione per cui C Standard considera in modo ricorsivo const -ness, come lo mette?

    
posta Michael Pankov 30.12.2013 - 10:04
fonte

2 risposte

4

So, what's the reason for C Standard to consider const-ness recursively, as it puts it?

Da una prospettiva tipologica da sola, non farlo sarebbe sbagliato (in altre parole: terribilmente rotto e intenzionalmente inaffidabile).

E questo a causa di ciò che "=" significa su una struttura: è un compito ricorsivo. Ne consegue che alla fine hai un s1._a = <value> che accade "all'interno delle regole di battitura". Se lo standard lo consente per i campi "nested" const , aggiunge una seria incoerenza nella sua definizione di sistema del tipo come contraddizione esplicita (potrebbe anche buttare via la funzione const , poiché è diventata inutile e inaffidabile proprio definizione).

La tua soluzione (1), per quanto ho capito, costringe inutilmente l'intera struttura a const ogni volta che uno dei suoi campi è const . In questo modo, s1._b = b sarebbe illegale per un campo non-const ._b su un non-const s1 contenente un const a .

    
risposta data 30.12.2013 - 20:05
fonte
2

Il motivo è che i campi di sola lettura sono di sola lettura. Nessuna grande sorpresa lì.

Si suppone erroneamente che l'unico effetto sia sul posizionamento nella ROM, il che è effettivamente impossibile quando ci sono campi adiacenti non const. In realtà, gli ottimizzatori possono assumere che const espressioni non vengano scritte e ottimizzare in base a ciò. Ovviamente quell'assunzione non vale quando esistono alias non const.

La tua soluzione (1) rompe il codice legale e ragionevole esistente. Non succederà. La tua soluzione (2) rimuove praticamente il significato di const sui membri. Anche se questo non infrange il codice esistente, sembra che manchi una logica.

    
risposta data 30.12.2013 - 16:49
fonte

Leggi altre domande sui tag