2012-11-08 55 views
18

Cho đến gần đây, tôi chỉ thấy sao chép các trường cấu trúc được thực hiện với memcpy(). Trong lớp học và hướng dẫn trực tuyến, sao chép nội dung của một struct thành khác thường trông giống nhưSao chép cấu trúc trong C với gán thay vì memcpy()

struct block *b0 = malloc(sizeof(struct block)); 
struct block *b1 = malloc(sizeof(struct block)); 
/* populate fields in *b0 */ 
memcpy(b1, b0, sizeof *b1); /* copy contents of b0 into b1 */ 
/* free b0, b1 */ 

Tuy nhiên, nhiệm vụ này cũng có thể được thực hiện bằng một nhiệm vụ đơn giản thay thế memcpy().

*b1 = *b0; /* dereferenced struct assignment */ 

Có lý do chính đáng tại sao điều này không được sử dụng rộng rãi (ít nhất là trong kinh nghiệm hạn chế của tôi)? Có phải hai phương pháp này — chỉ định và memcpy() — tương đương hoặc có một số lý do thuyết phục để sử dụng memcpy() nói chung không?

+3

Bạn có nghĩa là * b1 = * b0 ?? – Jeyaram

+7

Trong C, hai phương thức tương đương nhau, và cả hai đều không có bản sao sâu. –

+2

'memcpy (b1, b0, sizeof (khối cấu trúc));' là kiểu rất xấu và dễ bị lỗi. Hoặc sử dụng gán hoặc 'memcpy (b1, b0, sizeof * b1);' –

Trả lời

27

Cả hai phương pháp đều tương đương và thực hiện bản sao nông. Điều này có nghĩa là bản thân cấu trúc được sao chép, nhưng bất cứ thứ gì mà tham chiếu cấu trúc không được sao chép.

Đối với lý do tại sao memcpy phổ biến hơn, tôi không chắc chắn. Các phiên bản cũ hơn của C không hỗ trợ phân công cấu trúc (although it was a common extension as early as 1978), vì vậy có lẽ phong cách memcpy bị kẹt như một cách tạo mã di động nhiều hơn? Trong mọi trường hợp, việc gán cấu trúc được hỗ trợ rộng rãi trong các trình biên dịch PC và việc sử dụng memcpy dễ bị lỗi hơn (nếu bạn nhận được kích thước sai, những điều xấu có thể xảy ra), và do đó tốt nhất là sử dụng phân công cấu trúc nếu có thể.

Tuy nhiên, các trường hợp chỉ có memcpy hoạt động. Ví dụ:

  • Nếu bạn đang sao chép một cấu trúc hoặc từ một bộ đệm unaligned - ví dụ, để tiết kiệm/tải đến/từ đĩa hoặc gửi/nhận trên mạng - bạn cần phải sử dụng memcpy, như phân cấu trúc yêu cầu cả nguồn và đích để căn chỉnh đúng.
  • Nếu bạn đang đóng gói thông tin bổ sung sau cấu trúc, có lẽ using a zero-element array, bạn cần sử dụng memcpy và thêm thông tin bổ sung này vào trường kích thước.
  • Nếu bạn đang sao chép một mảng cấu trúc, nó có thể sẽ hiệu quả hơn để thực hiện một lần memcpy thay vì lặp và sao chép cấu trúc riêng lẻ. Thế lần sau, nó có thể không được nữa. Thật khó để nói, việc triển khai memcpy khác nhau về đặc điểm hiệu suất của chúng.
  • Một số trình biên dịch được nhúng có thể không hỗ trợ gán cấu trúc. Có lẽ những thứ quan trọng khác mà trình biên dịch được đề cập không hỗ trợ tốt, tất nhiên.

Cũng lưu ý rằng mặc dù trong C memcpy và phân cấu trúc thường tương đương, trong C++ memcpy và phân cấu trúc là không tương đương. Nói chung C++ tốt nhất là tránh các cấu trúc ing memcpy, như việc gán cấu trúc có thể, và thường bị quá tải để thực hiện những việc bổ sung như bản sao sâu hoặc quản lý số tham chiếu.

+0

Tuyệt vời, cảm ơn. Xin lỗi về sự nhầm lẫn sao chép sâu/nông. Tôi đã ấn tượng rằng một bản sao nông giống như một phép gán con trỏ, như 'b1 = b0' thay vì' * b1 = * b0'. – olliezhu

6

Đây không phải là câu trả lời chính xác mà bạn đang tìm kiếm.

Tôi đang giải thích về tình huống mà tôi đã gặp.

khi chúng tôi sử dụng memcpy(), nó sẽ sao chép từng byte sang đích. vì vậy đừng lo lắng về việc căn chỉnh dữ liệu trong kiến ​​trúc ARM.Nếu bạn sử dụng toán tử = và bất kỳ địa chỉ nào không được căn chỉnh với 4 byte thì lỗi căn chỉnh sẽ đến.

Từ trang Arm:

Một con trỏ đến vị trí đích đó là một byte ngoài byte cuối cùng ghi vào. Điều này cho phép tiếp tục quá trình viết with perfect alignment of bytes để nối chuỗi các khối bộ nhớ.

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0175k/Cihbbjge.html

+2

Không nên trình biên dịch chăm sóc mọi thứ được sắp xếp đúng cách? – alk

+2

@alk, trình biên dịch giả định mọi thứ được căn chỉnh đúng cách, vì điều này cho phép cải thiện hiệu suất đáng kể trên nhiều nền tảng. Nếu bạn chỉ sử dụng malloc để cấp phát bộ nhớ cho cấu trúc của bạn, nó sẽ đảm bảo nó được căn chỉnh, nhưng nếu bạn bắt đầu làm số học con trỏ hoặc cấu trúc đọc từ tệp hoặc mạng thì điều này không còn là trường hợp và các giả định của trình biên dịch có thể bị vi phạm. hành vi. Lưu ý rằng bạn sẽ không thấy điều này trên x86 vì hầu hết các hoạt động x86 xử lý các truy cập không được ký hiệu một cách minh bạch, với một hiệu suất nhỏ. – bdonlan

+0

@bdonlan Công bằng đủ Tôi thấy các vấn đề liên quan đến việc đọc/ghi từ/đến các bộ đệm I/O-được sắp xếp kém. Nhưng tại sao nên có vấn đề bằng cách sử dụng (đúng cách gõ) arithmetics con trỏ tôi không nhận được, như trình biên dịch nên biết bất cứ điều gì cần thiết (tại thời gian biên dịch) để tạo ra mã chính xác. – alk

-2

dân làm việc trên nền tảng nhúng sẽ thích sử dụng memcopy thay vì phân công trực tiếp của cấu trúc. Chủ yếu là khi bạn đối phó với nền tảng nhúng, một số trình biên dịch không hỗ trợ gán cấu trúc trực tiếp, cho rằng bạn cần sử dụng memcopy. nếu bạn đang làm việc trên máy tính thì không có vấn đề trong cả hai trường hợp, Cả hai đều hợp lệ.

0

Một số người thích memcpy bởi vì đó là những gì họ đã học và họ không bao giờ tìm ra rằng họ chỉ có thể làm một bài tập (trong thời cổ đại nhiệm vụ không được phép, nhưng đó là một thời gian dài trước đây). Không có vấn đề liên kết nào phải lo lắng vì bộ nhớ được phân bổ bởi malloc() luôn được căn chỉnh chính xác. Và kể từ khi một trình biên dịch có thể trivially dịch chuyển nhượng này cho một cuộc gọi memcpy, nó sẽ không bao giờ chậm hơn hoặc nhiều hơn mã memcpy. Tất nhiên có những hệ thống nhúng với các trình biên dịch lỗi thời.

3

Tôi đang hồi sinh câu hỏi cũ này vì câu trả lời không giải thích được lý do tại saomemcpy thực sự được ưu tiên.

memcpy được ưu tiên vì nó làm cho nó rõ ràng các lập trình viên muốn sao chép nội dung và không chỉ đơn giản là con trỏ.

Trong ví dụ sau, hai nhiệm vụ thực hiện hai việc rất khác nhau:

struct Type *s1,*s2; 
*s1=*s2; 
s1=s2; 

Vô tình sử dụng một thay vì bên kia có thể có những ảnh hưởng tai hại. Trình biên dịch sẽ không phàn nàn. Trừ khi chương trình bị treo khi một con trỏ chưa được khởi tạo được sử dụng, lỗi có thể không được chú ý trong một thời gian dài và tạo ra các hiệu ứng phụ lạ.

Viết nó như là một trong số:

memcpy(s1,s2,sizeof(*s1)); 
memcpy(s1,s2,sizeof(*s2)); 
memcpy(s1,s2,sizeof(struct Type)); 

để người đọc biết rằng mục đích là để sao chép nội dung (tại các chi phí về an toàn loại và kiểm tra giới hạn).

Một số trình biên dịch (gcc chẳng hạn) thậm chí đưa ra cảnh báo về sizeof khi họ gặp phải một cái gì đó như:

memcpy(s1,s2,sizeof(s1)); 
Các vấn đề liên quan