Tại sao void trong C có nghĩa là không void?


25

Trong các ngôn ngữ được gõ mạnh như Java và C #, void(hoặc Void) làm kiểu trả về cho một phương thức dường như có nghĩa là:

Phương pháp này không trả lại bất cứ điều gì. Không có gì. Không trở lại. Bạn sẽ không nhận được bất cứ điều gì từ phương pháp này.

Điều thực sự kỳ lạ là trong C, voidnhư một kiểu trả về hoặc thậm chí là một kiểu tham số phương thức có nghĩa là:

Nó thực sự có thể là bất cứ điều gì. Bạn sẽ phải đọc mã nguồn để tìm hiểu. Chúc may mắn. Nếu đó là một con trỏ, bạn thực sự nên biết những gì bạn đang làm.

Hãy xem xét các ví dụ sau trong C:

void describe(void *thing)
{
    Object *obj = thing;
    printf("%s.\n", obj->description);
}

void *move(void *location, Direction direction)
{
    void *next = NULL;

    // logic!

    return next;
}

Rõ ràng, phương thức thứ hai trả về một con trỏ, theo định nghĩa có thể là bất cứ điều gì.

Vì C cũ hơn Java và C #, tại sao các ngôn ngữ này lại voidcó nghĩa là "không có gì" trong khi C sử dụng nó là "không có gì hoặc bất cứ điều gì (khi một con trỏ)"?


138
Tiêu đề câu hỏi và văn bản của bạn nói về voidtrong khi ví dụ mã sử dụng void*đó là một cái gì đó hoàn toàn khác nhau.

4
Cũng lưu ý rằng Java và C # có cùng một khái niệm, họ chỉ cần gọi nó Objecttrong một trường hợp để định hướng.
Vịt Mooing

4
@Mephy họ không gõ chính xác? Bằng cách sử dụng C # bằng cách gõ vịt, bạn có nghĩa là dynamicloại hiếm khi được sử dụng?
Axarydax

9
@Mephy: Lần trước tôi đã kiểm tra Wikipedia liệt kê mười một ý nghĩa mâu thuẫn lẫn nhau cho "đánh máy mạnh". Nói rằng "C # không được gõ mạnh" là vô nghĩa.
Eric Lippert

8
@LightnessRacesinOrbit: Không, vấn đề là không có định nghĩa chính thức của thuật ngữ này. Wikipedia không phải là một tài nguyên quy định cho bạn biết ý nghĩa đúng của thuật ngữ này là gì. Nó là một tài nguyên mô tả . Việc nó mô tả mười một ý nghĩa mâu thuẫn khác nhau là bằng chứng cho thấy thuật ngữ này không thể được sử dụng trong cuộc trò chuyện mà không xác định cẩn thận . Làm khác đi có nghĩa là tỷ lệ cược rất tốt, những người trong cuộc trò chuyện sẽ nói chuyện với nhau, không nói với nhau.
Eric Lippert

Câu trả lời:


58

Từ khóa void(không phải là một con trỏ) có nghĩa là "không có gì" trong các ngôn ngữ đó. Điều này là phù hợp.

Như bạn đã lưu ý, void*có nghĩa là "con trỏ tới bất cứ thứ gì" trong các ngôn ngữ hỗ trợ con trỏ thô (C và C ++). Đây là một quyết định không may vì như bạn đã đề cập, nó có voidnghĩa là hai điều khác nhau.

Tôi đã không thể tìm thấy lý do lịch sử đằng sau việc tái sử dụng voidcó nghĩa là "không có gì" và "bất cứ điều gì" trong các bối cảnh khác nhau, tuy nhiên C làm điều này ở một số nơi khác. Ví dụ, staticcó các mục đích khác nhau trong các bối cảnh khác nhau. Rõ ràng có tiền lệ trong ngôn ngữ C để sử dụng lại các từ khóa theo cách này, bất kể người ta có thể nghĩ gì về thực tiễn.

Java và C # đủ khác nhau để giải quyết vấn đề này. Java và "an toàn" C # cũng không cho phép các con trỏ thô và không cần khả năng tương thích C dễ dàng (Unsafe C # không cho phép các con trỏ nhưng phần lớn mã C # không thuộc loại này). Điều này cho phép họ thay đổi mọi thứ một chút mà không lo lắng về khả năng tương thích ngược. Một cách để làm điều này là giới thiệu một lớp Objectở gốc của hệ thống phân cấp mà tất cả các lớp kế thừa, do đó, một Objecttham chiếu phục vụ cùng một chức năng void*mà không có sự khó chịu của các vấn đề kiểu và quản lý bộ nhớ thô.


2
Tôi sẽ cung cấp tài liệu tham khảo khi tôi về nhà

4
They also do not allow raw pointers and do not need easy C compatibility.Sai. C # có con trỏ. Họ thường (và thường nên) tắt, nhưng họ ở đó.
Magus

8
Về lý do tại sao họ sử dụng lại void: một lý do rõ ràng sẽ là để tránh giới thiệu một từ khóa mới (và có khả năng phá vỡ các chương trình hiện có). Nếu họ giới thiệu một từ khóa đặc biệt (ví dụ unknown), thì đó void*sẽ là một cấu trúc vô nghĩa (và có thể là bất hợp pháp), và unknownsẽ chỉ hợp pháp dưới dạng unknown*.
jamesdlin

20
Ngay cả trong void *, voidcó nghĩa là "không có gì", không phải "bất cứ điều gì". Bạn không thể tham gia a void *, trước tiên bạn cần chuyển nó sang loại con trỏ khác.
oefe

2
@oefe Tôi nghĩ việc chia tóc đó là vô nghĩa, bởi vì voidvoid*là những kiểu khác nhau (thực ra, voidlà "không có loại"). Nó có ý nghĩa nhiều như nói " inttrong int*có nghĩa là một cái gì đó." Không, một khi bạn khai báo một biến con trỏ, bạn sẽ nói "đây là địa chỉ bộ nhớ có khả năng chứa thứ gì đó". Trong trường hợp void*, bạn đang nói "địa chỉ này chứa một cái gì đó nhưng cái gì đó không thể được suy ra từ loại con trỏ."

32

voidvoid*là hai điều khác nhau. voidtrong C có nghĩa chính xác giống như trong Java, không có giá trị trả về. A void*là một con trỏ không có kiểu.

Tất cả các con trỏ trong C cần phải được hủy đăng ký. Nếu bạn hủy đăng ký a void*, bạn sẽ nhận được loại nào? Hãy nhớ rằng con trỏ C không mang bất kỳ thông tin loại thời gian chạy nào, vì vậy loại này phải được biết tại thời điểm biên dịch.

Với bối cảnh đó, điều duy nhất bạn có thể làm một cách hợp lý với việc hủy đăng ký void*là bỏ qua nó, đó chính xác là hành vi mà voidloại biểu thị.


1
Tôi đồng ý cho đến khi bạn nhận được bit "bỏ qua". Có thể là thứ được trả lại chứa thông tin về cách diễn giải nó. Nói cách khác, nó có thể tương đương với C của Biến thể BASIC. "Khi bạn nhận được điều này, hãy xem để tìm ra nó là gì - tôi không thể nói cho bạn biết trước những gì bạn sẽ tìm thấy".
Floris

1
Hoặc nói một cách khác, theo giả thuyết tôi có thể đặt một "mô tả kiểu" do lập trình viên định nghĩa trong byte đầu tiên tại vị trí bộ nhớ được con trỏ xử lý, nó sẽ cho tôi biết loại dữ liệu nào tôi có thể tìm thấy trong các byte theo sau.
Robert Harvey

1
Tất nhiên, nhưng trong C, người ta đã quyết định để lại những chi tiết đó cho lập trình viên, thay vì nướng chúng vào ngôn ngữ. Từ quan điểm ngôn ngữ , nó không biết làm gì khác hơn là bỏ qua. Điều này tránh được chi phí luôn luôn bao gồm thông tin loại là byte đầu tiên khi trong hầu hết các trường hợp sử dụng, bạn có thể xác định nó một cách tĩnh.
Karl Bielefeldt

4
@RobertHarvey có, nhưng sau đó bạn không hủy bỏ một con trỏ trống; bạn đang chuyển con trỏ void sang một con trỏ char và sau đó hủy bỏ con trỏ char.
dùng253751

4
@Floris gcckhông đồng ý với bạn. Nếu bạn cố gắng sử dụng giá trị, hãy gcctạo một thông báo lỗi error: void value not ignored as it ought to be.
kasperd

19

Có lẽ sẽ hữu ích hơn khi nghĩ voidvề kiểu trả về. Sau đó, phương thức thứ hai của bạn sẽ đọc "một phương thức trả về một con trỏ chưa được gõ."


Những sự giúp đỡ đó. Là một người (cuối cùng!) Học C sau nhiều năm dành cho các ngôn ngữ hướng đối tượng, con trỏ là một khái niệm rất mới đối với tôi. Có vẻ như khi trả về một con trỏ, voidgần tương đương với *các ngôn ngữ như ActionScript 3 có nghĩa đơn giản là "bất kỳ loại trả về nào".
Naftuli Kay

5
Chà, voidkhông phải là một ký tự đại diện. Một con trỏ chỉ là một địa chỉ bộ nhớ. Đặt một loại trước khi *nói "đây là loại dữ liệu bạn có thể tìm thấy tại địa chỉ bộ nhớ được gọi bởi con trỏ này." Đặt voidtrước *lời nói với trình biên dịch "Tôi không biết loại; chỉ cần trả lại cho tôi con trỏ thô và tôi sẽ tìm ra phải làm gì với dữ liệu mà nó trỏ đến."
Robert Harvey

2
Tôi thậm chí sẽ nói rằng một void*điểm tại một "khoảng trống". Một con trỏ trần không có gì nó chỉ vào. Tuy nhiên, bạn có thể sử dụng nó như bất kỳ con trỏ khác.
Robert

11

Hãy thắt chặt thuật ngữ một chút.

Từ tiêu chuẩn C 2011 trực tuyến :

6.2.5 Loại
...
19 voidLoại bao gồm một tập hợp các giá trị trống; nó là một loại đối tượng không đầy đủ không thể hoàn thành.
...
6.3 Chuyển đổi
...
6.3.2.2 void

1 Giá trị (không tồn tại) của một voidbiểu thức (một biểu thức có loại void) sẽ không được sử dụng theo bất kỳ cách nào và các chuyển đổi ngầm định hoặc rõ ràng (ngoại trừ void) sẽ không được áp dụng cho một biểu hiện như vậy. Nếu một biểu thức thuộc bất kỳ loại nào khác được đánh giá là void biểu thức, giá trị hoặc chỉ định của nó sẽ bị loại bỏ. (Một voidbiểu thức được ước tính cho các tác dụng phụ của nó.)

6.3.2.3 Con trỏ

1 Con trỏ tớivoidcó thể được chuyển đổi thành hoặc từ một con trỏ sang bất kỳ loại đối tượng nào. Một con trỏ tới bất kỳ loại đối tượng nào có thể được chuyển đổi thành một con trỏ để bỏ trống và quay lại; kết quả sẽ so sánh bằng với con trỏ ban đầu.

Một voidbiểu thức không có giá trị (mặc dù nó có thể có tác dụng phụ). Nếu tôi có một hàm được xác định để trả về void, như vậy:

void foo( void ) { ... }

sau đó là cuộc gọi

foo();

không đánh giá một giá trị; Tôi không thể gán kết quả cho bất cứ điều gì, vì không có kết quả.

Một con trỏ về voidcơ bản là một loại con trỏ "chung"; bạn có thể gán giá trị void *cho bất kỳ loại con trỏ đối tượng nào khác mà không cần ép kiểu rõ ràng (đó là lý do tại sao tất cả các lập trình viên C sẽ la mắng bạn vì đã đưa ra kết quả malloc).

Bạn không thể trực tiếp tham gia a void *; trước tiên bạn phải gán nó cho một loại con trỏ đối tượng khác trước khi bạn có thể truy cập đối tượng trỏ vào.

voidcon trỏ được sử dụng để thực hiện các giao diện chung (nhiều hơn hoặc ít hơn); ví dụ chính tắc là qsorthàm thư viện, có thể sắp xếp các mảng thuộc bất kỳ loại nào, miễn là bạn cung cấp hàm so sánh nhận biết kiểu.

Đúng, sử dụng cùng một từ khóa cho hai khái niệm khác nhau (không có giá trị so với con trỏ chung) là khó hiểu, nhưng nó không giống như không có tiền lệ; staticcó nhiều ý nghĩa riêng biệt trong cả C và C ++.


9

Trong Java và C #, void là kiểu trả về cho một phương thức dường như có nghĩa là: phương thức này không trả về bất cứ thứ gì. Không có gì. Không trở lại. Bạn sẽ không nhận được bất cứ điều gì từ phương pháp này.

Câu nói đó là chính xác. Nó cũng đúng cho C và C ++.

trong C, void là kiểu trả về hoặc thậm chí là kiểu tham số phương thức có nghĩa là một cái gì đó khác nhau.

Câu nói đó không đúng. voidnhư một kiểu trả về trong C hoặc C ++ có nghĩa giống như trong C # và Java. Bạn đang bối rối voidvới void*. Chúng hoàn toàn khác nhau.

Không phải nó khó hiểu voidvoid*có nghĩa là hai điều hoàn toàn khác nhau sao?

Vâng.

Một con trỏ là gì? Một con trỏ trống là gì?

Một con trỏ là một giá trị có thể được dereferenced . Hủy bỏ hội nghị một con trỏ hợp lệ cung cấp một vị trí lưu trữ của kiểu trỏ . Một con trỏ void là một con trỏ không có kiểu trỏ cụ thể; nó phải được chuyển đổi thành một loại con trỏ cụ thể hơn trước khi giá trị được hủy đăng ký để tạo ra một vị trí lưu trữ .

Các con trỏ void có giống nhau trong C, C ++, Java và C # không?

Java không có con trỏ void; C # nào. Chúng giống như trong C và C ++ - một giá trị con trỏ không có bất kỳ loại cụ thể nào được liên kết với nó, phải được chuyển đổi thành một loại cụ thể hơn trước khi hủy bỏ nó để tạo ra một vị trí lưu trữ.

Vì C cũ hơn Java và C #, tại sao các ngôn ngữ này chấp nhận void có nghĩa là "không có gì" trong khi C sử dụng nó là "không có gì hoặc bất cứ điều gì (khi một con trỏ)"?

Câu hỏi không mạch lạc vì nó giả định trước sự giả dối. Hãy hỏi một số câu hỏi tốt hơn.

Tại sao Java và C # chấp nhận quy ước voidlà loại trả về hợp lệ, ví dụ, thay vì sử dụng quy ước Visual Basic mà các hàm không bao giờ bị trả về và các chương trình con luôn bị trả về?

Để làm quen với các lập trình viên đến với Java hoặc C # từ các ngôn ngữ có voidkiểu trả về.

Tại sao C # áp dụng quy ước khó hiểu void*có ý nghĩa hoàn toàn khác so với void?

Để làm quen với các lập trình viên đến với C # từ các ngôn ngữ có void*loại con trỏ.


5
void describe(void *thing);
void *move(void *location, Direction direction);

Hàm đầu tiên không trả về gì cả. Hàm thứ hai trả về một con trỏ rỗng. Tôi đã tuyên bố rằng chức năng thứ hai là

void* move(void* location, Direction direction);

Nếu có thể giúp nếu bạn nghĩ "void" là "không có nghĩa". Giá trị trả về describelà "không có nghĩa". Trả về một giá trị từ một hàm như vậy là bất hợp pháp vì bạn đã nói với trình biên dịch rằng giá trị trả về là vô nghĩa. Bạn không thể nắm bắt giá trị trả về từ hàm đó bởi vì nó không có nghĩa. Bạn không thể khai báo một biến kiểu voidvì nó không có nghĩa. Ví dụ void nonsense;là vô nghĩa và trên thực tế là bất hợp pháp. Cũng lưu ý rằng một mảng trống void void_array[42];cũng vô nghĩa.

Một con trỏ đến void ( void*) là một cái gì đó khác nhau. Nó là một con trỏ, không phải là một mảng. Đọc void*có nghĩa là một con trỏ trỏ đến một cái gì đó "không có nghĩa". Con trỏ là "không có nghĩa", điều đó có nghĩa là nó không có ý nghĩa đối với việc hủy bỏ con trỏ như vậy. Cố gắng làm như vậy là trên thực tế bất hợp pháp. Mã quy định một con trỏ trống sẽ không biên dịch.

Vì vậy, nếu bạn không thể hủy bỏ một con trỏ trống và bạn không thể tạo ra một loạt các khoảng trống, làm thế nào bạn có thể sử dụng chúng? Câu trả lời là một con trỏ tới bất kỳ loại nào có thể được chuyển đến và đi void*. Việc truyền một số con trỏ tới void*và chuyển trở lại một con trỏ về kiểu ban đầu sẽ mang lại một giá trị bằng với con trỏ ban đầu. Tiêu chuẩn C đảm bảo hành vi này. Lý do chính mà bạn thấy rất nhiều con trỏ void trong C là khả năng này cung cấp một cách để thực hiện ẩn thông tin và lập trình dựa trên đối tượng trong ngôn ngữ không hướng đối tượng.

Cuối cùng, lý do bạn thường thấy movekhai báo void *move(...)thay vì void* move(...)là vì đặt dấu hoa thị bên cạnh tên chứ không phải loại là một thực tiễn rất phổ biến ở C. Lý do là tuyên bố sau đây sẽ khiến bạn gặp rắc rối lớn: int* iptr, jptr;Điều này sẽ khiến bạn gặp rắc rối nghĩ rằng bạn đang khai báo hai con trỏ tới một int. Bạn không. Tuyên bố này làm cho jptrmột intthay vì một con trỏ đến một int. Khai báo đúng là int *iptr, *jptr;Bạn có thể giả vờ dấu hoa thị thuộc về loại nếu bạn tuân theo thông lệ chỉ khai báo một biến trên mỗi câu lệnh khai báo. Nhưng hãy nhớ rằng bạn đang giả vờ.


"Mã quy định một con trỏ null sẽ không biên dịch." Tôi nghĩ rằng bạn có nghĩa là mã mà các con trỏ trống sẽ không biên dịch. Trình biên dịch không bảo vệ bạn khỏi hội thảo một con trỏ null; đó là một vấn đề thời gian chạy.
Cody Grey

@CodyGray - Cảm ơn! Đã sửa lỗi đó. Mã mà hủy bỏ con trỏ null sẽ biên dịch (miễn là con trỏ không void* null_ptr = 0;).
David Hammen

2

Nói một cách đơn giản, không có sự khác biệt . Hãy để tôi cung cấp cho bạn một số ví dụ.

Nói rằng tôi có một lớp gọi là Xe.

Car obj = anotherCar; // obj is now of type Car
Car* obj = new Car(); // obj is now a pointer of type Car
void obj = 0; // obj has no type
void* = new Car(); // obj is a pointer with no type

Tôi tin ở đây sự nhầm lẫn ở đây dựa trên cách bạn sẽ xác định voidso với void*. Loại trả về voidcó nghĩa là "Tôi không trả lại gì", trong khi loại trả về void*có nghĩa là "Tôi trả về một con trỏ không có gì".

Điều này là do thực tế là một con trỏ là một trường hợp đặc biệt. Một đối tượng con trỏ sẽ luôn trỏ đến một vị trí trong bộ nhớ, cho dù có một đối tượng hợp lệ ở đó hay không. Cho dù void* cartài liệu tham khảo của tôi là byte, từ, Xe hơi hay xe đạp đều không thành vấn đề bởi vì tôi void*chỉ đến một thứ không có loại xác định. Tùy thuộc vào chúng ta để sau này xác định nó.

Trong các ngôn ngữ như C # và Java, bộ nhớ được quản lý và khái niệm con trỏ được đưa vào lớp Object. Thay vì có một tham chiếu đến một đối tượng thuộc loại "không có gì", chúng ta biết rằng ít nhất đối tượng của chúng ta thuộc loại "Đối tượng". Hãy để tôi giải thích tại sao.

Trong C / C ++ thông thường nếu bạn tạo một đối tượng và xóa con trỏ của nó, thì không thể có quyền truy cập vào đối tượng đó. Đây là những gì được gọi là rò rỉ bộ nhớ .

Trong C # / Java, mọi thứ đều là một đối tượng và điều này cho phép bộ thực thi theo dõi mọi đối tượng. Không nhất thiết phải biết rằng đối tượng của tôi là Xe hơi, đơn giản chỉ cần biết một số thông tin cơ bản đã được lớp Đối tượng quan tâm.

Đối với các lập trình viên của chúng tôi, điều đó có nghĩa là phần lớn thông tin chúng tôi sẽ phải tự chăm sóc trong C / C ++ được lớp Đối tượng chăm sóc cho chúng tôi, loại bỏ sự cần thiết cho trường hợp đặc biệt là con trỏ.


"khoảng trống * của tôi chỉ đến một cái gì đó không có loại xác định" - không chính xác. Sẽ đúng hơn khi nói đó là một con trỏ tới giá trị có độ dài bằng không (gần giống như một mảng có 0 phần tử, nhưng thậm chí không có thông tin loại được liên kết với mảng).
Scott Whitlock

2

Tôi sẽ cố gắng nói làm thế nào người ta có thể nghĩ về những điều này trong C. Ngôn ngữ chính thức trong tiêu chuẩn C không thực sự thoải mái voidvà tôi sẽ không cố gắng hoàn toàn phù hợp với nó.

Loại voidkhông phải là một công dân hạng nhất trong số các loại. Mặc dù nó là một loại đối tượng, nó không thể là loại của bất kỳ đối tượng (hoặc giá trị) nào, của một trường hoặc của một tham số hàm; tuy nhiên nó có thể là kiểu trả về của hàm và do đó là kiểu của biểu thức (về cơ bản là các cuộc gọi của các hàm đó, nhưng các biểu thức được hình thành với toán tử điều kiện cũng có thể có kiểu void). Nhưng ngay cả việc sử dụng tối thiểu voidnhư một loại giá trị mà các lá phía trên cánh cửa mở cho, cụ thể là kết thúc một hàm trả về voidvới một tuyên bố của mẫu return E;nơi Elà một biểu hiện của loại hình void, bị cấm một cách rõ ràng trong C (nó được cho phép trong C ++ mặc dù, nhưng vì lý do không áp dụng cho C).

Nếu các đối tượng loại voidđược cho phép, chúng sẽ có 0 bit (đó là lượng thông tin trong giá trị của biểu thức voidloại); nó sẽ không phải là một vấn đề lớn khi cho phép các đối tượng như vậy, nhưng chúng sẽ khá vô dụng (các đối tượng có kích thước 0 sẽ gây khó khăn cho việc xác định số học con trỏ, có lẽ tốt nhất là bị cấm). Tập hợp các giá trị khác nhau như vậy một đối tượng sẽ có một phần tử (tầm thường); giá trị của nó có thể được lấy, một cách tầm thường bởi vì nó không có bất kỳ chất nào, nhưng nó không thể được sửa đổi (thiếu bất kỳ giá trị khác nhau ). Do đó, tiêu chuẩn bị nhầm lẫn khi nói voidbao gồm một tập hợp các giá trị trống; một kiểu như vậy có thể hữu ích để mô tả các biểu thức không thểđược đánh giá (chẳng hạn như các bước nhảy hoặc các cuộc gọi không kết thúc) mặc dù ngôn ngữ C không thực sự sử dụng loại đó.

Không được phép là loại giá trị, voidđã được sử dụng theo ít nhất hai cách không liên quan trực tiếp để chỉ định "cấm": chỉ định (void)làm đặc tả tham số trong các loại hàm có nghĩa là cung cấp bất kỳ đối số nào cho chúng đều bị cấm (trong khi '()' trái với mọi thứ được cho phép và có, thậm chí cung cấp một voidbiểu thức làm đối số cho các hàm với (void)đặc tả tham số bị cấm) và khai báo void *pcó nghĩa là biểu thức hủy bỏ hội nghị *pbị cấm (nhưng không phải là biểu thức hợp lệ của loại void). Vì vậy, bạn đúng rằng những cách sử dụng voidnày không thực sự phù hợp voidnhư một loại biểu thức hợp lệ. Tuy nhiên một đối tượng của loạivoid*không thực sự là một con trỏ tới các giá trị thuộc bất kỳ loại nào, nó có nghĩa là một giá trị được coi là một con trỏ mặc dù nó không thể bị hủy đăng ký và số học con trỏ với nó bị cấm. "Được coi như một con trỏ" sau đó thực sự chỉ có nghĩa là nó có thể được truyền từ và tới bất kỳ loại con trỏ nào mà không mất thông tin. Tuy nhiên, nó cũng có thể được sử dụng để truyền các giá trị nguyên đến và đi, do đó nó không phải trỏ đến bất cứ thứ gì cả.


Nói một cách chính xác, nó có thể được truyền từ và tới bất kỳ loại con trỏ đối tượng nào mà không mất thông tin nhưng không phải lúc nào cũng đến và đi . Nói chung, việc truyền từ void*một loại con trỏ khác có thể bị mất thông tin (hoặc thậm chí có UB, tôi không thể nhớ tay), nhưng nếu giá trị của void*xuất phát từ việc tạo một con trỏ cùng loại mà bạn hiện đang truyền tới, thì nó không
Steve Jessop

0

Trong C # và Java, mọi lớp đều bắt nguồn từ một Objectlớp. Do đó bất cứ khi nào chúng tôi muốn chuyển một tham chiếu đến "một cái gì đó", chúng tôi có thể sử dụng một tham chiếu loại Object.

Vì chúng ta không cần sử dụng con trỏ void cho mục đích đó trong các ngôn ngữ này, voidnên không có nghĩa là "một cái gì đó hoặc không có gì" ở đây.


0

Trong C, có thể có một con trỏ tới những thứ thuộc bất kỳ loại nào [con trỏ hoạt động như một kiểu tham chiếu]. Một con trỏ có thể xác định một đối tượng của một loại đã biết hoặc nó có thể xác định một đối tượng của loại tùy ý (không xác định). Những người tạo ra C đã chọn sử dụng cùng một cú pháp chung cho "con trỏ tới vật tùy ý" như "con trỏ tới vật của một số loại cụ thể". Vì cú pháp trong trường hợp sau yêu cầu mã thông báo để xác định loại là gì, cú pháp trước đó yêu cầu đặt một cái gì đó mà mã thông báo đó sẽ thuộc về nếu loại được biết. C đã chọn sử dụng từ khóa "void" cho mục đích đó.

Trong Pascal, như với C, có thể có con trỏ tới mọi thứ thuộc bất kỳ loại nào; Các phương ngữ phổ biến hơn của Pascal cũng cho phép "con trỏ tới loại tùy ý", nhưng thay vì sử dụng cùng một cú pháp như chúng sử dụng cho "con trỏ tới một loại cụ thể", chúng chỉ đơn giản đề cập đến loại trước đây Pointer.

Trong Java, không có các loại biến do người dùng định nghĩa; tất cả mọi thứ là một tham chiếu đối tượng nguyên thủy hoặc heap. Người ta có thể định nghĩa những thứ thuộc loại "tham chiếu đến một đối tượng heap của một loại cụ thể" hoặc "tham chiếu đến một đối tượng heap thuộc loại tùy ý". Cái trước chỉ đơn giản sử dụng tên loại mà không có bất kỳ dấu câu đặc biệt nào để chỉ ra rằng đó là một tham chiếu (vì nó không thể là bất cứ thứ gì khác); cái sau sử dụng tên loại Object.

Việc sử dụng void*như danh pháp cho một con trỏ đến một loại tùy ý cho đến nay tôi biết là duy nhất cho C và các dẫn xuất trực tiếp như C ++; các ngôn ngữ khác mà tôi biết xử lý khái niệm này bằng cách sử dụng một loại có tên riêng biệt.


-1

Bạn có thể nghĩ từ khóa voidgiống như một khoảng trống struct( Trong C99, các cấu trúc trống dường như không được phép , trong .NET và C ++, chúng chiếm hơn 0 bộ nhớ và cuối cùng tôi nhớ lại mọi thứ trong Java là một lớp):

struct void {
};

... có nghĩa là nó là một loại có độ dài 0. Đây là cách nó hoạt động khi bạn thực hiện một lệnh gọi hàm trả về void... thay vì phân bổ 4 byte hoặc hơn cho ngăn xếp cho giá trị trả về số nguyên, nó hoàn toàn không thay đổi con trỏ ngăn xếp (tăng thêm 0 độ dài ).

Vì vậy, nếu bạn đã khai báo một số biến như:

int a;
void b;
int c;

... Và sau đó bạn lấy địa chỉ của các biến đó, sau đó có lẽ bccó thể được đặt tại cùng một vị trí trong bộ nhớ, vì bcó độ dài bằng không. Vì vậy, a void*là một con trỏ trong bộ nhớ cho loại tích hợp có độ dài bằng không. Thực tế là bạn có thể biết rằng có một cái gì đó ngay sau nó có thể hữu ích cho bạn là một vấn đề khác và đòi hỏi bạn phải thực sự biết những gì bạn đang làm (và nguy hiểm).


Trình biên dịch duy nhất mà tôi biết có gán độ dài cho kiểu void là gcc và gcc gán cho nó độ dài là 1.
Joshua
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.