Kích thước của khoảng trống là gì?


77

Câu lệnh này sẽ mang lại kết quả gì?

void *p = malloc(sizeof(void));

Chỉnh sửa: Phần mở rộng cho câu hỏi.

Nếu sizeof (void) mang lại 1 trong trình biên dịch GCC, thì 1 byte bộ nhớ được cấp phát và con trỏ p trỏ đến byte đó và p ++ có được tăng lên 0x2346 không? Giả sử p là 0x2345. Tôi đang nói về p chứ không phải * p.


4
FYI. Đây không phải là tiêu chuẩn C. Trong nhiều trình biên dịch, đây sẽ là lỗi thời gian biên dịch.
Johan Kotlinski

tại sao bạn cần phải biết. Hiểu được điều này có thể giúp chúng tôi trả lời câu hỏi.
Martin York

4
Nhận xét về chỉnh sửa của bạn: vâng, trong GCC (chỉ), việc tăng con trỏ void sẽ thêm một con trỏ vào giá trị. Nếu bạn coi trọng tính di động của mã, đừng lạm dụng quyền tự do mà GCC cung cấp cho bạn. Nó hoàn toàn không theo tiêu chuẩn. Và GCC cũng thừa nhận nhiều như vậy với '-std = c99 -pedantic'.
Jonathan Leffler

1
Đề xuất Void thông thường: open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0146r1.html
alfC

Câu trả lời:


56

Loại voidkhông có kích thước; đó sẽ là một lỗi biên dịch. Vì lý do tương tự, bạn không thể làm điều gì đó như:

void n;

BIÊN TẬP. Trước sự ngạc nhiên của tôi, sizeof(void)thực sự làm biên dịch trong GNU C:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc -w - && ./a.out 
1

Tuy nhiên, trong C ++ nó không:

$ echo 'int main() { printf("%d", sizeof(void)); }' | gcc -xc++ -w - && ./a.out 
<stdin>: In function 'int main()':
<stdin>:1: error: invalid application of 'sizeof' to a void type
<stdin>:1: error: 'printf' was not declared in this scope

31
Nhưng GCC có một tùy chọn để xử lý 'sizeof (void) == sizeof (char)' ... Xem -Wpointer-arith
Jonathan Leffler

5
@Jon này dành cho những kẻ ngu từ đầu những năm 80, những người mới bắt đầu học cách lập trình. Các tiêu chuẩn không được quyết định rộng rãi.
unixman83,

5
sizeof (void)là một vi phạm ràng buộc. Trình biên dịch C phù hợp (mà gcc không phải là theo mặc định) ít nhất phải cảnh báo về nó và có thể (và IMHO) nên từ chối nó. Tham chiếu: N1570 6.5.3.4p1 ( voidlà loại chưa hoàn thiện). Đó là cách này kể từ tiêu chuẩn năm 1989, được giới thiệu void.
Keith Thompson

3
@ unixman83: Điều đó không có ý nghĩa. voidkhông tồn tại trước khi nó được giới thiệu bởi tiêu chuẩn ANSI C năm 1989, cùng một tiêu chuẩn đã sizeof (void)vi phạm ràng buộc.
Keith Thompson

1
Sizeof void được biên dịch vì bạn cần nó hoạt động với con trỏ void *.
kelin

41

Nếu bạn đang sử dụng GCC và bạn không sử dụng cờ biên dịch loại bỏ các phần mở rộng cụ thể của trình biên dịch, thì đó sizeof(void)là 1. GCC có một phần mở rộng không chuẩn thực hiện điều đó.

Nói chung, voidlà một loại không hoàn chỉnh, và bạn không thể sử dụng sizeof cho các loại không hoàn chỉnh.


2
Tùy chọn -Wpointer-arithđược ngụ ý bởi -pedantic. Tôi luôn luôn sử dụng -pedantic. :)
Josh Lee

1
@jleedev Thật vậy, tôi cũng sử dụng -pedantic nhiều nhất có thể. Tuy nhiên, thật không may, một số tiêu đề thư viện của bên thứ ba làm cho gcc -pedantic rất buồn :(
hrnt 11/11/09

1
+1 Thực sự, tại sao GCC có hàng nghìn tiện ích mở rộng vô dụng?

@ user142019: vì chúng hữu ích? Và giúp có được một mô hình phụ trợ nhất quán thông qua các ngôn ngữ lập trình và kiến ​​trúc phần cứng khác nhau.
kriss

16

Mặc dù voidcó thể thay thế cho một kiểu, nhưng nó không thể thực sự giữ một giá trị. Do đó, nó không có kích thước trong bộ nhớ. Nhận kích thước của một voidkhông được xác định.

Con void trỏ chỉ đơn giản là một cấu trúc ngôn ngữ có nghĩa là một con trỏ tới bộ nhớ không định kiểu .


1
Vậy thì sao ? Không nên bất kỳ cấu trúc trống nào cũng không tiêu tốn kích thước trong bộ nhớ và đó sẽ là một vấn đề. Thực sự thì tôi tự hỏi, nếu các cấu trúc trống thực sự đang sử dụng 0 byte trong C ++ (như đúng trong C) thì có vẻ như sizeof trả về 1 cho chúng.
kriss

1
@kriss Tôi không chắc chắn những gì bạn đang nói nhưng khi bạn đã nhận thấy chính mình, cấu trúc rỗng làm chiếm bộ nhớ cho lý do của nhận dạng đối tượng.
Konrad Rudolph

1
thực sự, nhưng nó không đúng trong C và việc sử dụng bộ nhớ cho các đối tượng trống cảm thấy xấu xa.
kriss

13

voidkhông có kích thước. Trong cả C và C ++, biểu thức sizeof (void)không hợp lệ.

Trong C, trích dẫn N1570 6.5.3.4 đoạn 1:

Các sizeofnhà điều hành sẽ không được áp dụng cho một biểu thức có kiểu chức năng hoặc một loại không đầy đủ , với tên trong ngoặc của một loại như vậy, hoặc một biểu thức chỉ định một thành viên bit lĩnh vực.

(N1570 là bản dự thảo của tiêu chuẩn ISO C năm 2011.)

voidlà một loại không hoàn chỉnh. Đoạn này là một ràng buộc , có nghĩa là bất kỳ trình biên dịch C tuân thủ nào đều phải chẩn đoán bất kỳ vi phạm nào đối với nó. (Thông báo chẩn đoán có thể là một cảnh báo không nghiêm trọng.)

Chuẩn C ++ 11 có cách diễn đạt rất giống nhau. Cả hai ấn bản đều được xuất bản sau khi câu hỏi này được hỏi, nhưng các quy tắc quay trở lại tiêu chuẩn ANSI C năm 1989 và các tiêu chuẩn C ++ sớm nhất. Trên thực tế, quy tắc voidlà một loại không hoàn chỉnh sizeofcó thể không được áp dụng trở lại chính xác khi giới thiệu voidngôn ngữ.

gcc có một phần mở rộng được coi sizeof (void)là 1. gcc không phải là một trình biên dịch C tuân thủ theo mặc định, vì vậy ở chế độ mặc định, nó không cảnh báo về điều đó sizeof (void). Các tiện ích mở rộng như thế này được phép ngay cả đối với các trình biên dịch C hoàn toàn phù hợp, nhưng chẩn đoán vẫn được yêu cầu.


Tôi đoán rằng họ đã cho phép tính sizeof(void) == 1nhất quán với số học voidcon trỏ trên con trỏ, đó cũng là một phần mở rộng gcc . Trích dẫn từ tài liệu của họ: A consequence of this is that sizeof is also allowed on void and on function types, and returns 1.. Nhưng thông báo chẩn đoán vẫn phải hiển thị theo mặc định cho C vì Tiêu chuẩn yêu cầu nó.
Grzegorz Szpetkowski

2
@GrzegorzSzpetkowski: (Trả lời một vài năm sau.) Có, tiêu chuẩn yêu cầu chẩn đoán, nhưng gcc không phải là trình biên dịch C phù hợp theo mặc định. Về mặt logic, tiêu chuẩn C không áp đặt yêu cầu nào đối với việc triển khai không yêu cầu tuân theo tiêu chuẩn đó. gcc có thể được thực hiện để (rất gần) tuân theo các tùy chọn dòng lệnh phù hợp, chẳng hạn như -std=c11 -pedantic. Với các tùy chọn như vậy, nó đưa ra chẩn đoán cần thiết.
Keith Thompson


6

sizeof()không thể áp dụng cho các loại không hoàn chỉnh. Và voidlà loại không hoàn chỉnh không thể hoàn thành.


3

Trong C, sizeof(void) == 1trong GCC, nhưng điều này dường như phụ thuộc vào trình biên dịch của bạn.

Trong C ++, tôi nhận được:

Trong hàm 'int main ()':
Dòng 2: lỗi: ứng dụng không hợp lệ của 'sizeof' cho một loại void
quá trình biên dịch bị chấm dứt do lỗi -Wfatal-.

3
Chỉ trong GCC là sizeof (void) == 1; trong tiêu chuẩn, đó là hành vi không xác định.
Jonathan Leffler

Đã cập nhật để thêm ghi chú cụ thể của GCC.
Greg Hewgill

2
@JonathanLeffler: Đó không chỉ là hành vi không xác định. Đó là một vi phạm ràng buộc. N1570 6.5.3.4p1.
Keith Thompson

Nó không chỉ GCC, nó là GCC và bất kỳ trình biên dịch nào khác được thiết kế để tương thích với nó (bao gồm clang, icc, tcc và có thể là những trình biên dịch khác).
Keith Thompson

0

Ở phần thứ 2 của câu hỏi: Lưu ý rằng sizeof (void *)! = Sizeof (void). Trên cung 32-bit, sizeof (void *) là 4 byte, do đó p ++, sẽ được đặt tương ứng. Số lượng mà một con trỏ được tăng lên phụ thuộc vào dữ liệu mà nó đang trỏ tới. Vì vậy, nó sẽ được tăng thêm 1 byte.


1
vì vậy p đã trỏ đến dữ liệu 1 byte vì sizeof (void) cho 1 nên p ++ có nghĩa là p sẽ tăng lên 1 chứ không phải 4 ??
Lakshmi

1
Số lượng con trỏ được tăng lên phụ thuộc vào dữ liệu mà nó trỏ tới. Vì vậy, Vâng.

p là kiểu void * (void pointer), không void. Vì vậy, nó sẽ được tăng lên bởi kích thước của kiểu void * (thường là 4 trong hệ thống 32 bit).
Technowise

4
@Technowise: không. Bất kỳ con trỏ kiểu nào T*cũng được tăng dần bởi sizeof(T)(và không sizeof(T*) , hầu như luôn giống nhau, có lẽ ngoại trừ con trỏ hàm) khi nó được tăng lên. Tất nhiên là ngoại lệ void(và một lần nữa, ngoại lệ là khi bật tiện ích mở rộng GCC).
Konrad Rudolph

1
@Amit: Làm thế nào bạn kết luận rằng kích thước của dữ liệu a void* con trỏ trỏ đến là 1 byte? sizeof (void)là một vi phạm ràng buộc. (Phần mở rộng gcc coi nó là 1)
Keith Thompson

-1

trong khi sizeof (void) có lẽ không có ý nghĩa gì, nhưng điều quan trọng là khi bạn thực hiện bất kỳ phép toán con trỏ nào.

ví dụ.

 void *p;
 while(...)
      p++;

Nếu sizeof (void) được coi là 1 thì điều này sẽ hoạt động. Nếu sizeof (void) được coi là 0 thì bạn đạt một vòng lặp vô hạn.


4
Đoạn mã ví dụ không hợp lệ trong C và C ++. Một số trình biên dịch vẫn cho phép nó.
James Roth

1
Không, nó không quan trọng chút nào (mặc dù các tác giả của gcc dường như nghĩ rằng nó là như vậy). Nếu bạn muốn thực hiện số học con trỏ trên con trỏ byte, hãy sử dụng unsigned char*.
Keith Thompson

@KeithThompson - thậm chí charkhông được đảm bảo là 1 byte. Tôi đã làm việc trên một hệ thống đặt nó thành 32 bit.
ash

@ash: Có, charđược đảm bảo là 1 byte. Nếu bạn đã làm việc trên một hệ thống với 32 bit char, thì một byte trên hệ thống đó là 32 bit ( CHAR_BIT == 32). Đó là cách tiêu chuẩn C định nghĩa "byte". C chuẩn, 6.5.3.4 khoản 2 và 4: "Các sizeofnhà điều hành mang lại kích thước (tính theo byte) của toán hạng của nó [...] Khi. sizeofĐược áp dụng cho một toán hạng có kiểu char, unsigned charhoặc signed char, (hoặc một phiên bản chất lượng của chúng) kết quả là 1. "
Keith Thompson

Tiêu chuẩn C 6.5.3.4 ra đời khi nào? Tôi đã làm việc trên hệ thống này vào giữa những năm 90.
ash

-1

Hầu hết các trình biên dịch C ++ được lựa chọn để gây ra lỗi biên dịch khi cố gắng lấy sizeof(void).

Khi biên dịch C, gcc không phù hợp và được chọn định nghĩa sizeof(void)là 1. Nó có thể trông lạ, nhưng có cơ sở. Khi bạn thực hiện số học con trỏ thêm hoặc bớt một đơn vị có nghĩa là thêm hoặc bớt đối tượng được trỏ tới kích thước. Do đó, xác định sizeof(void)là 1 giúp xác địnhvoid* như một con trỏ tới byte (địa chỉ bộ nhớ không định kiểu). Nếu không, bạn sẽ có những hành vi đáng ngạc nhiên khi sử dụng số học con trỏ như p+1 == p whenp void*. Số học con trỏ như vậy trên con trỏ void không được phép trong c ++ nhưng hoạt động tốt khi biên dịch C với gcc.

Cách tiêu chuẩn được đề xuất sẽ là sử dụng char*cho loại mục đích đó (con trỏ đến byte).

Một sự khác biệt tương tự khác giữa C và C ++ khi sử dụng sizeof xảy ra khi bạn xác định một cấu trúc trống như:

struct Empty {
} empty;

Sử dụng gcc làm trình biên dịch C của tôi sizeof(empty) trả về 0. Sử dụng g ++ cùng một mã sẽ trả về 1.

Tôi không chắc điều gì đã nêu cả tiêu chuẩn C và C ++ về điểm này, nhưng tôi tin rằng việc xác định kích thước của một số cấu trúc / đối tượng trống sẽ giúp quản lý tham chiếu để tránh hai tham chiếu đến các đối tượng liên tiếp khác nhau, đối tượng đầu tiên trống, hãy lấy cùng địa chỉ. Nếu tham chiếu được triển khai bằng cách sử dụng con trỏ ẩn như nó thường được thực hiện, việc đảm bảo địa chỉ khác nhau sẽ giúp so sánh chúng.

Nhưng điều này chỉ đơn thuần là tránh một hành vi đáng ngạc nhiên (so sánh trường hợp góc của các tham chiếu) bằng cách đưa vào một cái khác (các đối tượng trống, thậm chí các POD sử dụng bộ nhớ ít nhất 1 byte).


6
“Thay vào đó, C đã chọn xác định sizeof (void) là 1.” - Không có đó là sai. sizeof(void)không được định nghĩa trong C, giống như trong C ++. GCC đơn giản là không phù hợp ở điểm này và số học con trỏ trên voidcon trỏ đơn giản là không được định nghĩa - void* p; p++;không hợp lệ. Ngay cả GCC cũng sẽ đưa ra cảnh báo cho bạn khi biên dịch với -pedanticcờ. Và bạn cũng sai về hành vi mặc định của GCC cho C ++: theo mặc định, điều này được coi là lỗi .
Konrad Rudolph

@Konrad: vâng, tôi khá chắc điều đó không đúng với phiên bản cũ hơn của gcc, nhưng những phiên bản cập nhật thì đúng như bạn nói.
kriss

Ít nhất trong C, một structđịnh nghĩa trống là một lỗi cú pháp; hỗ trợ của gcc là một phần mở rộng. Chúng có thể hợp lệ trong C ++; Tôi không chắc.
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.