C standardının tutarlılığı tekrar tekrar düşünmesinin nedeni nedir?


9

C99 standardı 6.5.16: 2'de diyor:

Bir atama operatörü, sol işlenen olarak değiştirilebilir bir değere sahip olacaktır.

ve 6.3.2.1:1'de:

Değiştirilebilir bir değer, dizi türüne sahip olmayan, tamamlanmamış bir türe sahip olmayan, sabit nitelikli bir türe sahip olmayan ve bir yapı veya birleşim olan herhangi bir üyesi olmayan (yinelemeli olarak herhangi bir üye dahil) bir değerdir. veya içerilen tüm agregaların veya birliklerin elemanı).

Şimdi, alan olmayan const structbir constalanı ele alalım .

typedef struct S_s {
    const int _a;
} S_t;

Standart olarak, aşağıdaki kod tanımsız davranıştır (UB):

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

Bununla ilgili anlamsal sorun, ekteki varlık ( struct) 'ın beyan edilen kuruluş türü ( ) ile değerlendirilerek yazılabilir (salt okunur olmayan S_t s1) olarak kabul edilmesi, ancak standardın (2 fıkraları ile yazılabilir olarak kabul edilmemesi) üstte) constalan nedeniyle _a. Standart, bir programcının atamanın gerçekte bir UB olduğunu kodunu okumasını netleştirmez, çünkü struct S_s ... S_ttür tanımının w / o olduğunu söylemek imkansızdır .

Dahası, alana salt okunur erişim sadece sözdizimsel olarak zorunlu kılınmıştır. Bazı constnon-non alanlarının const structsalt okunur depolamaya yerleştirilmesinin hiçbir yolu yoktur . Ancak bu tür standart ifadeler, constbu alanların erişimci prosedürlerindeki alanların niteleyicisini kasıtlı olarak ortadan kaldıran kodu , bu şekilde ( C'deki yapı alanlarını daraltmak iyi bir fikir mi? ):

(*)

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

Bu nedenle, bir nedenden dolayı, bir bütünün structsalt okunur olması için bunu beyan etmek yeterlidirconst

const S_t s3;

Ancak bir bütünün structsalt okunur olmaması için, bunu w / o olarak bildirmek yeterli değildir const.

Ne daha iyi olacağını düşünüyorum, ya da:

  1. constYapı olmayan yapıların oluşturulmasını constalanlarla sınırlamak ve böyle bir durumda teşhis koymak. Bu, structiçerdiği salt okunur alanların salt okunur olduğunu açıkça ortaya koyacaktır.
  2. Yapıya constait olmayan bir alana yazma durumunda davranışı, constyukarıdaki kodu (*) Standart ile uyumlu hale getirecek şekilde tanımlamak .

Aksi takdirde davranış tutarlı ve anlaşılması zor değildir.

Öyleyse, C Standardının, const-sayıya koyduğu gibi, özyineliği tekrar tekrar düşünmesinin nedeni nedir?


Dürüst olmak gerekirse, orada bir soru görmüyorum.
Bart van Ingen Schenau

@BartvanIngenSchenau, vücudun sonunda konuya belirtilen soruyu eklemek için düzenlendi
Michael Pankov

1
Neden inişli çıkışlı?
Michael Pankov

Yanıtlar:


4

Öyleyse, C Standardının sabitliği tekrar tekrar düşünmesinin nedeni nedir?

Sadece bir tür perspektiften bakıldığında, bunu yapmamak mantıklı olmaz (başka bir deyişle: çok kırılmış ve kasıtlı olarak güvenilmez).

Bunun nedeni, bir yapıda "=" ne anlama geldiği için: özyinelemeli bir ödevdir. Sonunda bir s1._a = <value>"yazma kuralları içinde" oluyor. Standart buna "iç içe" constalanlar için izin veriyorsa, açık bir çelişki olarak kendi tür sistem tanımında ciddi bir tutarsızlık ekler (const yararsız ve güvenilmez hale geldiği için bu özelliği ).

Çözümünüz (1), anladığım kadarıyla, tüm yapıyı const, alanlarından biri olduğu zaman olmaya zorlamak zorunda kalıyor const. Bu şekilde, s1._b = bbir const olmayan için yasadışı olacağını ._bolmayan bir yapı üzerinde alanında s1bir içeren const a.


İyi. Czar zor bir ses tipi sistemi vardır (daha çok yıllar boyunca birbirine bağlanmış bir grup köşe kasası gibi). Ayrıca, ödevi a'ya koymanın diğer yolu da structbudur memcpy(s_dest, s_src, sizeof(S_t)). Ve bunun gerçek şekilde uygulandığından eminim. Ve böyle bir durumda mevcut "tip sistemi" bile bunu yapmanızı yasaklamaz.
Michael Pankov

2
Çok doğru. Umarım C'nin tip sisteminin sağlam olduğunu ima etmedim, sadece kasıtlı olarak belirli bir semantiği kasıtlı olarak kasıtlı olarak yenir. Dahası, C'nin tip sistemi güçlü bir şekilde uygulanmasa da, onu ihlal etme yolları genellikle açıktır (işaretçiler, dolaylı erişim, dökümler) - etkileri sıklıkla örtülü ve izlenmesi zor olsa da. Bu nedenle, onları ihlal etmek için açık "çitler" olması, tanımların kendisinde bir çelişkiye sahip olmaktan daha iyi bilgi verir.
Thiago Silva

2

Bunun nedeni, salt okunur alanların salt okunur olmasıdır. Orada büyük bir sürpriz yok.

Yanlışlıkla, tek etkinin bitişik sabit olmayan alanlar olduğunda gerçekten imkansız olan ROM'a yerleştirme üzerinde olduğunu varsayıyorsunuz. Gerçekte, optimize edicilerconst ifadelerin yazılmadığını ve buna göre optimize edebilir. Tabii ki bu varsayım, sabit olmayan takma adlar olduğunda geçerli değildir.

Çözümünüz (1) mevcut yasal ve makul kodu ihlal ediyor. Bu olmayacak. Çözümünüz (2) constüyelerin anlamını hemen hemen ortadan kaldırır . Bu mevcut kodu kırmayacak olsa da, mantıklı bir eksiklik var gibi görünüyor.


Ben% 90 emin optimize değilim olmayabilir varsayalım consther zaman kullanabilirsiniz, çünkü alanlar, yazılmaz memsetveya memcpyve bu hatta Standard uyumlu olacaktır. (1) en azından bir işaret tarafından etkinleştirilen ek uyarı olarak uygulanabilir. Bir bileşeni bir yolu yoktur - (2) 'nin gerekçesi de, tam olarak, yani structtüm yapı zaman dışı yazılabilir olarak kabul edilebilir bir yazılabilir.
Michael Pankov

"Bayrak tarafından belirlenen isteğe bağlı bir teşhis", Standardın talep etmesi için benzersiz bir gerekliliktir. Ayrıca, bayrağı ayarlamak hala mevcut kodu kıracaktı, bu yüzden gerçekte kimse bayrağı rahatsız etmeyecek ve çıkmaz sokak olacaktı. (2), 6.3.2.1:1 belirtir tam tersi gelince: tüm yapısı olmayan yazılabilir bir bileşen olduğu zaman. Bununla birlikte, diğer bileşenler hala yazılabilir olabilir. Krş operator=Üyeler açısından da tanımlayan ve bu nedenle operator=bir üyenin ne zaman olduğunu tanımlayan C ++ const. C ve C ++ burada hala uyumludur.
MSalters

@constantius - Bir üyenin kasıtlı olarak etrafından dolaşmak için bir şeyler yapabilmeniz, optimizatörün bu sabitliği görmezden gelmesinin bir nedeni DEĞİLDİR. Bir işlevin içindeki sabitliği kaldırabilir ve bir şeyleri değiştirmenize izin verebilirsiniz. Ancak, arama bağlamındaki optimize edicinin hala yapmayacağınızı varsaymasına izin verilir. Constness, programcı için yararlıdır, ancak bazı durumlarda optimize edici için bir tanrı gönderimi de sağlar.
Michael Kohne

O zaman neden yazılamaz bir yapının üzerine yazılmasına izin verilir memcpy? Diğer nedenlere gelince - tamam, bu miras, ama neden ilk etapta böyle yapıldı?
Michael Pankov

1
Hakkında yorumunuzun memcpydoğru olup olmadığını hala merak ediyorum . AFACIT John Bode'un diğer sorunuzdaki alıntısı doğrudur: kodunuz sabit bir nesneye yazar ve bu nedenle standart şikayet DEĞİLDİR, tartışma sonu.
MSalters
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.