Tôi có thể gọi memcpy () và memmove () với "số byte" được đặt bằng 0 không?


102

Tôi có cần phải xử lý các trường hợp khi tôi thực sự không có gì để di chuyển / sao chép với memmove()/ memcpy()như các trường hợp cạnh không

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

hay tôi chỉ nên gọi hàm mà không cần kiểm tra

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

Việc kiểm tra trong đoạn mã trước đây có cần thiết không?


6
câu hỏi nhắc nhở tôi một chút về việc kiểm tra con trỏ rỗng trên các hàm như miễn phí. Không cần thiết, nhưng tôi sẽ đặt một bình luận ở đó để cho bạn thấy suy nghĩ về nó.
Toad

12
@Toad: Mục đích đó phục vụ mục đích gì, ngoài việc làm lộn xộn mã? Khi đọc mã của ai đó, tôi không cần biết rằng lập trình viên ban đầu "nghĩ về việc thực hiện thao tác này thực sự không cần thiết, nhưng vì nó không cần thiết nên tôi đã không làm". Nếu tôi thấy một con trỏ đang được giải phóng, tôi biết nó được phép để trống, vì vậy tôi không cần biết suy nghĩ của lập trình viên ban đầu về chủ đề "tôi có nên kiểm tra null không". Và cùng đi để sao chép 0 byte vớimemcpy
jalf

8
@jalf: thực tế là nó là một câu hỏi trên stackoverflow, khiến mọi người nghi ngờ nó. Vì vậy, thêm một bình luận có thể không giúp bạn, nhưng có thể giúp đỡ một ai đó với kiến thức ít
Toad

2
@Toad Vâng, các bình luận nêu rõ lý do tại sao một tấm séc trông thực sự cần thiết lại không có giá trị về nguyên tắc. Mặt khác của vấn đề là ví dụ cụ thể này là một trường hợp phổ biến liên quan đến một hàm thư viện tiêu chuẩn mà mỗi lập trình viên chỉ cần tìm hiểu câu trả lời một lần; sau đó họ có thể nhận ra trong bất kỳ chương trình nào mà họ đọc rằng những kiểm tra này không cần thiết. Cho rằng lý do, tôi muốn bỏ qua các ý kiến. Một cơ sở mã với nhiều lệnh gọi như thế này sẽ cần phải sao chép và dán các nhận xét vào từng cuộc gọi hoặc tùy ý sử dụng chúng trên một số lệnh gọi, cả hai đều xấu xí.
Mark Amery

Câu trả lời:


145

Từ tiêu chuẩn C99 (7.21.1 / 2):

Trong đó một đối số được khai báo là size_t nchỉ định độ dài của mảng cho một hàm, ncó thể có giá trị bằng không khi gọi hàm đó. Trừ khi được nêu rõ ràng khác trong mô tả của một chức năng cụ thể trong điều khoản phụ này, các đối số con trỏ trên một lệnh gọi như vậy sẽ vẫn có giá trị hợp lệ, như được mô tả trong 7.1.4. Trong một cuộc gọi như vậy, một hàm định vị một ký tự không tìm thấy sự xuất hiện nào, một hàm so sánh hai chuỗi ký tự trả về giá trị không và một hàm sao chép ký tự sẽ sao chép các ký tự không.

Vì vậy, câu trả lời là không; việc kiểm tra là không cần thiết (hoặc có; bạn có thể vượt qua số không).


1
Một con trỏ có được coi là "hợp lệ" cho các mục đích của một hàm như vậy không nếu nó trỏ đến vị trí theo sau phần tử cuối cùng của một mảng? Một con trỏ như vậy không thể được tham chiếu một cách hợp pháp, nhưng người ta có thể làm một số việc khác của con trỏ một cách an toàn như trừ một con khỏi nó.
supercat

1
@supercat: vâng, một con trỏ trỏ đến một đầu cuối của mảng có giá trị cho phép số học con trỏ với các con trỏ khác trong (hoặc một con trỏ ở cuối) mảng đó, nhưng không thể bỏ qua.
Mike Seymour

@MikeSeymour: Không nên trích dẫn ngụ ý một câu trả lời ngược lại: việc kiểm tra là cần thiết và bạn không thể vượt qua số 0 với con trỏ rỗng?
neverhoodboy

7
@ preferhoodboy: Không, câu trích dẫn rõ ràng là " ncó thể có giá trị bằng 0". Bạn đúng là bạn không thể chuyển con trỏ null, nhưng đó không phải là điều mà câu hỏi đang đặt ra.
Mike Seymour

1
@MikeSeymour: Lỗi của tôi. Thật sự xin lỗi. Câu hỏi là về kích thước không phải là con trỏ.
neverhoodboy

5

Như đã nói bởi @You, tiêu chuẩn chỉ định rằng memcpy và memmove sẽ xử lý trường hợp này mà không có vấn đề gì; vì chúng thường được triển khai bằng cách nào đó như

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;
}

bạn thậm chí không nên có bất kỳ hình phạt hiệu suất nào ngoài lời gọi hàm; nếu trình biên dịch hỗ trợ nội tuyến / nội tuyến cho các chức năng như vậy, việc kiểm tra bổ sung thậm chí có thể làm cho mã chậm hơn một chút, vì việc kiểm tra đã được thực hiện tại thời điểm đó.


1
Tôi nghĩ rằng chức năng này có thể được thực hiện trong lắp ráp, nơi bạn có thể tối ưu hóa việc truyền bộ nhớ tốt hơn nhiều so với trong c
Toad

"bằng cách nào đó giống như" :) Thực ra hầu như tất cả các triển khai tôi đã thấy đều ở dạng lắp ráp và cố gắng sao chép hầu hết các bit bằng cách sử dụng kích thước từ gốc (ví dụ: uint32_t trên x86), nhưng điều đó không thay đổi nội dung của câu trả lời : đó là một trong khi vòng lặp mà không cần phải tính toán rất lớn trước khi bắt đầu, vì vậy việc kiểm tra đã được thực hiện.
Matteo Italia,

9
-1, việc triển khai điển hình không liên quan đến việc liệu C có hợp lệ để gọi các hàm này hay không (thậm chí có thể không được triển khai như các hàm C) với đối số bằng không.
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA,

4
Thực tế là nó hợp lệ C đã được đề cập trong các câu trả lời khác, như tôi đã nói ở đầu câu trả lời của mình: "Như @You đã nói, tiêu chuẩn chỉ định rằng memcpy và memmove sẽ xử lý trường hợp này mà không có vấn đề gì". Tôi chỉ nói thêm ý kiến ​​của tôi về thực tế là bạn thậm chí không nên sợ hãi khi gọi memcpy với len = 0 vì lý do hiệu suất, vì trong trường hợp đó, đó là một cuộc gọi với chi phí gần như bằng không.
Matteo Italia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.