ฉันสามารถเรียก memcpy () และ memmove () โดยตั้งค่า "จำนวนไบต์" เป็นศูนย์ได้หรือไม่


104

ฉันจำเป็นต้องปฏิบัติต่อกรณีหรือไม่เมื่อฉันไม่มีสิ่งใดให้เคลื่อนย้าย / คัดลอกโดยมีmemmove()/ memcpy()เป็นกรณีขอบ

int numberOfBytes = ...
if( numberOfBytes != 0 ) {
    memmove( dest, source, numberOfBytes );
}

หรือฉันควรเรียกใช้ฟังก์ชันโดยไม่ตรวจสอบ

int numberOfBytes = ...
memmove( dest, source, numberOfBytes );

การตรวจสอบข้อมูลโค้ดในอดีตจำเป็นหรือไม่


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

12
@Toad: จุดประสงค์นี้ทำหน้าที่อะไรนอกเหนือจากการถ่วงรหัส? เมื่ออ่านโค้ดของใครบางคนฉันไม่จำเป็นต้องรู้ว่าโปรแกรมเมอร์คนเดิม "คิดจะดำเนินการนี้ซึ่งไม่จำเป็นจริงๆ แต่เพราะมันไม่จำเป็นฉันจึงไม่ได้ทำ" ถ้าฉันเห็นตัวชี้ถูกปล่อยให้เป็นอิสระฉันรู้ว่ามันได้รับอนุญาตให้เป็นโมฆะดังนั้นฉันไม่จำเป็นต้องทราบความคิดของโปรแกรมเมอร์ต้นฉบับในหัวข้อ "ฉันควรตรวจสอบค่า null" และเดียวกันจะไปสำหรับการคัดลอก 0 ไบต์มีmemcpy
jalf

9
@jalf: ความจริงที่ว่ามันเป็นคำถามใน stackoverflow ทำให้เป็นสิ่งที่คนสงสัย ดังนั้นการเพิ่มความคิดเห็นอาจไม่ช่วยคุณ แต่อาจช่วยคนที่มีความรู้น้อยกว่า
คางคก

2
@ Toad ใช่ความคิดเห็นที่เรียกอย่างชัดเจนว่าทำไมการตรวจสอบที่ดูจำเป็นจริงๆจึงไม่สามารถมีค่าได้ในหลักการ อื่น ๆด้านของเหรียญเป็นว่าตัวอย่างนี้โดยเฉพาะอย่างยิ่งเป็นกรณีทั่วไปที่เกี่ยวข้องกับฟังก์ชั่นมาตรฐานห้องสมุดว่าแต่ละโปรแกรมเมอร์เพียงความต้องการที่จะเรียนรู้คำตอบครั้ง; จากนั้นพวกเขาสามารถรับรู้ในโปรแกรมใด ๆ ที่พวกเขาอ่านว่าไม่จำเป็นต้องมีการตรวจสอบเหล่านี้ สำหรับว่าเหตุผลที่ผมละเว้นการแสดงความคิดเห็น โค้ดเบสที่มีการเรียกหลายครั้งเช่นนี้อาจจำเป็นต้องคัดลอกและวางความคิดเห็นลงในแต่ละรายการหรือใช้โดยพลการกับการโทรบางสายซึ่งทั้งสองอย่างน่าเกลียด
Mark Amery

คำตอบ:


146

จากมาตรฐาน C99 (7.21.1 / 2):

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

ดังนั้นคำตอบคือไม่ ไม่จำเป็นต้องตรวจสอบ (หรือใช่คุณสามารถส่งผ่านศูนย์ได้)


1
ตัวชี้จะถือว่า "ถูกต้อง" สำหรับวัตถุประสงค์ของฟังก์ชันดังกล่าวหรือไม่หากชี้ไปยังตำแหน่งที่อยู่ถัดจากองค์ประกอบสุดท้ายของอาร์เรย์ ตัวชี้ดังกล่าวไม่สามารถอ้างอิงได้อย่างถูกต้อง แต่เราสามารถทำสิ่งอื่น ๆ ได้อย่างปลอดภัยเช่นลบตัวชี้ออกจากตัวชี้
supercat

1
@supercat: ใช่ตัวชี้ที่ชี้ไปที่ปลายด้านหนึ่งของอาร์เรย์นั้นใช้ได้กับตัวชี้ทางคณิตศาสตร์ที่มีพอยน์เตอร์อื่น ๆ อยู่ภายใน (หรือตัวหนึ่งเลยจุดสิ้นสุดของ) อาร์เรย์นั้น แต่ไม่สามารถหักล้างได้
Mike Seymour

@MikeSeymour: การอ้างอิงไม่ควรบ่งบอกถึงคำตอบที่ตรงกันข้าม: การตรวจสอบเป็นสิ่งที่จำเป็นและคุณไม่สามารถส่งผ่านศูนย์ด้วยพอยน์เตอร์ว่าง?
neverhoodboy

7
@neverhoodboy: ไม่คำพูดบอกไว้อย่างชัดเจนว่า " nสามารถมีค่าเป็นศูนย์" คุณถูกต้องที่ไม่สามารถส่งพอยน์เตอร์ว่างได้ แต่นั่นไม่ใช่สิ่งที่คำถามกำลังถาม
Mike Seymour

1
@MikeSeymour: ความผิดของฉัน ขอโทษจริงๆ. คำถามเกี่ยวกับขนาดไม่ใช่ตัวชี้
neverhoodboy

5

ตามที่กล่าวโดย @You มาตรฐานระบุว่า memcpy และ memmove ควรจัดการกรณีนี้โดยไม่มีปัญหา เนื่องจากมักจะถูกนำไปใช้อย่างใดอย่างหนึ่งเช่น

void *memcpy(void *_dst, const void *_src, size_t len)
{
    unsigned char *dst = _dst;
    const unsigned char *src = _src;
    while(len-- > 0)
        *dst++ = *src++;
    return _dst;
}

คุณไม่ควรมีโทษทางประสิทธิภาพใด ๆ นอกจากการเรียกใช้ฟังก์ชัน หากคอมไพเลอร์รองรับอินทรินซิค / อินไลน์สำหรับฟังก์ชันดังกล่าวการตรวจสอบเพิ่มเติมอาจทำให้โค้ดช้าลงเล็กน้อยเนื่องจากการตรวจสอบเสร็จสิ้นในขณะนั้น


1
ฉันคิดว่าฟังก์ชั่นนี้น่าจะถูกสร้างขึ้นในชุดประกอบซึ่งคุณสามารถปรับแต่งการโอนหน่วยความจำได้ดีกว่าใน c
Toad

"อย่างใดอย่างหนึ่ง" :) ที่จริงแล้วการใช้งานเกือบทั้งหมดที่ฉันเคยเห็นอยู่ในแอสเซมบลีและพยายามคัดลอกบิตส่วนใหญ่โดยใช้ขนาดคำดั้งเดิม (เช่น uint32_t บน x86) แต่นั่นไม่ได้เปลี่ยนเนื้อหาของคำตอบ : เป็นลูปในขณะที่ไม่จำเป็นต้องมีการคำนวณที่ดีก่อนที่จะเริ่มดังนั้นการตรวจสอบจึงเสร็จสิ้นแล้ว
Matteo Italia

9
-1 การใช้งานทั่วไปไม่เกี่ยวข้องกับว่า C ถูกต้องหรือไม่ในการเรียกใช้ฟังก์ชันเหล่านี้ (ซึ่งอาจไม่สามารถใช้เป็นฟังก์ชัน C ได้) โดยมีอาร์กิวเมนต์เป็นศูนย์
R .. GitHub STOP HELPING ICE

4
ความจริงที่ว่า C ถูกต้องได้ถูกครอบคลุมโดยคำตอบอื่น ๆ แล้วดังที่ฉันได้กล่าวไว้ในตอนต้นของคำตอบของฉัน: "ตามที่ @You กล่าวมาตรฐานระบุว่า memcpy และ memmove ควรจัดการกรณีนี้โดยไม่มีปัญหา" ฉันเพิ่งเพิ่มความคิดเห็นของฉันเกี่ยวกับความจริงที่ว่าคุณไม่ควรกลัวที่จะเรียก memcpy ด้วย len = 0 ด้วยเหตุผลด้านประสิทธิภาพเนื่องจากในกรณีนี้เป็นการโทรที่มีค่าใช้จ่ายเกือบเป็นศูนย์
Matteo Italia
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.