2009-11-21 40 views
8

Xin chào, tôi đang làm bài tập ở C, trong đó tôi cần chuyển một kiểu tham số không xác định vào một hàm.Vượt qua tham số kiểu Void trong C

Ví dụ giả sử tôi có như sau:

int changeCount(void* element) 
{ 
    element.Count = element.Count++; 

    return 1; 

} 

Lý do tại sao yếu tố biến là khoảng trống là bởi vì có 3 loại khả năng. Tất cả 3 tuy nhiên có một biến thành viên có tên là "Đếm".

Khi tôi cố gắng để biên dịch mã thực tế tôi đã viết trong Eclipese, tôi nhận được lỗi sau:

error: request for member ‘Count’ in something not a structure or union

Tôi đoán điều này xảy ra bởi vì trình biên dịch không biết loại "yếu tố" trước tay. Tuy nhiên tôi không thấy lý do tại sao điều này không hoạt động.

Cảm ơn bạn đã trợ giúp!

+1

Tại sao không chỉ viết 'element.Count ++' thay vì gọi hàm? – LnxPrgr3

+4

Không liên quan trực tiếp đến câu hỏi, nhưng hành vi của 'element.Count = element.Count ++;' là không xác định. Vì mục đích đơn giản, tôi sẽ tuyên bố quy tắc như thế này: bạn không thể sửa đổi một giá trị nhiều hơn một lần trong cùng một biểu thức. Để biết chi tiết đầy đủ, hãy xem "Biểu thức 6.5" trong tiêu chuẩn C. –

Trả lời

12

Bạn cần truyền con trỏ đến một trong 3 loại đó và sau đó sử dụng nó. Một cái gì đó như:

MyType* p = (MyType*)element; 
p->count++; 

Tuy nhiên, bạn cần chắc chắn loại đối tượng bạn đang truyền khi truyền một đối tượng sai loại có thể gây nguy hiểm.

+1

Hoặc gán cho một biến cục bộ của loại thích hợp: ví dụ: struct foo * f = element; (không yêu cầu diễn viên) –

+0

Cảm ơn bạn đã trả lời. Tuy nhiên điều này sẽ không hoạt động nếu tôi không biết MyType là gì trước đó phải không? – b1gtuna

+2

Bạn phải biết ... Không có cách nào thoát ra ngoài – Naveen

9

Đầu tiên, bạn đang đi qua một con trỏ để làm mất hiệu lực, đó là một cách tiếp cận hợp lệ cho các loại không xác định, nhưng bạn cần phải chuyển các con trỏ tới các kiểu đối tượng khác nhau của bạn.

C không phải là ngôn ngữ động nên thông tin kiểu tượng trưng bị xóa trước thời gian chạy vì vậy khi bạn nói rằng ba loại của bạn đều có thành viên Count, điều này không giúp thiết kế chức năng của bạn.

Cách duy nhất bạn có thể truy cập Count là bởi đúc tham số void* của bạn để loại con trỏ chính xác trước khi derefencing với -> hoặc (*element).Count (ví dụ: không chỉ .). Trừ khi bạn đang dựa vào các loại của bạn có một bố trí tương thích (có khả năng được thực hiện phụ thuộc), bạn cũng sẽ cần phải vượt qua một cái gì đó giúp chức năng của bạn xác định diễn viên chính xác để thực hiện. Tại thời điểm này, bạn có thể tốt hơn với ba chức năng riêng biệt và an toàn loại tốt hơn.

+0

giải thích của bạn đã làm rõ rất nhiều điều. Cảm ơn!! – b1gtuna

6

Bạn phải truyền cho loại thực tế nhưng bạn có thể nhận được kết quả không mong muốn trong các cấu trúc không có thuộc tính đếm ở cùng một vị trí.

typedef struct _A 
{ 
    int count; 
    int x; 
}A; 
int changeCount(void* element) 
{ 
    ((A*)element)->Count++; 

    return 1; 

} 

Cũng hãy nhớ rằng nếu bạn chuyển con trỏ đến cấu trúc giống như sau, bạn sẽ tăng trường x không phải là số.

typedef struct _B 
{ 
    int x;  
    int count; 

}B; 

    B st; 
    B* pst = &st; 
    changeCount(pst); 
+0

Nếu bạn định kiểu cho cấu trúc đã biết bên trong hàm thì mục đích của void * là gì? Chúng ta có thể trực tiếp đặt yếu tố A * không? Và tôi vẫn không hiểu cách void * được sử dụng làm tham số:/ –

+0

nếu bạn muốn sử dụng một hàm để thao tác với nhiều loại cấu trúc khác nhau. Đó là một loại đa hình. – rerun

+0

Nhưng trong ví dụ trên, bạn đang gõ vào A *, trong trường hợp đó tại sao sử dụng void *? –

1

sử dụng dàn diễn viên.

ClassName(element).count++ 

cũng là phần tử của bạn.Count = element.Count ++; dòng là dư thừa, bạn chỉ cần làm phần tử.đếm ++ (có nghĩa là tăng giá trị bởi một)

+0

Khuôn mẫu kiểu hàm của C++ không hợp lệ C và, nói chung, việc đúc kiểu con trỏ ('void *') cho một đối tượng không có khả năng hoạt động. –

0

Đây chính là vấn đề:

I am guessing this is happening because the compiler doesn't know the type of "element" before hand. However I don't see why this isn't working.

Gọi một phương pháp hoặc một thành viên dữ liệu theo tên không phải là nói chung là một tính năng của ngôn ngữ staticly-gõ.

Ngay cả một void * chỉ có thể được truyền đáng tin cậy đến T *, trong đó T là một loại, khi con trỏ thực sự là một con trỏ đến loại đó.

Trong C++, một khả năng là có tất cả ba kiểu kế thừa từ cùng một lớp bass ảo X có phương thức Đếm (tức là một giao diện ICountable). Sau đó truyền tới X * và sử dụng p->Count. Điều này sẽ là thành ngữ C++ - tất cả các lớp thực hiện cùng một giao diện và do đó tất cả chúng đều hỗ trợ phương thức này. Đây sẽ là một loại phương pháp hỗ trợ ngôn ngữ tương tự để dựa trên cùng một cấu trúc bù đắp lừa hiển thị trong câu trả lời của Tommy McGuire, mà làm cho các cấu trúc tất cả tương tự theo quy ước. Nếu bạn đã thay đổi cấu trúc, hoặc trình biên dịch đã rời khỏi tiêu chuẩn để đặt ra các cấu trúc, bạn sẽ ở trong nước nóng.

Tôi không thể không nghĩ rằng đây là một vấn đề đồ chơi, vì phương pháp đơn giản như vậy, người ta thường không bọc nó trong một hàm - người ta sẽ gọi nó là nội tuyến: T t; t.Count++;.

+0

Tôi đoán điều này sẽ không hoạt động: "kế thừa từ cùng một lớp bass ảo X" - câu hỏi là về C, không phải C++. – viraptor

+0

Trừ khi các thẻ sai lệch, đây là C, không phải C++. – LnxPrgr3

+0

Tôi sẽ chỉnh sửa để làm rõ rằng câu cuối cùng là một thành phần thiết kế C++. –

3

Nếu phần tử .Count có cùng loại cho mỗi loại trong số 3 loại đó, thì bạn cũng có thể sử dụng macro. Giả sử nó là int, bạn có thể làm điều này:

#define changeCount(a) _changeCount((a), &(a)->Count) 

int _changeCount(void* element, int *count) 
{ 
    (*count)++; 
    return 1; 
} 

này sẽ làm việc, vì địa chỉ a.Count sẽ được giải quyết khi bạn gọi hàm, chứ không phải sau (khi bạn không biết loại nữa). Tôi cho rằng bạn có loại thích hợp khi bạn gọi hàm này. Vì vậy, Sometype x; changeCount(x); sẽ hoạt động, nhưng chuyển vào thứ gì đó đã là (void*) thì không.

Biểu thức gốc của bạn element.Count = element.Count++; khá kỳ lạ. Nếu bạn muốn tăng, hãy sử dụng element.Count++ hoặc element.Count = element.Count + 1.

+0

+1: & (a) -> Đếm phải là & (a-> Đếm) - ngoài ra, tại sao bạn thậm chí còn đặt phần tử trong _changeCount –

+1

@Cade - Tôi để lại phần tử ở đó, trong trường hợp OP muốn làm điều gì đó hơn trong đó nhưng đã cho chúng tôi một phiên bản đơn giản. Ngoài ra - tôi nghĩ '& (a) -> Count' là đúng -' -> 'có ưu tiên cao hơn' & ', do đó giải quyết địa chỉ là ok, nhưng phiên bản của bạn sẽ không hoạt động chính xác cho' changeCount (some_ptr + 4) ' – viraptor

1

Khi biên dịch, C loại bỏ [1] hầu hết thông tin loại, chỉ để lại bù trừ. Vì vậy, chức năng của bạn sẽ biên dịch một cái gì đó như thế này, trong giả:


changeCount: 
    assign *(*(stack_ptr + offset_element) + offset_Count) + 1 
    to *(stack_ptr + offset_element) + offset_Count; 
    assign 1 to return_value; 
    pop 

Các stack_ptr là vị trí của stack frame được tạo ra khi bạn gọi changeCount, các offset_element là vị trí của đối số yếu tố, liên quan đến stack_ptr, nhưng offset_Count là gì? Hãy nhớ, tất cả trình biên dịch biết về mã của bạn chỉ là những gì bạn đã thể hiện trong ví dụ của mình; yếu tố là một con trỏ chung, không thực sự là một con trỏ đến bất cứ điều gì. Bạn sẽ phải nói với trình biên dịch những gì phần tử được trỏ đến, bằng cách đúc hoặc gán nó vào một biến [2]:


typedef struct { int Count; } counted_t; 
int changeCount(void* element) 
{ 
    counted_t* counted = element; 
    counted.Count++; 
    return 1; 
} 

Chức năng này sẽ tạo cơ bản (giả) cùng một mã như trên, nhưng trình biên dịch hiện nay biết những gì bù đắp của Count nên được.

Bạn đề cập rằng có ba khả năng cho loại phần tử nào trỏ đến. Có một vài cách để xử lý điều đó: hoặc là một công đoàn phân biệt hoặc một cấu trúc "được kế thừa". Đối với một sử dụng công đoàn phân biệt, nói, một cấu trúc với một phần tử là một enum xác định cái nào của ba khả năng và một phần tử khác là một liên minh của ba cấu trúc có thể; đây là những gì ngôn ngữ ML (OCaml, Haskell, v.v.)) gọi một loại dữ liệu đại số hoặc những gì một công đoàn là trong Pascal. Đối với "thừa kế", bạn có thể sử dụng định nghĩa kiểu:


typedef struct { counted_t counted; int i; } counted_int_t; 
typedef struct { counted_t counted; double d; } counted_double_t; 
typedef struct { counted_t counted; char* s; } counted_charptr_t; 

Trong trường hợp này, bạn có thể sử dụng chức năng changeCount ở trên và vượt qua trong một con trỏ đến một counted_int_t, counted_double_t, hoặc counted_charptr_t. Điều gì xảy ra là trình biên dịch sẽ bố trí ba cấu trúc với phần tử Đếm trong cấu trúc "hậu duệ" ở cùng một vị trí miễn là phần tử count_t là trước tiên. (Ít nhất, trong mọi trình biên dịch tôi đã từng sử dụng và trong mỗi đoạn mã tôi đã thấy. Tôi nghĩ điều này đã làm cho nó thành tiêu chuẩn C tại một số điểm, nhưng nó là một thành ngữ rất bình thường.)

[1] Ngoại trừ thông tin gỡ lỗi, nếu bạn đã yêu cầu trình biên dịch phát ra nó. Tuy nhiên, chương trình của bạn sẽ không có quyền truy cập vào thông tin đó, do đó, nó sẽ không giúp ích trong trường hợp này.

[2] Thao tác x ++ (postincrement) tăng biến (tốt, lvalue) mà nó được áp dụng; việc chuyển nhượng trong mã ban đầu là không cần thiết.

Các vấn đề liên quan