2010-03-16 33 views
10

Mặc dù có thể viết mã chung trong C bằng cách sử dụng con trỏ void (con trỏ chung), tôi thấy rằng khá khó gỡ lỗi mã vì con trỏ void có thể lấy bất kỳ loại con trỏ nào mà không cảnh báo từ trình biên dịch. (ví dụ: function foo() lấy con trỏ void được cho là con trỏ để cấu trúc, nhưng trình biên dịch sẽ không phàn nàn nếu mảng char được thông qua.) Loại phương pháp/chiến lược nào bạn sử dụng khi sử dụng con trỏ void trong C?Lập trình chung trong C với con trỏ rỗng

+4

Nếu đây là một câu hỏi duy nhất C loại bỏ các C + + tag –

+13

Hmmm ... "Khi tôi tấn công ngón tay của tôi với một cây búa nó đau thời gian lớn. Cách tiếp cận nào bạn sử dụng khi nổi bật các bộ phận của cơ thể của bạn với một cái búa?" – sharptooth

+2

Nếu bạn nghĩ rằng lập trình C chung với void * có thể thanh lịch/đẹp để sử dụng, hãy nhìn vào những gì bạn phải làm để sử dụng chức năng qsort ... –

Trả lời

8

Giải pháp không được sử dụng void* trừ khi bạn thực sự, thực sự phải. Những nơi mà một con trỏ void thực sự được yêu cầu là rất nhỏ: các tham số cho các hàm luồng, và một số ít những nơi khác mà bạn cần truyền dữ liệu cụ thể cho việc triển khai thông qua một hàm chung. Trong mọi trường hợp, mã chấp nhận tham số void* chỉ nên chấp nhận một loại dữ liệu được truyền qua con trỏ trống và loại phải được ghi lại trong nhận xét và được mọi người gọi tuân thủ một cách chậm chạp.

+4

Chương trình chung đặc biệt được đề cập trong OP, đây là trường hợp sử dụng khá tốt để trống * (nếu không có các cơ sở ngôn ngữ cấp cao hơn như mẫu hoặc macro đẹp.) –

+0

Ý tôi là, ví dụ , qsort() và bsearch() –

+0

@Matt, qsort và bsearch là ví dụ về "dữ liệu thực hiện cụ thể thông qua chức năng chung". –

4

này có thể giúp:

comp.lang.c FAQ list · Question 4.9

Q: Giả sử tôi muốn viết một hàm mang theo một con trỏ chung như một cuộc tranh cãi và tôi muốn để mô phỏng đi qua nó bằng cách tham khảo. Tôi có thể cung cấp loại thông số chính thức void ** và thực hiện điều gì đó như thế này không?

void f(void **); 
double *dp; 
f((void **)&dp); 

A: Không ổn định. Mã như thế này có thể làm việc và đôi khi được khuyến khích, nhưng nó dựa trên tất cả các kiểu con trỏ có cùng biểu diễn bên trong (phổ biến, nhưng không phổ biến; xem câu hỏi 5.17).

Không có kiểu con trỏ trỏ tới con trỏ trong C. void * hoạt động như một con trỏ chung chỉ vì chuyển đổi (nếu cần) được áp dụng tự động khi các kiểu con trỏ khác được gán và từ void * 's; những chuyển đổi này không thể được thực hiện nếu một nỗ lực được thực hiện gián tiếp khi một giá trị void ** trỏ vào một loại con trỏ khác với void *. Khi bạn sử dụng giá trị con trỏ void ** (ví dụ, khi bạn sử dụng toán tử * để truy cập giá trị void * mà void ** points), trình biên dịch không có cách nào biết liệu giá trị void * đó đã từng được chuyển đổi từ một số loại con trỏ khác. Nó phải giả định rằng nó không là gì hơn là một khoảng trống *; nó không thể thực hiện bất kỳ chuyển đổi tiềm ẩn nào.

Nói cách khác, bất kỳ giá trị void ** nào bạn chơi phải là địa chỉ của giá trị void * thực tế ở đâu đó; phôi như (void **) & dp, mặc dù chúng có thể đóng trình biên dịch lên, không thể di chuyển (và thậm chí không thể làm những gì bạn muốn; xem thêm câu hỏi 13.9). Nếu con trỏ mà khoảng trống ** trỏ tới không phải là một khoảng trống *, và nếu nó có kích thước hoặc biểu diễn khác với khoảng trống *, thì trình biên dịch sẽ không thể truy cập nó một cách chính xác.

Để thực hiện các đoạn mã trên làm việc, bạn sẽ phải sử dụng một khoảng trống trung gian * biến:

double *dp; 
void *vp = dp; 
f(&vp); 
dp = vp; 

Các bài tập đến và đi từ vp cho trình biên dịch có cơ hội để thực hiện bất kỳ chuyển đổi, nếu cần thiết.

Một lần nữa, cuộc thảo luận cho đến nay giả định rằng các kiểu con trỏ khác nhau có thể có các kích thước hoặc biểu diễn khác nhau, rất hiếm, nhưng không phải là chưa từng nghe thấy. Để đánh giá cao vấn đề với void ** rõ ràng hơn, so sánh tình huống với một cái tương tự liên quan đến, ví dụ, int và double, có thể có kích thước khác nhau và chắc chắn có các biểu diễn khác nhau. Nếu chúng ta có một hàm

void incme(double *p) 
{ 
    *p += 1; 
} 

sau đó chúng ta có thể làm điều gì đó như

int i = 1; 
double d = i; 
incme(&d); 
i = d; 

và tôi sẽ được tăng thêm 1. (Điều này giống như khoảng trống đúng ** đang liên quan đến vp phụ trợ.) Nếu, mặt khác, chúng tôi đã thử một số nội dung như

int i = 1; 
incme((double *)&i); /* WRONG */ 

(mã này tương tự với đoạn trong câu hỏi), rất khó có thể hoạt động.

-2

Chúng ta đều biết rằng hệ thống kiểu C về cơ bản là crap, nhưng cố gắng không làm điều đó ... Bạn vẫn có một số tùy chọn để xử lý các loại chung: công đoàn và con trỏ mờ.

Dù sao, nếu một hàm chung dùng một con trỏ void làm tham số, nó không nên cố gắng coi trọng nó !.

1

Cách tiếp cận/chiến lược là giảm thiểu sử dụng con trỏ void *. Họ là cần thiết trong trường hợp cụ thể. Nếu bạn thực sự cần phải vượt qua void * bạn nên vượt qua kích thước của mục tiêu của con trỏ cũng có.

0

chức năng trao đổi chung chung này sẽ giúp bạn rất nhiều trong việc tìm hiểu khoảng trống chung *

#include<stdio.h> 
void swap(void *vp1,void *vp2,int size) 
{ 
     char buf[100]; 
     memcpy(buf,vp1,size); 
     memcpy(vp1,vp2,size); 
     memcpy(vp2,buf,size); //memcpy ->inbuilt function in std-c 
} 

int main() 
{ 
     int a=2,b=3; 
     float d=5,e=7; 
     swap(&a,&b,sizeof(int)); 
     swap(&d,&e,sizeof(float)); 
     printf("%d %d %.0f %.0f\n",a,b,d,e); 
return 0; 
} 
+2

Điều gì sẽ xảy ra nếu kích thước> 100? –

2

giải pháp Arya có thể được thay đổi một chút để hỗ trợ kích thước biến:

#include <stdio.h> 
#include <string.h> 

void swap(void *vp1,void *vp2,int size) 
{ 
    char buf[size]; 
    memcpy(buf,vp1,size); 
    memcpy(vp1,vp2,size); 
    memcpy(vp2,buf,size); //memcpy ->inbuilt function in std-c 
} 

int main() 
{ 
    int array1[] = {1, 2, 3}; 
    int array2[] = {10, 20, 30}; 
    swap(array1, array2, 3 * sizeof(int)); 

    int i; 
    printf("array1: "); 
    for (i = 0; i < 3; i++) 
    printf(" %d", array1[i]); 
    printf("\n"); 

    printf("array2: "); 
    for (i = 0; i < 3; i++) 
    printf(" %d", array2[i]); 
    printf("\n"); 

    return 0; 
}