การล้างอาร์เรย์ถ่าน c


104

ฉันคิดว่าการตั้งค่าองค์ประกอบแรกเป็น null จะเป็นการล้างเนื้อหาทั้งหมดของอาร์เรย์ถ่าน

char my_custom_data[40] = "Hello!";
my_custom_data[0] = '\0';

อย่างไรก็ตามสิ่งนี้จะตั้งค่าองค์ประกอบแรกเป็นโมฆะเท่านั้น

หรือ

my_custom_data[0] = 0; 

แทนที่จะใช้memsetฉันคิดว่า 2 ตัวอย่างข้างต้นควรล้างข้อมูลทั้งหมด


1
จาเร็ดทำไมคุณถึงตั้งแท็ก c ++ เขาพูดถึง "C" และไม่ได้เพิ่มแท็กที่เกี่ยวข้องกับ C ++
Johannes Schaub - litb

1
สิ่งนี้ใช้ได้ดีกับอาร์เรย์อักขระใน C ++ แม้ว่าเขาจะไม่ได้ระบุก็ตาม
Adam Hawes

4
ฉันได้ลบแท็ก C ++ เพื่อหลีกเลี่ยงสิ่งที่เราเคยเห็นกับผู้คนที่นำเสนอโซลูชันเฉพาะ C ++
Alnitak

คำตอบ:


113

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

ในทางกลับกันหากคุณเลือกที่จะมองว่าสิ่งนี้เป็นสตริงที่สิ้นสุดด้วยค่าว่าง C / C ++ การตั้งค่าไบต์แรกเป็น 0 จะล้างสตริงได้อย่างมีประสิทธิภาพ


4
คำสำคัญในคำตอบคือ 'มีประสิทธิภาพ' เฉพาะองค์ประกอบแรกเท่านั้นที่เริ่มตั้งค่าเป็น 0 และส่วนที่เหลือยังคงมีค่าที่ไม่ได้กำหนดไว้ แต่ถ้าคุณถือว่าอาร์เรย์เป็นสตริงที่สิ้นสุดด้วยค่าว่างและองค์ประกอบแรกเป็นโมฆะสตริงนั้นจะถือว่าว่างเปล่า
Arnold Spence

แน่นอนนี่คือคำตอบของผู้คน
Johannes Schaub - litb

@caparcode เป๊ะ. นั่นเป็นเหตุผลว่าทำไมจึงสำคัญมากที่จะต้องทำความเข้าใจว่าอาร์เรย์ถูกใช้อย่างไร
JaredPar

ใช่นั่นคือสิ่งที่ฉันควรจะพูดในโพสต์แรกของฉัน char เป็นสตริงที่ถูกยกเลิก ดังนั้นสิ่งเหล่านี้จะทำเคล็ดลับนั้น ถ่าน [0] = '\ 0'; หรือถ่าน [0] = 0 ฉันไม่แน่ใจ แต่ได้ยินมาว่าการใช้ "\ 0" จะดีกว่าสำหรับการใช้สตริงที่สิ้นสุดด้วยค่าว่าง
ant2009

@robUK ใช่คุณถูกต้อง ในทางเทคนิค '\ 0' เท่ากับ 0 (ใน ascii) แต่คุณควรใช้ '\ 0' เพราะทำให้เจตนาของคุณชัดเจน
Mark Testa

70

อาร์เรย์ใน C เป็นเพียงตำแหน่งหน่วยความจำดังนั้นการmy_custom_data[0] = '\0';กำหนดของคุณเพียงแค่ตั้งค่าองค์ประกอบแรกเป็นศูนย์และปล่อยให้องค์ประกอบอื่น ๆ เหมือนเดิม

หากคุณต้องการล้างองค์ประกอบทั้งหมดของอาร์เรย์คุณจะต้องเข้าไปที่แต่ละองค์ประกอบ นั่นคือสิ่งที่memsetมีไว้สำหรับ:

memset(&arr[0], 0, sizeof(arr));

โดยทั่วไปแล้วเป็นวิธีที่เร็วที่สุดในการดูแลเรื่องนี้ หากคุณสามารถใช้ C ++ ได้ให้พิจารณา std :: fill แทน:

char *begin = &arr;
char *end = begin + sizeof(arr);
std::fill(begin, end, 0);

1
ฉันเชื่อว่าเวอร์ชันที่สองควรเป็น: std :: fill (arr, arr + sizeof (arr) / sizeof (arr [0]), 0);
David Rodríguez - dribeas

คำชี้แจง: อย่าใช้ sizeof กับการเติมเพราะคุณจะมีปัญหาในภายหลังด้วยอาร์เรย์ของ int, long, double หรือสิ่งที่คุณมี
Zan Lynx

ฉันชอบ: std :: เติม (& arr [0], & arr [arr_len], 0);
Zan Lynx

Zan Lynx นั่นคือพฤติกรรมที่ไม่ได้กำหนด คุณไม่สามารถทำ & arr [arr_len] แต่คุณต้องทำ std :: fill (arr, arr + sizeof arr, 0); หรือถ้าคุณมีความยาวที่ไหนสักแห่ง std :: fill (arr, arr + arr_len, 0); สมมติว่าอาร์เรย์ของถ่าน
Johannes Schaub - litb

ใช้ได้เฉพาะใน C แม้ว่าคำถามจะกำหนดเป้าหมาย C อย่างชัดเจน (ผู้ชายอีกคนเพิ่มแท็ก C ++ แต่ฉันไม่รู้ว่าทำไม) std :: fill แสดงความสัมพันธ์ C ++ :)
Johannes Schaub - litb

25

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

เมื่อเริ่มต้นคุณสามารถตั้งค่าอาร์เรย์เป็นศูนย์:

char mcd[40] = {0}; /* sets the whole array */

ไม่งั้นฉันไม่รู้เทคนิคอื่นนอกจาก memset หรืออะไรที่คล้ายกัน


ฉันเดาว่านี่ขึ้นอยู่กับคอมไพเลอร์ที่คุณใช้
cocoafan

1
@cocoafan: ไม่มันไม่ได้ขึ้นอยู่กับคอมไพเลอร์ มันเป็นส่วนหนึ่งของข้อกำหนดภาษา คอมไพเลอร์ใด ๆ ที่ทำงานแตกต่างกันไม่เป็นไปตามภาษาซี
abelenky

ฉันไม่รู้ว่าขอบคุณ ฉันไม่พบแหล่งข้อมูลที่สามารถอ่านกรณีพิเศษนี้ได้ คงจะดีไม่น้อยหากมีไว้เป็นที่คั่นหนังสือ
cocoafan

1
เรียกว่าการเริ่มต้นบางส่วน ฉันไม่มีข้อมูลจำเพาะ C99 แต่มีแหล่งที่มา 2 แหล่ง: bit.ly/enBC2m "คุณไม่จำเป็นต้องเตรียมใช้งานองค์ประกอบทั้งหมดในอาร์เรย์หากอาร์เรย์เริ่มต้นบางส่วนองค์ประกอบที่ไม่ได้เริ่มต้นจะได้รับค่า 0 ของ ประเภทที่เหมาะสม " bit.ly/f9asHH "หากมี initializers น้อยกว่าองค์ประกอบในอาร์เรย์องค์ประกอบที่เหลือจะเริ่มต้นโดยอัตโนมัติเป็น 0"
abelenky

สิ่งนี้ใช้ไม่ได้กับอาร์เรย์ที่มีการประกาศและกำหนดค่าไว้แล้วใช่หรือไม่
skinnedKnuckles

10

ใช้:

memset(my_custom_data, 0, sizeof(my_custom_data));

หรือ:

memset(my_custom_data, 0, strlen(my_custom_data));

1
ตัวเลือกที่สอง ( memset(my_custom_data, 0, strlen(my_custom_data));) จะล้างเฉพาะ '\ 0' ตัวแรกซึ่งอาจอยู่เลยส่วนท้ายของอาร์เรย์ สิ่งนี้อาจจะหรือไม่ก็ได้
brewmanz

9

ลองใช้รหัสต่อไปนี้:

void clean(char *var) {
    int i = 0;
    while(var[i] != '\0') {
        var[i] = '\0';
        i++;
    }
}

2
FYI - เยื้องรหัสด้วยช่องว่าง 4 ช่องหรือเลือกแล้วกดปุ่ม 'รหัส' ซึ่งดูเหมือนไบนารีสองบรรทัด
meagar

ทางออกที่ยอดเยี่ยมหากคุณไม่ต้องการรวม string.h สำหรับ memset ()
Akash Agarwal

7

ทำไมไม่ใช้ memset() ? นั่นคือวิธีการทำ

การตั้งค่าองค์ประกอบแรกจะทำให้หน่วยความจำที่เหลือไม่ถูกแตะต้อง แต่ฟังก์ชัน str จะถือว่าข้อมูลว่างเปล่า


1
อย่าใช้ memset เกินความสามารถในการอ่าน: stackoverflow.com/questions/453432/…
Johann Gerell

1
แล้วคำตอบของคุณคืออะไร?
mkb

6

โปรดดูด้านล่างที่ฉันได้อธิบายด้วยข้อมูลในอาร์เรย์หลังจากกรณีที่ 1 และกรณีที่ 2

char sc_ArrData[ 100 ];
strcpy(sc_ArrData,"Hai" );

กรณีที่ 1:

sc_ArrData[0] = '\0';

ผลลัพธ์:

-   "sc_ArrData"
[0] 0 ''
[1] 97 'a'
[2] 105 'i'
[3] 0 ''

กรณีที่ 2:

memset(&sc_ArrData[0], 0, sizeof(sc_ArrData));

ผลลัพธ์:

-   "sc_ArrData"
[0] 0 ''
[1] 0 ''
[2] 0 ''
[3] 0 ''

แม้ว่าการตั้งค่าอาร์กิวเมนต์แรกเป็น NULL จะช่วยแก้ปัญหาได้ แต่แนะนำให้ใช้ memset


4

ไม่ สิ่งที่คุณทำคือตั้งค่าแรกเป็น "\ 0" หรือ 0

หากคุณกำลังทำงานกับสตริงที่สิ้นสุดด้วยค่าว่างในตัวอย่างแรกคุณจะได้รับพฤติกรรมที่เลียนแบบสิ่งที่คุณคาดหวังอย่างไรก็ตามหน่วยความจำยังคงตั้งค่าอยู่

หากคุณต้องการล้างหน่วยความจำโดยไม่ใช้ memset ให้ใช้ for loop


ฉันบอกว่าไม่ต้องห่วง พยายามอย่าเขียนฟังก์ชันไลบรารีที่ "ปรับปรุงแล้ว" (และโดยปกติจะไม่ใช่) ของคุณเอง ในความเป็นจริง memset และ memcpy ค่อนข้างพิเศษมักจะถูกแทรกเข้าไปในรหัสเครื่องที่กำหนดเองสำหรับ CPU โดยพิจารณาจากสิ่งที่ทราบเกี่ยวกับการจัดตำแหน่งข้อมูลและความยาว
Zan Lynx

@Zan the OP ไม่ต้องการใช้ memset (บางทีเขาฝังไว้และไม่มีให้) แต่ใช่ Memset มักจะเหมาะสมที่สุดและน่าจะเร็วกว่าแบบวนซ้ำ
Adam Hawes

อย่างไรก็ตามเขาไม่ต้องการใช้ memset ดังนั้นฉันจึงแนะนำ for loop
Alan

3

คุณควรใช้ memset การตั้งค่าเพียงองค์ประกอบแรกจะไม่ทำงานคุณต้องตั้งค่าองค์ประกอบทั้งหมดหากไม่เป็นเช่นนั้นคุณจะตั้งค่าเฉพาะองค์ประกอบแรกเป็น 0 ได้อย่างไร


ไม่ควรใช้ memset มากเกินความสามารถในการอ่าน: stackoverflow.com/questions/453432/…
Johann Gerell

2

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



1

ฉันคิดว่าการตั้งค่าองค์ประกอบแรกเป็น null จะเป็นการล้างเนื้อหาทั้งหมดของอาร์เรย์ถ่าน

ไม่ถูกต้องตามที่คุณค้นพบ

อย่างไรก็ตามสิ่งนี้จะตั้งค่าองค์ประกอบแรกเป็นโมฆะเท่านั้น

เป๊ะ!

คุณต้องใช้ memset เพื่อล้างข้อมูลทั้งหมดการตั้งค่ารายการใดรายการหนึ่งเป็น null ไม่เพียงพอ

อย่างไรก็ตามหากการตั้งค่าองค์ประกอบของอาร์เรย์เป็น null หมายถึงสิ่งที่พิเศษ (ตัวอย่างเช่นเมื่อใช้สตริงการยุติ null) อาจเพียงพอที่จะตั้งค่าองค์ประกอบแรกเป็น null ด้วยวิธีนี้ผู้ใช้อาร์เรย์จะเข้าใจว่ามันว่างเปล่าแม้ว่าอาร์เรย์จะยังคงรวมอักขระเก่าไว้ในหน่วยความจำ


อย่าใช้ "memset" มากเกินความสามารถในการอ่าน: stackoverflow.com/questions/453432/…
Johann Gerell

1

ตั้งค่าองค์ประกอบแรกเป็น NULL การพิมพ์อาร์เรย์ถ่านจะไม่ให้อะไรกลับคืนมา



-3
void clearArray (char *input[]){
    *input = ' '; 
}

1
นี่ไม่ใช่การล้างมันเป็นเพียงการตั้งค่าอักขระตัวแรกเป็น ''! ฉันคิดว่าคุณต้องการเขียน * input = '\ 0'
stviper

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