2011-12-21 46 views
14

Vì một lý do nào đó, tôi muốn quay tay phiên bản số không malloc(). Để giảm thiểu độ phức tạp về thuật toán, tôi muốn viết:Có thể gọi memset() với một con trỏ null nếu kích thước là 0 không?

void * my_calloc(size_t size) 
{ 
    return memset(malloc(size), 0, size); 
} 

Điều này có được xác định rõ khi size == 0? Nó là tốt để gọi malloc() với một kích thước bằng không, nhưng điều đó cho phép nó trả về một con trỏ null. Liệu lệnh gọi tiếp theo là memset có được chấp nhận hay là hành vi không xác định này và tôi cần phải thêm một điều kiện if (size)?

Tôi rất muốn tránh kiểm tra dự phòng có điều kiện!

Giả sử thời điểm malloc() không thành công. Trong thực tế, sẽ có phiên bản có tay của malloc() ở đó, quá, sẽ chấm dứt khi không thành công.

Something như thế này:

void * my_malloc(size_t size) 
{ 
    void * const p = malloc(size); 
    if (p || 0 == size) return p; 
    terminate(); 
} 

+0

'memset' AFAIK không bắt buộc phải kiểm tra NULL, vì vậy nếu 'malloc' thất bại, bạn sẽ không có' byte' byte bắt đầu từ địa chỉ 0. – Praetorian

+0

@Praetorian: Xin lỗi, tôi đã thêm sau: Giả sử rằng ' malloc() 'không bao giờ thất bại. Câu hỏi là chỉ khi 'size' có thể là' 0'. –

Trả lời

6

Sửa Re:

tôi thêm này sau: Giả sử malloc rằng() không bao giờ thất bại. Câu hỏi chỉ là nếu kích thước có thể là 0

Tôi hiểu. Vì vậy, bạn chỉ muốn mọi thứ để được an toàn nếu con trỏ là NULL kích thước là 0.

Đề cập đến các tài liệu POSIX

Không , nó không phải là quy định rằng nó nên được an toàn để gọi memset với một con trỏ NULL (nếu bạn gọi nó với số không hoặc kích thước ... mà muốn được thậm chí nhiều hơn 'liên esting ', nhưng cũng không được chỉ định).

Không có gì về nó thậm chí còn được đề cập trong phần 'thông tin'.

Lưu ý rằng liên kết đầu tiên đề cập đến

Các chức năng được mô tả trên trang thông tin này được liên kết với các tiêu chuẩn ISO C. Bất kỳ xung đột nào giữa các yêu cầu được mô tả ở đây và tiêu chuẩn ISO C là không chủ ý. Cuốn sách này của IEEE Std 1.003,1-2001 trì hoãn đến ISO C tiêu chuẩn

Cập nhật Tôi có thể khẳng định rằng các tiêu chuẩn ISO C99 (n1256.pdf) cũng không kém phần ngắn gọn như các tài liệu POSIX C++ 11 spec chỉ đề cập đến tiêu chuẩn ansi C cho memset và bạn bè.

+0

Tôi đã duyệt các tiêu chuẩn cho một chút trước khi đăng bài :-) Tôi phải bỏ lỡ nó ... Toàn bộ vấn đề của tôi là tình huống 'size == 0'. –

+0

Vâng, vì nó là một hàm thư viện, tôi muốn a) tránh các điều kiện không cần thiết, và b) được chuẩn bị để đối phó với bất kỳ đầu vào người dùng nào, thậm chí là '0'. Nếu tôi có thể đạt được cả hai hoàn toàn nhờ vào sự bảo đảm của thư viện C, điều đó sẽ thuận lợi hơn khi thêm các kiểm tra cồng kềnh của riêng tôi. –

+0

Tôi có thể xác nhận rằng tiêu chuẩn ISO C99 (n1256.pdf) là ngắn gọn như tài liệu POSIX ** và ** thông số C++ 11 chỉ đề cập đến tiêu chuẩn ansi C cho 'memset' và bạn bè. – sehe

9

Đây là tuyên bố glibc:

extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1)); 

Các __nonnull cho thấy rằng họ hy vọng con trỏ trở thành phi null.

+0

Những corrobates ý tưởng của tôi rằng nó trông giống như nó là ** không xác định **, và trong trường hợp của glibc, ** undefined ** là tốt (nguyên mẫu chỉ làm cho chắc chắn rằng trình biên dịch cảnh báo về kết quả là _undefined_ khi gọi với một con trỏ null) – sehe

4

Đây là những gì các tiêu chuẩn C99 cho biết về điều này:

7.1.4 "Sử dụng các chức năng thư viện

Nếu một đối số cho một hàm có giá trị không hợp lệ (chẳng hạn như một giá trị bên ngoài lĩnh vực hàm hoặc con trỏ nằm ngoài vùng địa chỉ của chương trình, hoặc con trỏ rỗng hoặc con trỏ đến bộ nhớ không thể sửa đổi khi tham số tương ứng không đủ điều kiện) hoặc loại (sau khi quảng bá) không được hàm mong đợi số lượng đối số thay đổi, hành vi không xác định.

7.21.1 "Chuỗi chức năng ước" (hãy nhớ rằng memset() là trong string.h)

đâu một cuộc tranh cãi khai báo là size_t n xác định độ dài của mảng cho một chức năng, n có thể có giá trị zero trên gọi hàm đó. Trừ khi được nêu rõ trong phần mô tả của một hàm cụ thể trong phần này, các đối số con trỏ trên một cuộc gọi như vậy sẽ vẫn có các giá trị hợp lệ, như được mô tả trong 7.1.4.

7.21.6.1 "Chức năng memset"

Các memset chức năng sao chép giá trị của c (chuyển đổi thành một char unsigned) vào mỗi trong những nhân vật n đầu tiên của đối tượng được trỏ đến bởi s.

Vì vậy, nói đúng, vì tiêu chuẩn chỉ định rằng s phải trỏ đến một đối tượng, đi qua một con trỏ rỗng sẽ là UB. Thêm séc (chi phí so với malloc() sẽ biến mất nhỏ). Mặt khác, nếu bạn biết malloc() không thể không thành công (vì bạn có tùy chỉnh chấm dứt), thì rõ ràng bạn không cần phải kiểm tra trước khi gọi memset().

+0

Nhưng trong cách giải thích của tôi, 'malloc (0)' cũng không bao giờ thất bại, vì vậy tôi không đảm bảo rằng tôi có một con trỏ hợp lệ. Bạn đang nói rằng nó * là * chắc chắn UB để gọi 'memset' với một con trỏ không hợp lệ, ngay cả khi kích thước là 0? –

+0

@Kerrick: đọc của tôi về tiêu chuẩn là UB của nó nếu bạn truyền vào một con trỏ không hợp lệ - bao gồm 'NULL' - tới' memset() ', ngay cả khi kích thước là' 0'. Tôi tưởng tượng rằng trong hầu hết các triển khai nó sẽ hoạt động như người ta muốn ('memset()' là một nop nếu 'size == 0'), nhưng đó chỉ là một sự trùng hợp hạnh phúc của UB 'working'. Ngoài ra, tôi nghĩ rằng làm cho một đối số hiệu suất cho việc không thực hiện kiểm tra là yếu - zeroing khối bộ nhớ sẽ thống trị hiệu suất cho trường hợp sử dụng của chức năng này nên có nhiều khả năng hơn bởi các đơn đặt hàng của cường độ: khi 'size> 0'. –

+0

@Kerrick: Tôi hơi bối rối về bình luận của bạn về việc con trỏ có hợp lệ không - nếu bạn có thể giả sử 'malloc (0)' không thể thất bại, thì bạn có thể gọi 'memset()' với số không kích thước trên con trỏ được trả về bởi 'malloc (0)'. –

Các vấn đề liên quan