พฤติกรรมที่ไม่ได้กำหนดทั่วไปทั้งหมดที่โปรแกรมเมอร์ C ++ ควรรู้คืออะไร
พูดเช่น:
a[i] = i++;
พฤติกรรมที่ไม่ได้กำหนดทั่วไปทั้งหมดที่โปรแกรมเมอร์ C ++ ควรรู้คืออะไร
พูดเช่น:
a[i] = i++;
คำตอบ:
NULL
ตัวชี้memcpy
เพื่อคัดลอกบัฟเฟอร์ที่ทับซ้อนกันint64_t i = 1; i <<= 72
ไม่ได้กำหนด)int i; i++; cout << i;
)volatile
หรือsig_atomic_t
ที่การรับสัญญาณlong int
#if
นิพจน์ลำดับที่พารามิเตอร์ฟังก์ชั่นได้รับการประเมินเป็นที่ไม่ระบุพฤติกรรม (สิ่งนี้จะไม่ทำให้โปรแกรมของคุณขัดข้องระเบิดหรือสั่งพิซซ่า ... ไม่เหมือนกับพฤติกรรมที่ไม่ได้กำหนด )
ข้อกำหนดเพียงอย่างเดียวคือพารามิเตอร์ทั้งหมดจะต้องถูกประเมินอย่างสมบูรณ์ก่อนที่จะเรียกใช้ฟังก์ชัน
นี้:
// The simple obvious one.
callFunc(getA(),getB());
สามารถเทียบเท่ากับสิ่งนี้:
int a = getA();
int b = getB();
callFunc(a,b);
หรือสิ่งนี้:
int b = getB();
int a = getA();
callFunc(a,b);
มันสามารถเป็นได้ทั้ง; มันขึ้นอยู่กับคอมไพเลอร์ ผลที่ได้อาจมีความสำคัญขึ้นอยู่กับผลข้างเคียง
คอมไพเลอร์มีอิสระในการจัดลำดับส่วนการประเมินของนิพจน์อีกครั้ง (สมมติว่าความหมายไม่เปลี่ยนแปลง)
จากคำถามเดิม:
a[i] = i++;
// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)
// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:
int rhs = i++;
int lhs& = a[i];
lhs = rhs;
// or
int lhs& = a[i];
int rhs = i++;
lhs = rhs;
ล็อคตรวจสอบอีกครั้ง และหนึ่งความผิดพลาดง่ายที่จะทำ
A* a = new A("plop");
// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'
// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.
// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
a = new A("Plop"); // (Point A).
}
}
a->doStuff();
// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
// Remember (c) has been done thus 'a' is not NULL.
// But the memory has not been initialized.
// Thread 2 now executes doStuff() on an uninitialized variable.
// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
A* tmp = new A("Plop"); // (Point A).
a = tmp;
}
}
a->doStuff();
// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
สิ่งที่ฉันชอบคือ "การเรียกซ้ำแบบไม่สิ้นสุดในการสร้างอินสแตนซ์ของเทมเพลต" เพราะฉันเชื่อว่ามันเป็นสิ่งเดียวที่พฤติกรรมที่ไม่ได้กำหนดเกิดขึ้นในเวลารวบรวม
การกำหนดให้เป็นค่าคงที่หลังจากการดึงconst
ness โดยใช้const_cast<>
:
const int i = 10;
int *p = const_cast<int*>( &i );
*p = 1234; //Undefined
นอกจากนี้ยังไม่ได้กำหนดพฤติกรรม , นอกจากนี้ยังมีที่น่ารังเกียจอย่างเท่าเทียมกันพฤติกรรมการดำเนินงานที่กำหนดไว้
พฤติกรรมที่ไม่ได้กำหนดเกิดขึ้นเมื่อโปรแกรมทำบางสิ่งผลลัพธ์ที่ไม่ได้ระบุโดยมาตรฐาน
พฤติกรรมที่กำหนดไว้ในการนำไปใช้คือการกระทำของโปรแกรมผลลัพธ์ที่ไม่ได้กำหนดโดยมาตรฐาน แต่ต้องใช้เอกสารใดในการนำไปปฏิบัติ ตัวอย่างคือ "ตัวอักษรหลายไบต์ตัวอักษร" จากคำถาม Stack Overflow มีตัวแปลภาษา C ที่ไม่สามารถคอมไพล์ได้หรือไม่ .
พฤติกรรมที่กำหนดโดยการนำไปปฏิบัติจะกัดคุณเมื่อคุณเริ่มพอร์ต (แต่การอัปเกรดเป็นเวอร์ชั่นใหม่ของคอมไพเลอร์ก็คือการย้าย!)
ตัวแปรอาจได้รับการปรับปรุงเพียงครั้งเดียวในนิพจน์ (โดยทางเทคนิคหนึ่งครั้งระหว่างจุดลำดับ)
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
ความเข้าใจพื้นฐานเกี่ยวกับข้อ จำกัด ด้านสิ่งแวดล้อมต่างๆ รายการทั้งหมดอยู่ในส่วน 5.2.4.1 ของข้อกำหนด C ที่นี่มีไม่กี่;
จริง ๆ แล้วฉันรู้สึกประหลาดใจเล็กน้อยที่ขีด จำกัด ของป้ายกำกับกรณี 1,023 รายการสำหรับคำสั่งเปลี่ยนฉันสามารถมองเห็นได้ว่าเกินความเป็นจริงสำหรับโค้ด / lex / parsers ที่สร้างขึ้นค่อนข้างง่าย
หากเกินขีด จำกัด เหล่านี้คุณมีพฤติกรรมที่ไม่ได้กำหนดไว้ (ขัดข้องข้อบกพร่องด้านความปลอดภัย ฯลฯ ... )
ใช่ฉันรู้ว่านี่มาจากข้อกำหนด C แต่ C ++ แบ่งปันการสนับสนุนพื้นฐานเหล่านี้
ใช้memcpy
เพื่อคัดลอกระหว่างพื้นที่หน่วยความจำที่ทับซ้อนกัน ตัวอย่างเช่น:
char a[256] = {};
memcpy(a, a, sizeof(a));
พฤติกรรมนี้ไม่ได้กำหนดตามมาตรฐาน C ซึ่งถูกรวมไว้ด้วยมาตรฐาน C ++ 03
สรุป
1 / # รวมโมฆะ * memcpy (โมฆะ * จำกัด s1, โมฆะ const * จำกัด s2, size_t n);
ลักษณะ
2 / ฟังก์ชั่น memcpy คัดลอกตัวละคร n จากวัตถุที่ชี้ไปโดย s2 ลงในวัตถุที่ชี้ไปโดย s1 หากการคัดลอกเกิดขึ้นระหว่างวัตถุที่ทับซ้อนกันพฤติกรรมจะไม่ได้กำหนดไว้ คืน 3 ฟังก์ชัน memcpy ส่งคืนค่า s1
สรุป
1 #include void * memmove (void * s1, const void * s2, size_t n);
ลักษณะ
2 ฟังก์ชั่น memmove คัดลอกตัวละคร n จากวัตถุที่ชี้ไปโดย s2 ลงในวัตถุที่ชี้ไปโดย s1 การคัดลอกเกิดขึ้นราวกับว่าอักขระ n จากวัตถุที่ชี้ไปยัง s2 ถูกคัดลอกไปยังอาร์เรย์ชั่วคราวที่มีอักขระ n ตัวที่ไม่ทับซ้อนวัตถุที่ชี้ไปที่ s1 และ s2 จากนั้นคัดลอกอักขระ n จากอาร์เรย์ชั่วคราวลงใน วัตถุที่ชี้ไปโดย s1 ผลตอบแทน
3 ฟังก์ชัน memmove ส่งคืนค่า s1
ชนิดเดียวที่ c ++ char
รับประกันขนาดคือ และขนาดคือ 1 ขนาดของประเภทอื่น ๆ ทั้งหมดขึ้นอยู่กับแพลตฟอร์ม
วัตถุระดับเนมสเปซในหน่วยการคอมไพล์ที่แตกต่างกันไม่ควรพึ่งพาซึ่งกันและกันสำหรับการเริ่มต้นเนื่องจากลำดับการเริ่มต้นของพวกเขาจะไม่ได้กำหนด