Có bất kỳ nhược điểm nào trong việc chuyển các cấu trúc theo giá trị trong C, thay vì truyền một con trỏ không?


157

Có bất kỳ nhược điểm nào trong việc chuyển các cấu trúc theo giá trị trong C, thay vì truyền một con trỏ không?

Nếu cấu trúc lớn, rõ ràng có khía cạnh hiệu suất của việc sao chép nhiều dữ liệu, nhưng đối với một cấu trúc nhỏ hơn, về cơ bản nó sẽ giống như truyền một số giá trị cho hàm.

Nó thậm chí còn thú vị hơn khi được sử dụng làm giá trị trả về. C chỉ có các giá trị trả về duy nhất từ ​​các hàm, nhưng bạn thường cần một vài giá trị. Vì vậy, một giải pháp đơn giản là đặt chúng trong một cấu trúc và trả lại nó.

Có bất kỳ lý do cho hoặc chống lại điều này?

Vì mọi người có thể không rõ ràng về những gì tôi đang nói ở đây, tôi sẽ đưa ra một ví dụ đơn giản.

Nếu bạn đang lập trình bằng C, sớm muộn bạn cũng sẽ bắt đầu viết các hàm giống như thế này:

void examine_data(const char *ptr, size_t len)
{
    ...
}

char *p = ...;
size_t l = ...;
examine_data(p, l);

Đây không phải là một vấn đề. Vấn đề duy nhất là bạn phải đồng ý với đồng nghiệp của mình, theo đó thứ tự các tham số sẽ là do đó bạn sử dụng cùng một quy ước trong tất cả các chức năng.

Nhưng điều gì xảy ra khi bạn muốn trả lại cùng loại thông tin? Bạn thường nhận được một cái gì đó như thế này:

char *get_data(size_t *len);
{
    ...
    *len = ...datalen...;
    return ...data...;
}
size_t len;
char *p = get_data(&len);

Điều này hoạt động tốt, nhưng có nhiều vấn đề hơn. Giá trị trả về là giá trị trả về, ngoại trừ trong triển khai này thì không. Không có cách nào để nói ở trên rằng hàm get_data không được phép xem xét những gì len trỏ tới. Và không có gì làm cho trình biên dịch kiểm tra rằng một giá trị thực sự được trả về thông qua con trỏ đó. Vì vậy, vào tháng tới, khi một người khác sửa đổi mã mà không hiểu đúng về mã (vì anh ta không đọc tài liệu?), Nó sẽ bị hỏng mà không ai nhận ra, hoặc nó bắt đầu bị sập một cách ngẫu nhiên.

Vì vậy, giải pháp tôi đề xuất là cấu trúc đơn giản

struct blob { char *ptr; size_t len; }

Các ví dụ có thể được viết lại như thế này:

void examine_data(const struct blob data)
{
    ... use data.tr and data.len ...
}

struct blob = { .ptr = ..., .len = ... };
examine_data(blob);

struct blob get_data(void);
{
    ...
    return (struct blob){ .ptr = ...data..., .len = ...len... };
}
struct blob data = get_data();

Vì một số lý do, tôi nghĩ rằng hầu hết mọi người sẽ theo bản năng làm cho tests_data lấy một con trỏ đến một blob struct, nhưng tôi không hiểu tại sao. Nó vẫn nhận được một con trỏ và một số nguyên, rõ ràng hơn là chúng đi cùng nhau. Và trong trường hợp get_data, không thể gây rối theo cách tôi đã mô tả trước đây, vì không có giá trị đầu vào cho độ dài và phải có độ dài được trả về.


Đối với những gì nó có giá trị, void examine data(const struct blob)là không chính xác.
Chris Lutz

Cảm ơn, đã thay đổi nó để bao gồm một tên biến.
dkagedal

1
"Không có cách nào để nói ở trên rằng hàm get_data không được phép xem những gì len trỏ tới. Và không có gì khiến trình biên dịch kiểm tra rằng một giá trị thực sự được trả về qua con trỏ đó." - điều này không có ý nghĩa gì với tôi cả (có lẽ vì ví dụ của bạn là mã không hợp lệ do hai dòng cuối cùng xuất hiện bên ngoài một hàm); xin vui lòng bạn có thể giải thích?
Adam Spiers

2
Hai dòng bên dưới hàm có để minh họa cách gọi hàm. Chữ ký hàm không đưa ra gợi ý nào cho thực tế rằng việc triển khai sẽ chỉ ghi vào con trỏ. Và trình biên dịch không có cách nào để biết rằng nó nên xác minh rằng một giá trị được ghi vào con trỏ, vì vậy cơ chế giá trị trả về chỉ có thể được mô tả trong tài liệu.
dkagedal

1
Lý do chính khiến mọi người không làm điều này thường xuyên hơn trong C là lịch sử. Trước C89, bạn không thể vượt qua hoặc trả lại các cấu trúc theo giá trị, vì vậy tất cả các giao diện hệ thống có trước C89 và logic phải gettimeofdaysử dụng con trỏ (như ) thay vào đó và mọi người lấy đó làm ví dụ.
zwol

Câu trả lời:


202

Đối với các cấu trúc nhỏ (ví dụ: điểm, trực tràng) đi qua giá trị là hoàn toàn chấp nhận được. Nhưng, ngoài tốc độ, còn có một lý do khác khiến bạn nên cẩn thận vượt qua / trả lại các cấu trúc lớn theo giá trị: Không gian ngăn xếp.

Rất nhiều lập trình C dành cho các hệ thống nhúng, trong đó bộ nhớ ở mức cao và kích thước ngăn xếp có thể được đo bằng KB hoặc thậm chí là byte ... Nếu bạn chuyển hoặc trả lại các cấu trúc theo giá trị, các bản sao của các cấu trúc đó sẽ được đặt vào ngăn xếp, có khả năng gây ra tình huống trang web này được đặt tên theo ...

Nếu tôi thấy một ứng dụng dường như sử dụng ngăn xếp quá mức, các cấu trúc được truyền theo giá trị là một trong những điều tôi tìm kiếm đầu tiên.


2
"Nếu bạn chuyển hoặc trả lại các cấu trúc theo giá trị, các bản sao của các cấu trúc đó sẽ được đặt trên ngăn xếp" Tôi sẽ gọi braindead bất kỳ chuỗi công cụ nào làm như vậy. Vâng, thật đáng buồn khi rất nhiều người sẽ làm điều đó, nhưng đó không phải là bất cứ điều gì mà tiêu chuẩn C yêu cầu. Một trình biên dịch lành mạnh sẽ tối ưu hóa tất cả.
Tái lập lại

1
@KubaOber Đây là lý do tại sao điều đó không được thực hiện thường xuyên: stackoverflow.com/questions/552134/NH
Roddy

1
Có một đường dứt khoát ngăn cách một cấu trúc nhỏ với một cấu trúc lớn?
Josie Thompson

63

Một lý do để không làm điều này chưa được đề cập là điều này có thể gây ra vấn đề trong đó vấn đề tương thích nhị phân.

Tùy thuộc vào trình biên dịch được sử dụng, các cấu trúc có thể được chuyển qua ngăn xếp hoặc các thanh ghi tùy thuộc vào các tùy chọn / triển khai trình biên dịch

Xem: http://gcc.gnu.org/onlinesocs/gcc/Code-Gen-Options.html

-fpcc-struct-return

-freg-struct-return

Nếu hai trình biên dịch không đồng ý, mọi thứ có thể nổ tung. Không cần phải nói lý do chính để không làm điều này được minh họa là tiêu thụ ngăn xếp và lý do hiệu suất.


4
Đây là loại câu trả lời tôi đang tìm kiếm.
dkagedal

2
Đúng, nhưng các tùy chọn đó không liên quan đến giá trị truyền qua. chúng liên quan đến việc trả lại các cấu trúc đó là một điều hoàn toàn khác. Trả lại mọi thứ bằng cách tham khảo thường là một cách chắc chắn để tự bắn vào cả hai chân. int &bar() { int f; int &j(f); return j;};
Roddy

19

Để thực sự trả lời câu hỏi này, người ta cần đào sâu vào vùng đất lắp ráp:

(Ví dụ sau sử dụng gcc trên x86_64. Bất kỳ ai cũng được chào đón để thêm các kiến ​​trúc khác như MSVC, ARM, v.v.)

Hãy có chương trình ví dụ của chúng tôi:

// foo.c

typedef struct
{
    double x, y;
} point;

void give_two_doubles(double * x, double * y)
{
    *x = 1.0;
    *y = 2.0;
}

point give_point()
{
    point a = {1.0, 2.0};
    return a;
}

int main()
{
    return 0;
}

Biên dịch nó với tối ưu hóa đầy đủ

gcc -Wall -O3 foo.c -o foo

Nhìn vào hội đồng:

objdump -d foo | vim -

Đây là những gì chúng ta nhận được:

0000000000400480 <give_two_doubles>:
    400480: 48 ba 00 00 00 00 00    mov    $0x3ff0000000000000,%rdx
    400487: 00 f0 3f 
    40048a: 48 b8 00 00 00 00 00    mov    $0x4000000000000000,%rax
    400491: 00 00 40 
    400494: 48 89 17                mov    %rdx,(%rdi)
    400497: 48 89 06                mov    %rax,(%rsi)
    40049a: c3                      retq   
    40049b: 0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

00000000004004a0 <give_point>:
    4004a0: 66 0f 28 05 28 01 00    movapd 0x128(%rip),%xmm0
    4004a7: 00 
    4004a8: 66 0f 29 44 24 e8       movapd %xmm0,-0x18(%rsp)
    4004ae: f2 0f 10 05 12 01 00    movsd  0x112(%rip),%xmm0
    4004b5: 00 
    4004b6: f2 0f 10 4c 24 f0       movsd  -0x10(%rsp),%xmm1
    4004bc: c3                      retq   
    4004bd: 0f 1f 00                nopl   (%rax)

Không bao gồm các noplmiếng đệm, give_two_doubles()có 27 byte trong khi give_point()có 29 byte. Mặt khác, give_point()mang lại một chỉ dẫn ít hơngive_two_doubles()

Điều thú vị là chúng tôi nhận thấy trình biên dịch đã có thể tối ưu hóa movthành các biến thể SSE2 nhanh hơn movapdmovsd. Hơn nữa, give_two_doubles()thực sự di chuyển dữ liệu vào và ra khỏi bộ nhớ, điều này làm cho mọi thứ chậm lại.

Rõ ràng phần lớn điều này có thể không được áp dụng trong các môi trường nhúng (đó là nơi mà sân chơi cho C hầu hết thời gian ngày nay). Tôi không phải là một phù thủy lắp ráp nên mọi ý kiến ​​sẽ được hoan nghênh!


6
Đếm số lượng hướng dẫn không thú vị lắm, trừ khi bạn có thể cho thấy sự khác biệt lớn hoặc đếm các khía cạnh thú vị hơn như số lần nhảy khó dự đoán, v.v. Các thuộc tính hiệu suất thực tế tinh tế hơn nhiều so với số chỉ dẫn .
dkagedal

6
@dkagedal: Đúng. Nhìn lại, tôi nghĩ rằng câu trả lời của riêng tôi được viết rất kém. Mặc dù tôi không tập trung vào số lượng hướng dẫn rất nhiều (dunno điều mang lại cho bạn ấn tượng đó: P), nhưng điểm thực tế cần làm là chuyển cấu trúc theo giá trị tốt hơn là chuyển qua tham chiếu cho các loại nhỏ. Dù sao, việc chuyển theo giá trị được ưu tiên vì đơn giản hơn (không phải tung hứng cả đời, không cần phải lo lắng về việc ai đó thay đổi dữ liệu của bạn hoặc constmọi lúc) và tôi thấy rằng không có nhiều hình phạt về hiệu suất (nếu không đạt được) trong sao chép giá trị truyền qua , trái với những gì nhiều người có thể tin.
kizzx2

15

Giải pháp đơn giản sẽ trả về mã lỗi dưới dạng giá trị trả về và mọi thứ khác là tham số trong hàm,
Tham số này có thể là một cấu trúc tất nhiên nhưng không thấy bất kỳ lợi thế cụ thể nào vượt qua giá trị này, chỉ cần gửi một con trỏ.
Truyền cấu trúc theo giá trị là nguy hiểm, bạn cần hết sức cẩn thận những gì bạn đang vượt qua, hãy nhớ rằng không có hàm tạo sao chép trong C, nếu một trong các tham số cấu trúc là một con trỏ, giá trị con trỏ sẽ bị sao chép và rất khó để duy trì.

Chỉ cần hoàn thành câu trả lời (tín dụng đầy đủ cho Roddy ), việc sử dụng ngăn xếp là một lý do khác không vượt qua cấu trúc theo giá trị, hãy tin rằng tôi gỡ lỗi tràn ngăn xếp là PITA thực.

Phát lại để bình luận:

Truyền cấu trúc bằng con trỏ có nghĩa là một số thực thể có quyền sở hữu đối tượng này và có kiến ​​thức đầy đủ về những gì và khi nào nên được phát hành. Truyền cấu trúc theo giá trị tạo ra một tham chiếu ẩn đến dữ liệu bên trong của struct (con trỏ đến cấu trúc khác, v.v.) tại đây rất khó để duy trì (có thể nhưng tại sao?).


6
Nhưng vượt qua một con trỏ không "nguy hiểm" hơn chỉ vì bạn đặt nó trong một cấu trúc, vì vậy tôi không mua nó.
dkagedal

Điểm tuyệt vời khi sao chép một cấu trúc có chứa một con trỏ. Điểm này có thể không rõ ràng lắm. Đối với những người không biết những gì anh ta đang đề cập đến, hãy tìm kiếm trên bản sao sâu so với bản sao nông.
zooropa

1
Một trong những quy ước của hàm C là có các tham số đầu ra được liệt kê đầu tiên trước các tham số đầu vào, ví dụ int func (char * out, char * in);
zooropa

Ý bạn là như thế nào ví dụ getaddrinfo () đặt tham số đầu ra cuối cùng? :-) Có hàng ngàn quy ước và bạn có thể chọn bất cứ điều gì bạn muốn.
dkagedal

10

Một điều mọi người ở đây đã quên đề cập đến cho đến nay (hoặc tôi đã bỏ qua nó) là các cấu trúc thường có một phần đệm!

struct {
  short a;
  char b;
  short c;
  char d;
}

Mỗi char là 1 byte, mỗi short là 2 byte. Làm thế nào lớn là cấu trúc? Không, nó không phải là 6 byte. Ít nhất là không trên bất kỳ hệ thống được sử dụng phổ biến hơn. Trên hầu hết các hệ thống sẽ là 8. Vấn đề là, sự liên kết không phải là hằng số, nó phụ thuộc vào hệ thống, do đó, cùng một cấu trúc sẽ có sự liên kết khác nhau và kích thước khác nhau trên các hệ thống khác nhau.

Không chỉ việc đệm sẽ tiếp tục ăn hết ngăn xếp của bạn, nó còn làm tăng thêm sự không chắc chắn của việc không thể dự đoán trước về phần đệm, trừ khi bạn biết cách hệ thống của bạn và sau đó nhìn vào từng cấu trúc bạn có trong ứng dụng của mình và tính kích thước cho nó. Vượt qua một con trỏ cần một lượng không gian có thể dự đoán được - không có sự không chắc chắn. Kích thước của một con trỏ được biết đến với hệ thống, nó luôn bằng nhau, bất kể cấu trúc trông như thế nào và kích thước con trỏ luôn được chọn theo cách chúng được căn chỉnh và không cần đệm.


2
Phải, nhưng phần đệm tồn tại mà không phụ thuộc vào việc chuyển cấu trúc theo giá trị hoặc bằng tham chiếu.
Ilya

2
@dkagedal: Phần nào của "kích cỡ khác nhau trên các hệ thống khác nhau" bạn không hiểu? Chỉ vì nó là như vậy trên hệ thống của bạn, bạn cho rằng nó phải giống với bất kỳ ai khác - đó chính xác là lý do tại sao bạn không nên vượt qua giá trị. Thay đổi mẫu để nó cũng thất bại trên hệ thống của bạn.
Mecki

2
Tôi nghĩ ý kiến ​​của Mecki về đệm cấu trúc có liên quan đặc biệt đối với các hệ thống nhúng trong đó kích thước ngăn xếp có thể là một vấn đề.
zooropa

1
Tôi đoán mặt trái của đối số là nếu cấu trúc của bạn là một cấu trúc đơn giản (chứa một vài kiểu nguyên thủy), việc truyền theo giá trị sẽ cho phép trình biên dịch xử lý nó bằng các thanh ghi - trong khi nếu bạn sử dụng các con trỏ, mọi thứ sẽ kết thúc bộ nhớ, mà chậm hơn. Điều đó trở nên khá thấp và khá nhiều phụ thuộc vào kiến ​​trúc mục tiêu của bạn, nếu bất kỳ vấn đề nào trong số những vấn đề này.
kizzx2

1
Trừ khi cấu trúc của bạn nhỏ hoặc CPU của bạn có nhiều thanh ghi (và CPU Intel thì không), dữ liệu kết thúc trên ngăn xếp và đó cũng là bộ nhớ và nhanh / chậm như mọi bộ nhớ khác. Mặt khác, một con trỏ luôn nhỏ và chỉ là một con trỏ và chính con trỏ sẽ luôn luôn kết thúc trong một thanh ghi khi được sử dụng thường xuyên hơn.
Mecki

9

Tôi nghĩ rằng câu hỏi của bạn đã tóm tắt mọi thứ khá tốt.

Một lợi thế khác của việc chuyển cấu trúc theo giá trị là quyền sở hữu bộ nhớ rõ ràng. Không có thắc mắc về việc cấu trúc là từ đống, và ai có trách nhiệm giải phóng nó.


9

Tôi muốn nói việc chuyển các cấu trúc (không quá lớn) theo giá trị, cả dưới dạng tham số và giá trị trả về, là một kỹ thuật hoàn toàn hợp pháp. Tất nhiên, người ta phải quan tâm rằng cấu trúc là loại POD hoặc ngữ nghĩa sao chép được chỉ định rõ.

Cập nhật: Xin lỗi, tôi đã có nắp suy nghĩ C ++ của mình. Tôi nhớ lại một thời gian khi C không trả lại cấu trúc từ hàm, nhưng điều này có lẽ đã thay đổi kể từ đó. Tôi vẫn sẽ nói nó hợp lệ miễn là tất cả các trình biên dịch bạn muốn sử dụng hỗ trợ thực hành.


Lưu ý rằng câu hỏi của tôi là về C, không phải C ++.
dkagedal

Nó là hợp lệ để trả về struct từ chức năng chỉ không hữu ích :)
Ilya

1
Tôi thích đề xuất của llya sử dụng trả về làm mã lỗi và tham số để trả về dữ liệu từ hàm.
zooropa

8

Đây là điều không ai nhắc đến:

void examine_data(const char *c, size_t l)
{
    c[0] = 'l'; // compiler error
}

void examine_data(const struct blob blob)
{
    blob.ptr[0] = 'l'; // perfectly legal, quite likely to blow up at runtime
}

Thành viên của a const structconst, nhưng nếu thành viên đó là một con trỏ (thích char *), nó sẽ trở thành char *constchứ không phải là thứ const char *chúng ta thực sự muốn. Tất nhiên, chúng tôi có thể cho rằng đó constlà tài liệu về ý định và bất kỳ ai vi phạm điều này đều viết mã xấu (nhưng đó là không đủ cho một số người (đặc biệt là những người chỉ mất bốn giờ để theo dõi nguyên nhân của một tai nạn).

Giải pháp thay thế có thể là tạo struct const_blob { const char *c; size_t l }và sử dụng nó, nhưng điều đó khá lộn xộn - nó gặp phải vấn đề về cách đặt tên giống như tôi gặp phải với typedefcon trỏ ing. Vì vậy, hầu hết mọi người dính vào chỉ có hai tham số (hoặc, nhiều khả năng cho trường hợp này, sử dụng thư viện chuỗi).


Vâng, điều đó hoàn toàn hợp pháp và đôi khi bạn cũng muốn làm điều đó. Nhưng tôi đồng ý rằng đó là một hạn chế của giải pháp cấu trúc mà bạn không thể tạo ra các con trỏ mà chúng trỏ tới trỏ tới const.
dkagedal

Một Gotcha khó chịu với các struct const_blobgiải pháp là ngay cả khi const_blobcó thành viên khác blobchỉ trong "gián tiếp const-ness", loại struct blob*một struct const_blob*sẽ được xem xét riêng biệt cho mục đích cai trị răng cưa nghiêm ngặt. Do đó, nếu mã chuyển từ a blob*sang a const_blob*, bất kỳ lần ghi nào sau đó vào cấu trúc cơ bản bằng cách sử dụng một loại sẽ âm thầm làm mất hiệu lực mọi con trỏ hiện có của loại khác, do đó, bất kỳ việc sử dụng nào sẽ gọi Hành vi không xác định (thường có thể vô hại, nhưng có thể gây tử vong) .
supercat

5

Trang 150 của Hướng dẫn lắp ráp PC trên http://www.drpaulcarter.com/pcasm/ có giải thích rõ ràng về cách C cho phép một hàm trả về cấu trúc:

C cũng cho phép một loại cấu trúc được sử dụng làm giá trị trả về của một chức năng. Rõ ràng một cấu trúc không thể được trả về trong thanh ghi EAX. Trình biên dịch khác nhau xử lý tình huống này khác nhau. Một giải pháp phổ biến mà trình biên dịch sử dụng là viết lại hàm bên trong như một hàm lấy con trỏ cấu trúc làm tham số. Con trỏ được sử dụng để đặt giá trị trả về vào một cấu trúc được xác định bên ngoài thường trình được gọi.

Tôi sử dụng mã C sau đây để xác minh tuyên bố trên:

struct person {
    int no;
    int age;
};

struct person create() {
    struct person jingguo = { .no = 1, .age = 2};
    return jingguo;
}

int main(int argc, const char *argv[]) {
    struct person result;
    result = create();
    return 0;
}

Sử dụng "gcc -S" để tạo lắp ráp cho đoạn mã C này:

    .file   "foo.c"
    .text
.globl create
    .type   create, @function
create:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $16, %esp
    movl    8(%ebp), %ecx
    movl    $1, -8(%ebp)
    movl    $2, -4(%ebp)
    movl    -8(%ebp), %eax
    movl    -4(%ebp), %edx
    movl    %eax, (%ecx)
    movl    %edx, 4(%ecx)
    movl    %ecx, %eax
    leave
    ret $4
    .size   create, .-create
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $20, %esp
    leal    -8(%ebp), %eax
    movl    %eax, (%esp)
    call    create
    subl    $4, %esp
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

Ngăn xếp trước khi tạo cuộc gọi:

        +---------------------------+
ebp     | saved ebp                 |
        +---------------------------+
ebp-4   | age part of struct person | 
        +---------------------------+
ebp-8   | no part of struct person  |
        +---------------------------+        
ebp-12  |                           |
        +---------------------------+
ebp-16  |                           |
        +---------------------------+
ebp-20  | ebp-8 (address)           |
        +---------------------------+

Ngăn xếp ngay sau khi gọi tạo:

        +---------------------------+
        | ebp-8 (address)           |
        +---------------------------+
        | return address            |
        +---------------------------+
ebp,esp | saved ebp                 |
        +---------------------------+

2
Có hai vấn đề ở đây. Điều rõ ràng nhất là điều này hoàn toàn không mô tả "làm thế nào C cho phép một hàm trả về một cấu trúc". Điều này chỉ mô tả cách có thể được thực hiện trên phần cứng x86 32 bit, đây là một trong những kiến ​​trúc hạn chế nhất khi bạn nhìn vào số lượng thanh ghi, v.v. Vấn đề thứ hai là cách trình biên dịch C tạo mã để trả về giá trị được quyết định bởi ABI (ngoại trừ các hàm không xuất hoặc nội tuyến). Và nhân tiện, các hàm nội tuyến có lẽ là một trong những nơi mà các cấu trúc trả về là hữu ích nhất.
năm11 lúc 22:51

Cảm ơn đã sửa chữa. Để biết chi tiết đầy đủ về quy ước gọi, en.wikipedia.org/wiki/Calling_convent là một tài liệu tham khảo tốt.
Jingguo Yao

@dkagedal: Điều quan trọng không chỉ là x86 xảy ra để làm mọi thứ theo cách này, mà là tồn tại một cách tiếp cận "phổ quát" (tức là phương pháp này) cho phép trình biên dịch cho bất kỳ nền tảng nào hỗ trợ trả về bất kỳ loại cấu trúc nào không ' t lớn đến mức thổi tung đống. Mặc dù trình biên dịch cho nhiều nền tảng sẽ sử dụng các phương tiện hiệu quả khác để xử lý một số giá trị trả về kiểu cấu trúc, nhưng ngôn ngữ không cần phải giới hạn các kiểu trả về cấu trúc để các nền tảng có thể xử lý tối ưu.
supercat

0

Tôi chỉ muốn chỉ ra một lợi thế của việc chuyển các cấu trúc của bạn theo giá trị là trình biên dịch tối ưu hóa có thể tối ưu hóa mã của bạn tốt 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.