Gán một cấu trúc cho một cấu trúc khác trong C


146

Bạn có thể gán một thể hiện của một cấu trúc cho một cấu trúc khác không, như vậy:

struct Test t1;
struct Test t2;
t2 = t1;

Tôi đã thấy nó hoạt động cho các cấu trúc đơn giản, bu có hoạt động cho các cấu trúc phức tạp không?
Làm thế nào để trình biên dịch biết cách sao chép các mục dữ liệu tùy thuộc vào loại của chúng, tức là phân biệt giữa một intchuỗi và chuỗi?

Câu trả lời:


151

Có nếu cấu trúc cùng loại. Hãy nghĩ nó như một bản sao bộ nhớ.


72
Hãy nhớ rằng không có bản sao sâu, chỉ vào bộ nhớ không được sao chép.
Georg Schölly

3
Đồng thời cũng là một vấn đề ở đây.
Tim Post

16
@Tim Đồng thời không phải là vấn đề nhiều hơn so với việc gán các loại được tích hợp sẵn, như số nguyên và nhân đôi - gán không phải là một hoạt động nguyên tử cho cả hai.

2
OK, nếu có bản sao được tạo, tôi có thể giải phóng bộ nhớ sau với free () không?
Betlista

5
@Betlista Bạn không thể giải phóng bộ nhớ với free () vì chúng là các biến tự động: en.wikipedia.org/wiki/Automatic_variable
joshdoe

138

Có, sự phân công được hỗ trợ cho các cấu trúc. Tuy nhiên, có những vấn đề:

struct S {
   char * p;
};

struct S s1, s2;
s1.p = malloc(100);
s2 = s1;

Bây giờ các con trỏ của cả hai cấu trúc trỏ đến cùng một khối bộ nhớ - trình biên dịch không sao chép dữ liệu được trỏ vào dữ liệu. Bây giờ rất khó để biết cá thể struct nào sở hữu dữ liệu. Đây là lý do tại sao C ++ đã phát minh ra khái niệm về toán tử gán gán có thể xác định người dùng - bạn có thể viết mã cụ thể để xử lý trường hợp này.


1
Tôi ủng hộ nó bởi vì đọc nó làm tôi nhận ra lỗi / thiếu sót trong câu trả lời của riêng tôi.
Clifford

1
+1 để lưu ý rằng thực tế không có bất kỳ sự sao chép nào đang diễn ra.
Tom Duckering

14
Tại sao điều này được gắn cờ là thư rác? Có ai đó mất kiểm soát chuột của họ?
Georg Fritzsche

@gf Và hình như là phản cảm quá!

2
@rahmanisback Câu trả lời của anon khá rõ ràng về chủ đề này: "trình biên dịch không sao chép dữ liệu nhọn ". Dữ liệu của structchính nó được sao chép rõ ràng.
Tobias

24

Trước tiên hãy xem ví dụ này:

Mã C cho một chương trình C đơn giản được đưa ra dưới đây

struct Foo {
    char a;
    int b;
    double c;
    } foo1,foo2;

void foo_assign(void)
{
    foo1 = foo2;
}
int main(/*char *argv[],int argc*/)
{
    foo_assign();
return 0;
}

Mã ASM tương đương cho foo_assign () là

00401050 <_foo_assign>:
  401050:   55                      push   %ebp
  401051:   89 e5                   mov    %esp,%ebp
  401053:   a1 20 20 40 00          mov    0x402020,%eax
  401058:   a3 30 20 40 00          mov    %eax,0x402030
  40105d:   a1 24 20 40 00          mov    0x402024,%eax
  401062:   a3 34 20 40 00          mov    %eax,0x402034
  401067:   a1 28 20 40 00          mov    0x402028,%eax
  40106c:   a3 38 20 40 00          mov    %eax,0x402038
  401071:   a1 2c 20 40 00          mov    0x40202c,%eax
  401076:   a3 3c 20 40 00          mov    %eax,0x40203c
  40107b:   5d                      pop    %ebp
  40107c:   c3                      ret    

Như bạn có thể thấy rằng một phép gán được thay thế đơn giản bằng lệnh "Mov" trong cụm, toán tử gán chỉ đơn giản có nghĩa là di chuyển dữ liệu từ một vị trí bộ nhớ sang một vị trí bộ nhớ khác. Nhiệm vụ sẽ chỉ làm điều đó cho các thành viên trực tiếp của một cấu trúc và sẽ không sao chép khi bạn có các kiểu dữ liệu phức tạp trong một cấu trúc. Ở đây COMPLEX có nghĩa là bạn không thể có mảng con trỏ, trỏ đến danh sách.

Bản thân một mảng các ký tự trong một cấu trúc sẽ không hoạt động trên hầu hết các trình biên dịch, điều này là do phép gán sẽ đơn giản cố gắng sao chép mà không cần nhìn vào kiểu dữ liệu có kiểu phức tạp.


2
Bạn có thể giải thích những điều kiện nào sẽ thất bại vì nó dường như luôn hoạt động với tôi không
AlphaGoku

15

Đây là một bản sao đơn giản, giống như bạn sẽ làm với memcpy()(thực sự, một số trình biên dịch thực sự tạo ra một lệnh gọi memcpy()cho mã đó). Không có "chuỗi" trong C, chỉ có một con trỏ một ký tự. Nếu cấu trúc nguồn của bạn chứa một con trỏ như vậy, thì con trỏ sẽ được sao chép, chứ không phải chính các ký tự.


OK, vì vậy trình biên dịch dịch cái này sang memcpy, xem tại đây: godbolt.org/z/nPxqWc - Nhưng bây giờ nếu tôi chuyển các con trỏ giống hệt nhau ab, *a = *bđược dịch thành một memcpyhành vi không xác định, bởi vì memcpy"Vùng nhớ không được chồng lấp." (trích dẫn từ trang người đàn ông). Vì vậy, trình biên dịch sai trong việc sử dụng memcpyhay tôi sai khi viết một bài tập như vậy?
không phải người dùng

6

Ý của bạn là "Phức tạp" như số phức với phần thực và phần ảo? Điều này dường như là không thể, vì vậy nếu không bạn sẽ phải đưa ra một ví dụ vì "phức tạp" có nghĩa là không có gì cụ thể về ngôn ngữ C.

Bạn sẽ nhận được một bản sao bộ nhớ trực tiếp của cấu trúc; cho dù đó là những gì bạn muốn phụ thuộc vào cấu trúc. Ví dụ: nếu cấu trúc chứa một con trỏ, cả hai bản sao sẽ trỏ đến cùng một dữ liệu. Điều này có thể hoặc không thể là những gì bạn muốn; đó là xuống thiết kế chương trình của bạn.

Để thực hiện bản sao 'thông minh' (hoặc bản sao 'sâu'), bạn sẽ cần thực hiện một chức năng để thực hiện bản sao. Điều này có thể rất khó đạt được nếu bản thân cấu trúc chứa con trỏ và cấu trúc cũng chứa con trỏ và có lẽ con trỏ đến cấu trúc như vậy (có lẽ đó là ý nghĩa của "phức tạp") và rất khó để duy trì. Giải pháp đơn giản là sử dụng C ++ và triển khai các hàm tạo sao chép và toán tử gán cho từng cấu trúc hoặc lớp, sau đó mỗi cấu trúc chịu trách nhiệm về ngữ nghĩa sao chép của chính nó, bạn có thể sử dụng cú pháp gán và nó được duy trì dễ dàng hơn.

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.