Một con trỏ tới void
là một loại con trỏ "chung". A void *
có thể được chuyển đổi thành bất kỳ loại con trỏ nào khác mà không cần đúc rõ ràng. Bạn không thể hủy đăng ký a void *
hoặc làm số học con trỏ với nó; trước tiên bạn phải chuyển đổi nó thành một con trỏ thành kiểu dữ liệu hoàn chỉnh.
void *
thường được sử dụng ở những nơi bạn cần để có thể làm việc với các loại con trỏ khác nhau trong cùng một mã. Một ví dụ thường được trích dẫn là chức năng thư viện qsort
:
void qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
base
là địa chỉ của một mảng, nmemb
là số phần tử trong mảng, size
là kích thước của mỗi phần tử và compar
là con trỏ tới một hàm so sánh hai phần tử của mảng. Nó được gọi như vậy:
int iArr[10];
double dArr[30];
long lArr[50];
...
qsort(iArr, sizeof iArr/sizeof iArr[0], sizeof iArr[0], compareInt);
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareDouble);
qsort(lArr, sizeof lArr/sizeof lArr[0], sizeof lArr[0], compareLong);
Các biểu thức mảng iArr
, dArr
và lArr
được mặc nhiên chuyển đổi từ các loại mảng với các loại con trỏ trong cuộc gọi chức năng, và mỗi được ngầm chuyển đổi từ "con trỏ đến int
/ double
/ long
" thành "con trỏ tới void
".
Các chức năng so sánh sẽ trông giống như:
int compareInt(const void *lhs, const void *rhs)
{
const int *x = lhs; // convert void * to int * by assignment
const int *y = rhs;
if (*x > *y) return 1;
if (*x == *y) return 0;
return -1;
}
Bằng cách chấp nhận void *
, qsort
có thể làm việc với các mảng thuộc bất kỳ loại nào.
Nhược điểm của việc sử dụng void *
là bạn ném loại an toàn ra khỏi cửa sổ và tham gia giao thông. Không có gì để bảo vệ bạn khỏi việc sử dụng thói quen so sánh sai:
qsort(dArr, sizeof dArr/sizeof dArr[0], sizeof dArr[0], compareInt);
compareInt
đang mong đợi các đối số của nó được trỏ đến int
s, nhưng thực sự đang làm việc với double
s. Không có cách nào để bắt vấn đề này vào thời gian biên dịch; bạn sẽ kết thúc với một mảng sai.