{0} หมายถึงอะไรเมื่อเริ่มต้นวัตถุ


252

เมื่อ{0}ใช้ในการเริ่มต้นวัตถุมันหมายถึงอะไร? ฉันไม่สามารถหาการอ้างอิงใด ๆ ไปยัง{0}ที่ใดก็ได้และเนื่องจากการจัดฟันแบบปีกกาการค้นหาของ Google ไม่เป็นประโยชน์

รหัสตัวอย่าง:

SHELLEXECUTEINFO sexi = {0}; // what does this do?
sexi.cbSize = sizeof(SHELLEXECUTEINFO);
sexi.hwnd = NULL;
sexi.fMask = SEE_MASK_NOCLOSEPROCESS;
sexi.lpFile = lpFile.c_str();
sexi.lpParameters = args;
sexi.nShow = nShow;

if(ShellExecuteEx(&sexi))
{
    DWORD wait = WaitForSingleObject(sexi.hProcess, INFINITE);
    if(wait == WAIT_OBJECT_0)
        GetExitCodeProcess(sexi.hProcess, &returnCode);
}

ก็ไม่มีรหัสข้างต้นจะผิดพลาดในการรันไทม์

คำตอบ:


302

สิ่งที่เกิดขึ้นที่นี่เรียกว่าการเริ่มต้นรวม นี่คือคำจำกัดความ (ตัวย่อ) ของการรวมจากส่วน 8.5.1 ของข้อกำหนด ISO:

การรวมเป็นอาร์เรย์หรือคลาสที่ไม่มีคอนสตรัคเตอร์ที่ผู้ใช้ประกาศไม่มีสมาชิกข้อมูลส่วนตัวหรือการป้องกันที่ไม่มีการป้องกันไม่มีคลาสพื้นฐานและไม่มีฟังก์ชั่นเสมือน

ทีนี้{0}การเริ่มต้นรวมกันแบบนี้ก็เป็นกลลวง0ทั้งหมด นี่เป็นเพราะเมื่อใช้การเริ่มต้นรวมคุณไม่จำเป็นต้องระบุสมาชิกทั้งหมดและข้อมูลจำเพาะต้องการให้สมาชิกที่ไม่ได้รับการกำหนดค่าเริ่มต้นซึ่งหมายความว่าตั้งเป็น0ประเภทง่าย ๆ

นี่คือคำพูดที่เกี่ยวข้องจากข้อมูลจำเพาะ:

หากมี initializers น้อยกว่าในรายการมากกว่ามีสมาชิกในการรวมแล้วสมาชิกแต่ละคนที่ไม่ได้เริ่มต้นอย่างชัดเจนจะเริ่มต้นได้เริ่มต้น ตัวอย่าง:

struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };

เริ่มต้นss.aด้วย1, ss.bมี "asdf"และss.cมีมูลค่าของการแสดงออกของรูปแบบนั้นint(), 0ที่อยู่,

คุณสามารถหาข้อมูลจำเพาะทั้งหมดของหัวข้อนี้ได้ที่นี่


15
การตอบสนองที่ยอดเยี่ยม เพียงต้องการเพิ่มว่าการเริ่มต้นการรวมด้วย {0} นั้นเหมือนกับการเริ่มต้นด้วยเพียงแค่ {} บางทีอดีตทำให้ชัดเจนมากขึ้นว่าประเภทในตัวจะมีค่าเป็นศูนย์
James Hopkin

7
คอมไพเลอร์บางตัวทำให้หายใจไม่ออกในวันที่ {} นั่นคือสาเหตุที่ {0} ถูกใช้งาน
Branan

10
ไม่มาก .. ใน C ++ หากสมาชิกรายแรกไม่สามารถสร้างศูนย์แล้ว {0} จะไม่ทำงาน ตัวอย่าง: struct A {B b; int i; ถ่านค; }; struct B {B (); B (สตริง); }; A = {}; // คำแถลงนี้ไม่สามารถเขียนใหม่เป็น 'A = {0}'
แอรอน

25
@Branan นั่นเป็นเพราะใน C "{}" ไม่ถูกต้อง ใน C ++ มันคือ @ don.neufeld สิ่งนี้มีการเปลี่ยนแปลงด้วย C ++ 03 (ค่าเริ่มต้นเริ่มต้น -> ค่าเริ่มต้น) อ้างถึงอ้างอิงมาตรฐาน C ++ 98 จำไว้
Johannes Schaub - litb

3
ดังนั้นสิ่งนี้จะแทนที่ความจำเป็นในการใช้ ZeroMemory () (ใน VC ++) หรือไม่
Ray

89

สิ่งหนึ่งที่ต้องระวังคือเทคนิคนี้จะไม่ตั้งค่าการเติมเต็มเป็นศูนย์ ตัวอย่างเช่น:

struct foo
{
    char c;
    int  i;
};

foo a = {0};

ไม่เหมือนกับ:

foo a;
memset(&a,0,sizeof(a));

ในกรณีแรกแพดไบต์ระหว่าง c และ i จะไม่ถูกกำหนดค่าเริ่มต้น ทำไมคุณถึงสนใจ ถ้าคุณบันทึกข้อมูลนี้ลงดิสก์หรือส่งข้อมูลผ่านเครือข่ายหรืออะไรก็ตามคุณอาจมีปัญหาด้านความปลอดภัย


13
แน่นอนว่ามันเป็นปัญหาด้านความปลอดภัยหากคุณ 'เขียน (f, & a, sizeof (a))' ซึ่งสามารถสร้างการเข้ารหัสไฟล์ที่แตกต่างกันในโปรเซสเซอร์ / คอมไพเลอร์ที่แตกต่างกัน รูปแบบเอาต์พุตที่ดีจะปลอดภัยหากไม่มี memset
แอรอน

3
นอกจากนี้หากคุณกำลังส่งข้อมูลผ่านเครือข่ายคุณจะตั้งค่าการจัดตำแหน่งให้แน่นอยู่เสมอ ด้วยวิธีนี้คุณจะได้รับหน่วยความจำเพิ่มเป็นจำนวนน้อยที่สุดเท่าที่จะเป็นไปได้
Mark Kegel

18
มันควรจะสังเกตว่าถึงแม้ว่า spec ไม่ต้องการให้ช่องว่างภายในถูกเตรียมใช้งานคอมไพเลอร์ที่มีเหตุผลจะใช้เวลาในการเริ่มต้น "รอบ" เท่านั้น
โทมัส

4
ฉันต้องการที่จะมีปัญหากับการใช้คำว่า "ไม่" ไบต์การแพ็ดดิ้งถูกกำหนดไว้ คอมไพเลอร์มีอิสระที่จะเปลี่ยน foo a = {0} ให้เป็น memset (& a, 0, sizeof (a)) ในยามว่าง มันไม่จำเป็นต้อง "ข้าม" ไบต์ padding และเพียง foo.c ชุดและ foo.i. +1 สำหรับข้อผิดพลาดด้านความปลอดภัย (ที่อาจเกิดขึ้น)
SecurityMatt

3
@ Leushenko จุดที่ 19 บอกว่า "subobjects ทั้งหมดที่ไม่ได้กำหนดค่าเริ่มต้นอย่างชัดเจน" ดังนั้นฉันเลยเอนตัวไปยังจุดที่ 21 (ซึ่งคุณยกมา) กำลังเลอะเทอะ มันจะเป็นเรื่องที่แปลกประหลาดอย่างแท้จริงถ้า spec อนุญาตให้การเว้นระยะห่างจะไม่เริ่มต้นจนกว่าจะถึงช่วงเวลาของ initializer สุดท้ายหลังจากนั้นการเว้นวรรคจะต้องมีค่าเป็นศูนย์โดยเฉพาะอย่างยิ่งการพิจารณาว่า initializers
MM

20

โปรดทราบว่า initializer รวมที่ว่างเปล่ายังใช้งานได้:

SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};

11

เพื่อตอบว่าทำไมการShellExecuteEx()ขัดข้อง: โครงสร้างSHELLEXECUTEINFO"sexi" ของคุณมีสมาชิกจำนวนมากและคุณกำลังเริ่มต้นบางส่วนเท่านั้น

ตัวอย่างเช่นสมาชิกsexi.lpDirectoryอาจชี้ไปที่ใดก็ได้ แต่ShellExecuteEx()จะพยายามใช้มันดังนั้นคุณจะได้รับการละเมิดการเข้าถึงหน่วยความจำ

เมื่อคุณรวมบรรทัด:

SHELLEXECUTEINFO sexi = {0};

ก่อนการตั้งค่าโครงสร้างส่วนที่เหลือของคุณคุณกำลังบอกให้คอมไพเลอร์เป็นศูนย์สมาชิกโครงสร้างทั้งหมดก่อนที่คุณจะเริ่มต้นสมาชิกที่คุณสนใจShellExecuteEx()รู้ว่าถ้าsexi.lpDirectoryเป็นศูนย์ก็ควรเพิกเฉย


7

ฉันยังใช้มันเพื่อเริ่มต้นสตริงเช่น

char mytext[100] = {0};

5
แม้ว่าแน่นอนถ้า mytext กำลังใช้สตริง char mytext [100]; mytext [0] = '\ 0'; จะมีผลเหมือนกันของการให้สตริงที่ว่างเปล่า แต่เพียงทำให้การใช้งานเป็นศูนย์ไบต์แรก
Chris Young

@Chris: ฉันมักจะคิดว่ามีไวยากรณ์สำหรับการเริ่มต้นวัตถุบางส่วน ความสามารถในการ "ประกาศและกำหนดค่าเริ่มต้น" x จากนั้นทำเช่นเดียวกันกับ y แล้ว z เป็นจำนวนมากที่ดีกว่าการประกาศ x, y และ z แล้วเริ่มต้น x, y และ z แต่เริ่มต้น 100 ไบต์เมื่อเท่านั้น หนึ่งความต้องการเริ่มต้นดูเหมือนจะค่อนข้างสิ้นเปลือง
supercat

7

{0}เป็นเครื่องมือเริ่มต้นที่ถูกต้องสำหรับประเภทใด ๆ (วัตถุที่สมบูรณ์) ทั้งใน C และ C ++ มันเป็นสำนวนสามัญที่ใช้ในการเริ่มต้นวัตถุเป็นศูนย์ (อ่านเพื่อดูว่ามันหมายถึงอะไร)

สำหรับประเภทสเกลาร์ (ประเภทเลขคณิตและตัวชี้) การจัดฟันนั้นไม่จำเป็น แต่ก็อนุญาตอย่างชัดเจน การอ้างอิงร่าง N1570ของมาตรฐาน ISO C ส่วนที่ 6.7.9:

ตัวกำหนดค่าเริ่มต้นสำหรับสเกลาร์ต้องเป็นนิพจน์เดียวซึ่งอยู่ในวงเล็บปีกกา

มันเริ่มต้นวัตถุเป็นศูนย์ ( 0สำหรับจำนวนเต็ม0.0สำหรับจุดลอยตัวชี้ว่างสำหรับพอยน์เตอร์)

สำหรับประเภทที่ไม่ใช่สเกลาร์ (โครงสร้างอาร์เรย์สหภาพ) {0}ระบุว่าองค์ประกอบแรกของวัตถุจะเริ่มต้นเป็นศูนย์ สำหรับโครงสร้างที่มีโครงสร้างอาร์เรย์ของโครงสร้างและอื่น ๆ สิ่งนี้จะถูกนำไปใช้ซ้ำดังนั้นองค์ประกอบสเกลาร์แรกจะถูกตั้งค่าเป็นศูนย์ตามความเหมาะสมสำหรับประเภท ใน initializer ใด ๆ องค์ประกอบใด ๆ ที่ไม่ได้ระบุจะถูกตั้งค่าเป็นศูนย์

วงเล็บกลาง ( {, }) อาจถูกละเว้น; ตัวอย่างเช่นทั้งสองถูกต้องและเทียบเท่า:

int arr[2][2] = { { 1, 2 }, {3, 4} };

int arr[2][2] = { 1, 2, 3, 4 };

ซึ่งเป็นสาเหตุที่คุณไม่ต้องเขียนตัวอย่างเช่น{ { 0 } }สำหรับชนิดที่องค์ประกอบแรกไม่ใช่แบบสเกลาร์

ดังนั้นนี่คือ:

some_type obj = { 0 };

เป็นวิธีย่อของการเริ่มต้นobjเป็นศูนย์ซึ่งหมายความว่าแต่ละวัตถุย่อยสเกลาร์ของobjถูกกำหนดเป็น0หากเป็นจำนวนเต็ม0.0ถ้าเป็นทศนิยมหรือชี้ว่างถ้ามันเป็นตัวชี้

กฎมีความคล้ายคลึงกับ C ++

ในกรณีเฉพาะของคุณเนื่องจากคุณกำลังกำหนดค่าให้กับsexi.cbSizeและอื่น ๆ เป็นที่ชัดเจนว่าSHELLEXECUTEINFOเป็นประเภท struct หรือคลาส (หรืออาจจะเป็นสหภาพ แต่อาจไม่) ดังนั้นจึงไม่ทั้งหมดนี้ใช้ แต่ตามที่ฉันบอกว่า{ 0 }เป็นเรื่องธรรมดา สำนวนที่สามารถใช้ในสถานการณ์ทั่วไปมากขึ้น

นี่ไม่ใช่ (จำเป็น) เทียบเท่ากับการใช้memsetเพื่อตั้งค่าการแสดงของวัตถุเป็นบิตทั้งหมดเป็นศูนย์ ทั้ง floating-point 0.0หรือตัวชี้ null นั้นไม่จำเป็นต้องถูกแทนด้วย all-bits-zero และ{ 0 }initializer ไม่จำเป็นต้องตั้งค่า padding bytes เป็นค่าใด ๆ สำหรับระบบส่วนใหญ่นั้นมีแนวโน้มที่จะมีผลเหมือนกัน


1
ใน C ++, {0}ไม่ได้เป็นค่าเริ่มต้นที่ถูกต้องสำหรับวัตถุที่ไม่มีคอนสตรัคยอมรับ0; หรือสำหรับการรวมที่มีองค์ประกอบแรกคือเป็นเช่นนี้ (หรือรวมกับองค์ประกอบไม่ได้)
เอ็มเอ็ม

3

มันไม่นานมานี้ตั้งแต่ฉันทำงานใน c / c ++ แต่ IIRC ทางลัดเดียวกันสามารถใช้กับอาร์เรย์ได้เช่นกัน


2

ฉันสงสัยอยู่เสมอว่าทำไมคุณควรใช้สิ่งที่ชอบ

struct foo bar = { 0 };

นี่คือกรณีทดสอบที่จะอธิบาย:

check.c

struct f {
    int x;
    char a;
} my_zero_struct;

int main(void)
{
    return my_zero_struct.x;
}

ฉันรวบรวมgcc -O2 -o check check.cแล้วส่งออกตารางสัญลักษณ์ด้วยreadelf -s check | sort -k 2(นี่คือกับ gcc 4.6.3 บน ubuntu 12.04.2 บนระบบ x64) ข้อความที่ตัดตอนมา:

59: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
48: 0000000000601018     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
25: 0000000000601018     0 SECTION LOCAL  DEFAULT   25 
33: 0000000000601018     1 OBJECT  LOCAL  DEFAULT   25 completed.6531
34: 0000000000601020     8 OBJECT  LOCAL  DEFAULT   25 dtor_idx.6533
62: 0000000000601028     8 OBJECT  GLOBAL DEFAULT   25 my_zero_struct
57: 0000000000601030     0 NOTYPE  GLOBAL DEFAULT  ABS _end

ส่วนที่สำคัญที่นี่my_zero_structคือหลังจาก__bss_startนั้น ส่วน ".bss" ในโปรแกรม C คือส่วนหนึ่งของหน่วยความจำที่มีการตั้งค่าเป็นศูนย์ก่อนที่จะ mainถูกเรียกดูวิกิพีเดีย .bss

หากคุณเปลี่ยนรหัสด้านบนเป็น:

} my_zero_struct = { 0 };

จากนั้นการเรียกใช้งาน "check" ที่ได้จะมีลักษณะเหมือนกันอย่างน้อยกับ gcc 4.6.3 คอมไพเลอร์บน Ubuntu 12.04.2; my_zero_structยังอยู่ใน.bssส่วนจึงจะสามารถเริ่มต้นโดยอัตโนมัติไปยังศูนย์ก่อนที่จะmainถูกเรียกว่า

คำแนะนำในการแสดงความคิดเห็นว่าmemsetอาจเริ่มต้นโครงสร้าง "เต็ม" ยังไม่ได้รับการปรับปรุงเพราะ.bssส่วนจะถูกล้างอย่างเต็มที่ซึ่งยังหมายถึงโครงสร้าง "เต็ม" ตั้งค่าเป็นศูนย์

มันอาจเป็นไปได้ว่ามาตรฐานภาษา C ไม่ได้พูดถึงเรื่องนี้ แต่ในคอมไพเลอร์ C โลกจริงฉันไม่เคยเห็นพฤติกรรมที่แตกต่างกัน


ตัวแปรโกลบอลและสแตติกถูกกำหนดค่าเริ่มต้นเป็น 0 หรือ ctor ดีฟอลต์เสมอ แต่ถ้าคุณประกาศตัวอย่างของ f ในพื้นที่คุณอาจได้ผลลัพธ์ที่แตกต่างกัน
Logman

0

มันเป็นน้ำตาลแบบซิงโครไนซ์เพื่อเริ่มต้นโครงสร้างทั้งหมดของคุณเพื่อล้างค่า / ศูนย์ / null

รุ่นยาว

SHELLEXECUTEINFO sexi;
sexi.cbSize = 0;
sexi.fMask = 0;
sexi.hwnd = NULL;
sexi.lpVerb = NULL;
sexi.lpFile = NULL;
sexi.lpParameters = NULL;
sexi.lpDirectory = NULL;
sexi.nShow = nShow;
sexi.hInstApp = 0;
sexi.lpIDList = NULL;
sexi.lpClass = NULL;
sexi.hkeyClass = 0;
sexi.dwHotKey = 0;
sexi.hMonitor = 0;
sexi.hProcess = 0;

เวอร์ชั่นสั้น

SHELLEXECUTEINFO sexi = {0};

ไม่ง่ายกว่านี้อีกใช่ไหม

มันก็ดีเพราะ:

  • คุณไม่ต้องตามล่าสมาชิกทุกคนและเริ่มต้นมัน
  • คุณไม่ต้องกังวลว่าคุณอาจไม่ได้เริ่มสมาชิกใหม่เมื่อพวกเขาเพิ่มในภายหลัง
  • คุณไม่ต้องโทรZeroMemory

-5

{0} เป็นอาร์เรย์แบบไม่ระบุชื่อที่มีองค์ประกอบเป็น 0

ใช้เพื่อเริ่มต้นองค์ประกอบหนึ่งหรือทั้งหมดของอาร์เรย์ด้วย 0

เช่น int arr [8] = {0};

ในกรณีนี้องค์ประกอบทั้งหมดของ arr จะเริ่มต้นเป็น 0


4
{0}ไม่ใช่อาร์เรย์ที่ไม่ระบุชื่อ มันไม่ใช่การแสดงออก มันเป็นเครื่องมือเริ่มต้น
Keith Thompson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.