อะไรคือเหตุผลที่มาตรฐาน C พิจารณาข้อเสียแบบวนซ้ำ?


9

มาตรฐาน C99 ระบุไว้ใน 6.5.16: 2:

ผู้ประกอบการที่ได้รับมอบหมายจะต้องมีค่า lvalue ที่สามารถแก้ไขได้เป็นตัวถูกดำเนินการด้านซ้าย

และใน 6.3.2.1:1:

lvalue ที่แก้ไขได้คือ lvalue ที่ไม่มีประเภทอาร์เรย์, ไม่มีประเภทที่ไม่สมบูรณ์, ไม่มีประเภทที่ผ่านการรับรองและถ้าเป็นโครงสร้างหรือสหภาพไม่มีสมาชิกใด ๆ (รวมถึงสมาชิกแบบเรียกซ้ำสมาชิกใด ๆ ) หรือองค์ประกอบของมวลรวมหรือสหภาพที่มีอยู่ทั้งหมด) ที่มีประเภทที่ผ่านการรับรอง

ตอนนี้ขอพิจารณาไม่ใช่const structมีconstข้อมูล

typedef struct S_s {
    const int _a;
} S_t;

ตามมาตรฐานรหัสต่อไปนี้คือพฤติกรรมที่ไม่ได้กำหนด (UB):

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

ปัญหาทางความหมายของเรื่องนี้คือสิ่งที่แนบมาเอนทิตี้ของ ( struct) ควรพิจารณาเขียนได้ (ไม่ใช่อ่านอย่างเดียว), ตัดสินตามประเภทของกิจการที่ประกาศ ( S_t s1), แต่ไม่ควรพิจารณาเขียนโดยถ้อยคำของมาตรฐาน (2 ข้อ ด้านบน) เพราะฟิลด์const _aมาตรฐานทำให้ไม่ชัดเจนสำหรับโปรแกรมเมอร์ที่อ่านรหัสว่าการมอบหมายนั้นเป็น UB จริง ๆ เพราะมันเป็นไปไม่ได้ที่จะบอกว่าไม่มีนิยามของstruct S_s ... S_tประเภท

ยิ่งกว่านั้นการเข้าถึงฟิลด์แบบอ่านอย่างเดียวจะถูกบังคับใช้ทางไวยากรณ์เท่านั้น ไม่มีทางที่บางconstฟิลด์ที่ไม่ใช่const structจะถูกนำไปไว้ในที่เก็บข้อมูลแบบอ่านอย่างเดียว แต่การใช้ถ้อยคำมาตรฐานเช่นรหัสที่จงใจโยนconstคุณสมบัติของเขตข้อมูลในขั้นตอนการเข้าถึงของเขตข้อมูลเหล่านี้เช่น ( มันเป็นความคิดที่ดีที่จะ const - ปรับสาขาของโครงสร้างใน 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);
}

ดังนั้นด้วยเหตุผลบางอย่างการstructที่จะอ่านอย่างเดียวทั้งหมดก็เพียงพอที่จะประกาศได้const

const S_t s3;

แต่สำหรับคนstructที่ไม่ใช่ผู้อ่านอย่างเดียวมันไม่เพียงพอที่จะประกาศว่าconstไม่มี

สิ่งที่ฉันคิดว่าน่าจะดีกว่าก็คือ:

  1. เพื่อ จำกัด การสร้างconstโครงสร้างที่ไม่มีconstฟิลด์และออกการวินิจฉัยในกรณีเช่นนี้ นั่นจะทำให้ชัดเจนว่าstructเขตข้อมูลแบบอ่านอย่างเดียวที่มีนั้นเป็นแบบอ่านอย่างเดียว
  2. เพื่อกำหนดพฤติกรรมในกรณีที่เขียนไปยังconstเขตข้อมูลที่เป็นของ non- conststruct ว่าจะทำให้รหัสข้างต้น(*) เป็นไปตามมาตรฐาน

มิฉะนั้นพฤติกรรมจะไม่สอดคล้องและเข้าใจยาก

ดังนั้นอะไรคือเหตุผลที่ C Standard ต้องคำนึงถึงการใช้ความconstรู้สึกแบบเรียกซ้ำ


พูดตามตรงฉันไม่เห็นคำถามเลย
Bart van Ingen Schenau

@BartvanIngenSchenau แก้ไขเพื่อเพิ่มคำถามที่ระบุไว้ในหัวข้อในตอนท้ายของร่างกาย
Michael Pankov

1
ทำไมต้องลงคะแนน?
Michael Pankov

คำตอบ:


4

ดังนั้นอะไรคือสาเหตุที่มาตรฐาน C พิจารณาข้อเสียแบบวนซ้ำตามที่วางไว้

จากมุมมองประเภทเพียงอย่างเดียวการไม่ทำเช่นนั้นจะไม่ปลอดภัย (ในคำอื่น ๆ : หักอย่างน่ากลัวและไม่น่าเชื่อถือโดยเจตนา)

และนั่นเป็นเพราะความหมาย "=" ในโครงสร้าง: มันเป็นการกำหนดแบบเรียกซ้ำ ตามมาว่าในที่สุดคุณก็มีs1._a = <value>สิ่งที่เกิดขึ้น "ในกฎการพิมพ์" หากมาตรฐานอนุญาตให้สิ่งนี้สำหรับconstฟิลด์"ซ้อน" การเพิ่มความไม่ลงรอยกันอย่างรุนแรงในคำจำกัดความของระบบประเภทเป็นความขัดแย้งที่ชัดเจน (เช่นกันอาจทำให้constฟีเจอร์นี้หมดไปเนื่องจากมันเพิ่งไร้ประโยชน์และไม่น่าเชื่อถือตามคำจำกัดความ)

วิธีแก้ปัญหาของคุณ (1) เท่าที่ฉันเข้าใจจะบังคับให้โครงสร้างทั้งหมดเป็นconstเมื่อใดก็ตามที่ฟิลด์ใดฟิลด์หนึ่งของมันไม่constจำเป็น ด้วยวิธีนี้s1._b = bจะผิดกฎหมายสำหรับไม่ใช่ const ._bฟิลด์ไม่ใช่ const ที่มีs1const a


ดี. Cแทบจะไม่ได้มีระบบเสียงประเภท (เช่นกรณีมุมมากขึ้นผูกติดกันในช่วงหลายปีที่ผ่านมา) นอกจากนี้วิธีการอื่น ๆ ที่จะนำที่ได้รับมอบหมายให้เป็นคือstruct memcpy(s_dest, s_src, sizeof(S_t))และฉันค่อนข้างแน่ใจว่ามันเป็นวิธีการที่เกิดขึ้นจริง และในกรณีเช่นนี้แม้แต่ "ระบบพิมพ์" ที่มีอยู่ก็ไม่ได้ห้ามคุณทำเช่นนั้น
Michael Pankov

2
จริงแท้แน่นอน. ฉันหวังว่าฉันไม่ได้บอกเป็นนัยว่าระบบเสียงของ C นั้นมี แต่เสียงเท่านั้นที่จงใจทำให้ความหมายที่เฉพาะเจาะจงนั้นไม่มีเจตนาที่จะเอาชนะมันอย่างจงใจ ยิ่งไปกว่านั้นแม้ว่าระบบประเภทของ C จะไม่บังคับใช้อย่างรุนแรง แต่วิธีการฝ่าฝืนมันมักจะชัดเจน (พอยน์เตอร์, การเข้าถึงโดยอ้อม, การปลดเปลื้อง) - ถึงแม้ว่าเอฟเฟกต์ของมันจะมีความยากและติดตามยาก ดังนั้นการมี "รั้ว" ที่ชัดเจนในการฝ่าฝืนพวกเขาแจ้งดีกว่าการมีความขัดแย้งในคำจำกัดความของตัวเอง
Thiago Silva

2

เหตุผลคือฟิลด์อ่านอย่างเดียวเป็นแบบอ่านอย่างเดียว ไม่แปลกใจเลยที่นั่น

คุณเข้าใจผิดคิดว่าเอฟเฟ็กต์เพียงอย่างเดียวคือการวางใน ROM ซึ่งเป็นไปไม่ได้แน่นอนเมื่อมีฟิลด์ที่ไม่ใช่ const ติดกัน ในความเป็นจริงเครื่องมือเพิ่มประสิทธิภาพอาจถือว่าconstนิพจน์ไม่ได้ถูกเขียนและปรับให้เหมาะสมตามนั้น แน่นอนว่าการสันนิษฐานนั้นไม่ถือเมื่อนามแฝงที่ไม่ใช่ const มีอยู่

โซลูชันของคุณ (1) ใช้รหัสที่ถูกต้องตามกฎหมายและสมเหตุสมผล นั่นจะไม่เกิดขึ้น ทางออกของคุณ (2) ลบความหมายของconstสมาชิก แม้ว่าสิ่งนี้จะไม่ทำลายรหัสที่มีอยู่ แต่ดูเหมือนว่าจะไม่มีเหตุผล


ฉันแน่ใจว่า 90% เพิ่มประสิทธิภาพอาจไม่คิดว่าconstสาขาที่ไม่ได้เขียนไปเพราะคุณสามารถใช้memsetหรือmemcpyและว่าแม้จะเป็นไปตามมาตรฐาน (1) สามารถนำไปใช้เป็นอย่างน้อยที่สุดคำเตือนเพิ่มเติมเปิดใช้งานโดยธง (2) 's เหตุผลก็คือว่ากันว่า - มีวิธีที่เป็นส่วนประกอบที่ไม่structได้รับการพิจารณาไม่สามารถเขียนได้เมื่อโครงสร้างทั้งหมดเป็นเขียนได้
Michael Pankov

"การวินิจฉัยทางเลือกที่พิจารณาโดยธง" จะเป็นข้อกำหนดเฉพาะสำหรับมาตรฐานที่ต้องการ นอกจากนี้การตั้งค่าสถานะจะยังคงทำลายรหัสที่มีอยู่ดังนั้นไม่มีใครจะรบกวนการตั้งค่าสถานะและมันจะเป็นจุดสิ้นสุด สำหรับ (2) 6.3.2.1:1 ระบุสิ่งที่ตรงกันข้าม: โครงสร้างทั้งหมดไม่สามารถเขียนได้เมื่อใดก็ตามที่องค์ประกอบหนึ่งเป็น อย่างไรก็ตามส่วนประกอบอื่น ๆอาจยังสามารถเขียนได้ cf เลย C ++ ซึ่งได้กำหนดยังoperator=ในแง่ของการเป็นสมาชิกและดังนั้นจึงไม่ได้กำหนดเมื่อหนึ่งในสมาชิกคือoperator= constC และ C ++ ยังคงใช้งานได้ที่นี่
MSalters

@constantius - ความจริงที่ว่าคุณสามารถทำบางสิ่งบางอย่างเพื่อจงหลีกเลี่ยงความเป็นสมาชิกของสมาชิกไม่ใช่เหตุผลที่เครื่องมือเพิ่มประสิทธิภาพจะเพิกเฉยต่อความรู้สึกนั้น คุณสามารถทิ้งความใสที่อยู่ภายในฟังก์ชั่นออกไปทำให้คุณเปลี่ยนสิ่งต่างๆได้ แต่เครื่องมือเพิ่มประสิทธิภาพในบริบทการโทรยังคงได้รับอนุญาตให้สมมติว่าคุณไม่ต้องการ Constness มีประโยชน์สำหรับโปรแกรมเมอร์ แต่มันก็เป็น god-send สำหรับ optimizer ในบางกรณี
Michael Kohne

ถ้าอย่างนั้นทำไมโครงสร้างที่ไม่สามารถเขียนทับได้นั้นได้รับอนุญาตให้เขียนทับด้วยเช่นmemcpy? สำหรับเหตุผลอื่น - โอเคมันเป็นมรดก แต่ทำไมมันถึงทำแบบนั้นตั้งแต่แรก?
Michael Pankov

1
ฉันยังคงสงสัยว่าความคิดเห็นของคุณเกี่ยวกับmemcpyสิ่งที่ถูกต้อง คำพูดของ AFACIT ของ John Bode ในคำถามอื่นของคุณนั้นถูกต้อง: รหัสของคุณเขียนไปยังวัตถุที่ผ่านการรับรองดังนั้นจึงไม่ใช่การร้องเรียนมาตรฐานสิ้นสุดการสนทนา
MSalters
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.