không dấu int so với size_t


492

Tôi nhận thấy rằng mã C và C ++ hiện đại dường như sử dụng size_tthay vì int/ unsigned intkhá nhiều ở mọi nơi - từ các tham số cho các hàm chuỗi C đến STL. Tôi tò mò về lý do cho điều này và những lợi ích mà nó mang lại.

Câu trả lời:


388

Các size_ttype là kiểu integer unsigned mà là kết quả của các sizeofnhà điều hành (và các offsetofnhà điều hành), vì vậy nó được đảm bảo để có đủ lớn để chứa kích thước của đối tượng lớn nhất hệ thống của bạn có thể xử lý (ví dụ, một mảng tĩnh của 8Gb).

Các size_tloại có thể được lớn hơn, bằng hoặc nhỏ hơn một unsigned int, và trình biên dịch của bạn có thể làm cho các giả định về nó để tối ưu hóa.

Bạn có thể tìm thấy thông tin chính xác hơn trong tiêu chuẩn C99, phần 7.17, bản nháp có sẵn trên Internet ở định dạng pdf hoặc trong tiêu chuẩn C11, phần 7.19, cũng có sẵn dưới dạng bản nháp pdf .


50
Không. Hãy nghĩ về x86-16 với mô hình bộ nhớ lớn (không lớn): Con trỏ ở xa (32 bit), nhưng các đối tượng riêng lẻ bị giới hạn ở 64k (vì vậy size_t có thể là 16 bit).
dan04

8
"kích thước của đối tượng lớn nhất" không phải là từ ngữ kém, nhưng hoàn toàn chính xác. Sixe của một đối tượng có thể bị giới hạn hơn nhiều so với không gian địa chỉ.
gnasher729

3
"Trình biên dịch của bạn có thể đưa ra giả định về nó": Tôi hy vọng trình biên dịch biết phạm vi chính xác của các giá trị size_tcó thể đại diện! Nếu không, ai làm?
Marc van Leeuwen

4
@Marc: Tôi nghĩ rằng vấn đề là trình biên dịch có thể làm được điều gì đó với kiến ​​thức đó.

8
Tôi chỉ muốn loại ngày càng phổ biến này không yêu cầu bao gồm một tệp tiêu đề.
2023370

98

Classic C (phương ngữ đầu tiên của C được mô tả bởi Brian Kernighan và Dennis Ritchie trong Ngôn ngữ lập trình C, Prentice-Hall, 1978) đã không cung cấp size_t. Ủy ban tiêu chuẩn C được giới thiệu size_tđể loại bỏ một vấn đề về tính di động

Giải thích chi tiết tại embed.com (với một ví dụ rất hay)


6
Một bài viết tuyệt vời khác giải thích cả size_t và ptrdiff_t: viva64.com/en/a/0050
Ihor Kaharlichenko

73

Nói tóm lại, size_tkhông bao giờ là tiêu cực và nó tối đa hóa hiệu suất vì nó được coi là loại số nguyên không dấu đủ lớn - nhưng không quá lớn - để biểu thị kích thước của đối tượng lớn nhất có thể trên nền tảng đích.

Kích thước không bao giờ nên âm và thực sự size_tlà một loại không dấu. Ngoài ra, vì size_tkhông được ký, bạn có thể lưu trữ các số lớn gấp đôi so với loại đã ký tương ứng, bởi vì chúng ta có thể sử dụng bit dấu để biểu thị cường độ, giống như tất cả các bit khác trong số nguyên không dấu. Khi chúng tôi đạt được thêm một bit, chúng tôi sẽ nhân phạm vi số mà chúng tôi có thể đại diện cho hệ số khoảng hai.

Vì vậy, bạn hỏi, tại sao không chỉ sử dụng một unsigned int? Nó có thể không có khả năng giữ số lượng đủ lớn. Trong một triển khai có unsigned int32 bit, số lớn nhất có thể biểu thị là 4294967295. Một số bộ xử lý, chẳng hạn như IP16L32, có thể sao chép các đối tượng lớn hơn 4294967295byte.

Vì vậy, bạn hỏi, tại sao không sử dụng một unsigned long int? Nó thực hiện một số lượng hiệu suất trên một số nền tảng. Tiêu chuẩn C yêu cầu longchiếm ít nhất 32 bit. Một nền tảng IP16L32 thực hiện mỗi chiều dài 32 bit dưới dạng một cặp từ 16 bit. Hầu như tất cả các toán tử 32 bit trên các nền tảng này đều yêu cầu hai hướng dẫn, nếu không muốn nói là vì chúng hoạt động với 32 bit trong hai khối 16 bit. Ví dụ, di chuyển dài 32 bit thường yêu cầu hai hướng dẫn máy - một để di chuyển mỗi đoạn 16 bit.

Sử dụng size_tđể tránh hiệu suất này. Theo bài viết tuyệt vời này , "Loại size_tlà một typedef là bí danh cho một số loại số nguyên không dấu, thông thường unsigned inthoặc unsigned long, nhưng thậm chí có thể unsigned long long. Mỗi triển khai C tiêu chuẩn được cho là chọn số nguyên không dấu đủ lớn - nhưng không lớn hơn cần thiết-- để thể hiện kích thước của đối tượng lớn nhất có thể trên nền tảng đích. "


1
Xin lỗi để nhận xét về điều này sau một thời gian dài, nhưng tôi chỉ phải xác nhận số lớn nhất mà một số nguyên không dấu có thể giữ - có lẽ tôi đang hiểu nhầm thuật ngữ của bạn, nhưng tôi nghĩ rằng số lớn nhất mà một số không dấu có thể giữ là 4294967295, 65356 tối đa của một không dấu ngắn.
Mitch

Nếu int unsign của bạn chiếm 32 bit, thì có, số lớn nhất nó có thể giữ là 2 ^ 32 - 1, đó là 4294967295 (0xffffffff). Bạn có một câu hỏi?
Rose Perrone

3
@Mitch: Giá trị lớn nhất có thể được biểu diễn trong một unsigned intlon và thay đổi từ hệ thống này sang hệ thống khác. Nó bắt buộc phải có ít nhất 65536 , nhưng nó phổ biến 4294967295và có thể là 18446744073709551615(2 ** 64-1) trên một số hệ thống.
Keith Thompson

1
Giá trị lớn nhất mà int unsign 16 bit có thể chứa là 65535, không phải 65536. Một sự khác biệt nhỏ nhưng quan trọng như 65536 giống như 0 trong int 16 unsign int.
Sie Raybould

1
@ gnasher729: Bạn có chắc về tiêu chuẩn C ++ không? Đã tìm kiếm một thời gian tôi cảm thấy rằng họ chỉ đơn giản là loại bỏ tất cả các đảm bảo tuyệt đối về phạm vi số nguyên (không bao gồm unsigned char). Tiêu chuẩn dường như không chứa chuỗi '65535' hoặc '65536' ở bất cứ đâu và '+32767' chỉ xảy ra (1.9: 9) trong một ghi chú là số nguyên lớn nhất có thể biểu thị trong int; không có sự đảm bảo nào được đưa ra thậm chí INT_MAXkhông thể nhỏ hơn thế!
Marc van Leeuwen

51

Loại size_t là loại được trả về bởi toán tử sizeof. Nó là một số nguyên không dấu có khả năng thể hiện kích thước theo byte của bất kỳ phạm vi bộ nhớ nào được hỗ trợ trên máy chủ. Nó (thường) liên quan đến ptrdiff_t trong đó ptrdiff_t là một giá trị số nguyên đã ký sao cho sizeof (ptrdiff_t) và sizeof (size_t) bằng nhau.

Khi viết mã C, bạn phải luôn sử dụng size_t bất cứ khi nào xử lý phạm vi bộ nhớ.

Mặt khác, kiểu int được định nghĩa cơ bản là kích thước của giá trị số nguyên (đã ký) mà máy chủ có thể sử dụng để thực hiện hiệu quả nhất số học số nguyên. Ví dụ, trên nhiều máy tính loại PC cũ, giá trị sizeof (size_t) sẽ là 4 (byte) nhưng sizeof (int) sẽ là 2 (byte). Số học 16 bit nhanh hơn số học 32 bit, mặc dù CPU có thể xử lý không gian bộ nhớ (logic) lên tới 4 GiB.

Chỉ sử dụng kiểu int khi bạn quan tâm đến hiệu quả vì độ chính xác thực tế của nó phụ thuộc mạnh mẽ vào cả tùy chọn trình biên dịch và kiến ​​trúc máy. Cụ thể, tiêu chuẩn C chỉ định các bất biến sau: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) không đặt ra giới hạn nào khác cho biểu diễn thực tế của độ chính xác có sẵn cho mỗi lập trình viên những kiểu nguyên thủy.

Lưu ý: Điều này KHÔNG giống như trong Java (thực tế chỉ định độ chính xác bit cho từng loại 'char', 'byte', 'short', 'int' và 'long').


định nghĩa thực tế của int là 16 bit trên 16 máy và 32 bit trên bất cứ thứ gì lớn hơn. Quá nhiều mã đã được viết mà giả sử rằng int rộng 32 bit, để thay đổi điều này ngay bây giờ và kết quả là mọi người phải luôn sử dụng size_t hoặc {, u} int {8,16,32,64} _t nếu họ muốn một cái gì đó cụ thể - - để phòng ngừa, mọi người chỉ nên sử dụng những thứ này, thay vì các loại số nguyên tích phân.
Rõ ràng hơn

3
"Đó là một số nguyên không dấu có khả năng thể hiện kích thước theo byte của bất kỳ phạm vi bộ nhớ nào được hỗ trợ trên máy chủ." -> Số size_tcó khả năng đại diện cho kích thước của bất kỳ đối tượng nào (ví dụ: số, mảng, cấu trúc). Toàn bộ phạm vi bộ nhớ có thể vượt quásize_t
chux - Phục hồi Monica

"Khi viết mã C, bạn phải luôn sử dụng size_t bất cứ khi nào xử lý phạm vi bộ nhớ." - điều đó ngụ ý rằng mọi chỉ mục cho mỗi mảng nên size_t- Tôi hy vọng bạn không có ý đó. Hầu hết thời gian chúng tôi không xử lý các mảng trong đó tính chính xác của không gian địa chỉ + tính di động thậm chí còn quan trọng. Trong những trường hợp này, bạn sẽ thực hiện size_t. Trong mọi trường hợp khác, bạn lấy các chỉ số ra khỏi số nguyên (đã ký). Bởi vì sự nhầm lẫn (không có cảnh báo) phát sinh từ hành vi ngầm không được xem xét của dấu không dấu là phổ biến hơn và tồi tệ hơn các vấn đề về tính di động có thể phát sinh trong các trường hợp khác.
johannes_lalala

23

Nhập size_t phải đủ lớn để lưu trữ kích thước của bất kỳ đối tượng nào có thể. Unsign int không phải đáp ứng điều kiện đó.

Ví dụ: trong hệ thống 64 bit int và unsign int có thể rộng 32 bit, nhưng size_t phải đủ lớn để lưu trữ số lớn hơn 4G


38
"đối tượng" là ngôn ngữ được sử dụng bởi tiêu chuẩn.
R .. GitHub DỪNG GIÚP ICE

2
Tôi nghĩ size_tsẽ chỉ phải lớn như vậy nếu trình biên dịch có thể chấp nhận loại X sao cho sizeof (X) sẽ mang lại giá trị lớn hơn 4G. Hầu hết các trình biên dịch sẽ từ chối typedef unsigned char foo[1000000000000LL][1000000000000LL], ví dụ , và thậm chí foo[65536][65536];có thể bị từ chối một cách hợp pháp nếu vượt quá giới hạn được xác định theo tài liệu.
supercat

1
@MattJoiner: Từ ngữ tốt. "Đối tượng" hoàn toàn không mơ hồ, nhưng được định nghĩa là "vùng lưu trữ".
Các cuộc đua nhẹ nhàng trong quỹ đạo

4

Đoạn trích từ hướng dẫn sử dụng glibc 0,02 cũng có thể có liên quan khi nghiên cứu chủ đề:

Có một vấn đề tiềm ẩn với loại size_t và các phiên bản của GCC trước khi phát hành 2.4. ANSI C yêu cầu size_t luôn là loại không dấu. Để tương thích với các tệp tiêu đề của hệ thống hiện tại, GCC định nghĩa size_t trong stddef.h' to be whatever type the system'ssys / type.h 'định nghĩa nó là. Hầu hết các hệ thống Unix định nghĩa size_t trong `sys / type.h ', định nghĩa nó là một kiểu đã ký. Một số mã trong thư viện phụ thuộc vào size_t là loại không dấu và sẽ không hoạt động chính xác nếu nó được ký.

Mã thư viện GNU C dự kiến ​​size_t không được ký là chính xác. Định nghĩa của size_t là một loại đã ký là không chính xác. Chúng tôi dự định trong phiên bản 2.4, GCC sẽ luôn xác định size_t là loại không dấu và fixincludes' script will massage the system'ssys / type.h 'để không xung đột với điều này.

Trong khi đó, chúng tôi giải quyết vấn đề này bằng cách thông báo rõ ràng cho GCC sử dụng loại không dấu cho size_t khi biên dịch thư viện GNU C. `configure 'sẽ tự động phát hiện loại GCC sử dụng cho size_t sắp xếp để ghi đè nó nếu cần thiết.


2

Nếu trình biên dịch của tôi được đặt thành 32 bit, size_tkhông có gì khác ngoài typedef cho unsigned int. Nếu trình biên dịch của tôi được đặt thành 64 bit, size_tkhông có gì khác ngoài typedef cho unsigned long long.


1
Có thể chỉ được định nghĩa như unsigned longcho cả hai trường hợp trên một số hệ điều hành.
StaceyGirl

-4

size_t là kích thước của một con trỏ.

Vì vậy, trong 32 bit hoặc mô hình IL_t32 (số nguyên, dài, con trỏ) chung size_t là 32 bit. và trong 64 bit hoặc mô hình LP_t (dài, con trỏ) phổ biến size_t là 64 bit (số nguyên vẫn là 32 bit).

Có những mô hình khác nhưng đây là những mô hình mà g ++ sử dụng (ít nhất là theo mặc định)


15
size_tkhông nhất thiết phải có cùng kích thước với một con trỏ, mặc dù nó thường là vậy. Một con trỏ phải có khả năng trỏ đến bất kỳ vị trí nào trong bộ nhớ; size_tchỉ phải đủ lớn để thể hiện kích thước của vật thể lớn nhất.
Keith Thompson
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.