2013-08-31 65 views
7

Có hợp pháp để truy cập loại con trỏ thông qua một void ** không?Có hợp pháp để sửa đổi bất kỳ con trỏ dữ liệu nào thông qua khoảng trống **

Tôi đã nhìn qua các tiêu chuẩn trích dẫn trên con trỏ răng cưa nhưng tôi vẫn không chắc chắn về việc liệu đây là hợp lệ trong C hay không:

int *array; 
void **vp = (void**)&array; 
*vp = malloc(sizeof(int)*10); 

dụ Trivial, nhưng nó áp dụng cho một tình huống phức tạp hơn tôi m thấy.

Dường như nó sẽ không hợp pháp vì tôi đang truy cập int * thông qua biến có loại không phải là int * hoặc char *. Tôi không thể đi đến một kết luận đơn giản về điều này.

liên quan:

+0

Bạn có nghĩa là gì 'int *' đang truy cập "mặc dù một loại"? – ash

+0

Eli Bendersky có bài đăng trên blog về chủ đề này: http://eli.thegreenplace.net/2009/11/16/void-and-casts-in-c-and-c/ –

+0

@MortenJensen dường như chỉ thảo luận về khoảng trống * và không void ** –

Trả lời

2

Pointer với các loại khác nhau có thể có kích thước khác nhau.

Bạn có thể lưu con trỏ vào bất kỳ loại nào thành void * và sau đó bạn có thể khôi phục lại nhưng điều này có nghĩa đơn giản là void * phải đủ lớn để giữ tất cả các con trỏ khác.

Xử lý một biến đang giữ int * giống như thực tế, thay vào đó, thực tế là không được phép sử dụng số void *.

Cũng lưu ý rằng làm một dàn diễn viên (ví dụ đúc để int * kết quả của malloc) là một cái gì đó hoàn toàn khác biệt so với điều trị một khu vực bộ nhớ chứa một int * như nó có chứa một void *. Trong trường hợp đầu tiên trình biên dịch được thông báo về việc chuyển đổi nếu cần thiết, trong lần thứ hai thay vào đó bạn đang cung cấp thông tin sai lệch cho trình biên dịch.

Trên X86 tuy nhiên chúng thường có cùng kích thước và bạn an toàn nếu bạn chỉ chơi với con trỏ tới dữ liệu (con trỏ đến hàm có thể khác nhau).

Về việc đánh răng bất kỳ thao tác ghi nào được thực hiện thông qua void * hoặc char * có thể làm biến đổi bất kỳ đối tượng nào để trình biên dịch phải xem xét răng cưa càng tốt. Tuy nhiên, trong ví dụ của bạn, bạn đang viết thông qua một số void ** (một điều khác) và trình biên dịch được tự do bỏ qua các hiệu ứng bí danh có khả năng là int *.

+0

Tôi không thực sự chắc chắn rằng kích thước con trỏ có bất cứ điều gì để làm với nó. AFAIK, 'void *' được đảm bảo để có thể giữ địa chỉ được trả về bởi bất kỳ lệnh gọi nào tới malloc(). Nếu bạn có thể lưu trữ sự trở lại của malloc() trong một 'int *' thì nó sẽ không thực sự quan trọng nếu bạn làm như vậy thông qua một 'void **' thay thế. Với tôi, nó trông giống như một sự đảm bảo rằng 'int *' và 'void *' có cùng kích thước. Nếu không một cái gì đó đơn giản như 'int * a = malloc (N);' sẽ là hành vi không xác định. –

+0

Điều đó mới đối với tôi - khi nào nó trở thành có thể cho hai con trỏ trong cùng một chương trình có kích thước khác nhau? – ash

+0

@ash Con trỏ hàm có thể có kích thước khác với 'void *', và giá trị trả về của malloc() không thể được lưu trữ trong một con trỏ hàm. –

4

No. void **loại cụ thể (trỏ đến con trỏ để vô hiệu). I E. loại con trỏ cơ bản là "con trỏ để vô hiệu"

Bạn không lưu trữ giá trị giống như con trỏ khi lưu trữ con trỏ đến int. Đó là một diễn viên được yêu cầu là một chỉ số mạnh mẽ những gì bạn đang làm không phải là được xác định hành vi theo tiêu chuẩn (và nó không phải là). Tuy nhiên, điều thú vị là bạn có thể có thể sử dụng số void* thông thường và sẽ hiển thị hành vi được xác định. Nói cách khác, điều này:

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    int *array; 
    void *vp = &array; 
    int **parray = vp; 
    *parray = malloc(sizeof(int)*10); 
} 

là hợp pháp.Ví dụ ban đầu của bạn thậm chí sẽ không biên dịch nếu tôi xóa dàn diễn viên và sử dụng táo llvm 4.2 (tiếng kêu), do chính xác đến các loại con trỏ không tương thích, tức là chủ đề của câu hỏi của bạn. Các lỗi cụ thể là:

"kiểu con trỏ không tương thích khởi 'khoảng trống **' với một biểu hiện của loại 'int **"

và đúng là như vậy.

0

Mã của bạn có thể hoạt động trên một số nền tảng nhưng không thể di động được. Lý do là C không có con trỏ chung cho kiểu con trỏ. Trong trường hợp của void * tiêu chuẩn cho phép rõ ràng chuyển đổi giữa nó và con trỏ khác để hoàn thành/không đầy đủ các loại, nhưng đây không phải là trường hợp với void **. Điều này có nghĩa là trong mã của bạn, trình biên dịch không có cách nào biết được giá trị của *vp đã được chuyển đổi từ bất kỳ loại nào khác ngoài void * và do đó không thể thực hiện bất kỳ chuyển đổi nào ngoại trừ chuyển đổi bạn tự cast.

xem xét mã này:

void dont_do_this(struct a_t **a, struct b_t **b) 
{ 
    void **x = (void **) a; 
    *x = *b; 
} 

Trình biên dịch sẽ không phàn nàn về diễn viên tiềm ẩn b_t *-void * trong dòng *x = *b, mặc dù dòng đó đang cố gắng đưa một con trỏ đến một b_t ở một nơi mà chỉ cần trỏ đến a_t. Sai lầm là trong thực tế trong dòng trước đó, đó là chuyển đổi "một con trỏ đến một nơi mà con trỏ đến a_t có thể được đặt" để "một con trỏ đến một nơi mà con trỏ đến bất cứ điều gì có thể được đặt". Đây là lý do không có diễn viên tiềm ẩn có thể. Để có ví dụ tương tự với con trỏ cho các loại số học, hãy xem C FAQ.

Tác vụ của bạn, sau đó, mặc dù nó tắt cảnh báo trình biên dịch, rất nguy hiểm vì không phải tất cả các loại con trỏ đều có cùng kích thước/biểu diễn bên trong (ví dụ: void **int *). Để làm cho mã của bạn hoạt động trong mọi trường hợp, bạn phải sử dụng một số trung gian void *:

int *array; 
void *varray = array; 
void **vp = &varray; 
*vp = malloc(sizeof(int) * 10);