Làm thế nào để xác minh nếu một con trỏ void (void *) là một trong hai loại dữ liệu?


10

Tôi đang viết một hàm trong đó tôi muốn chấp nhận 2 types tham số.

  • Một string(char *)
  • Một structurenơi sẽ có n số phần tử.

Và để đạt được điều này tôi đang nghĩ đến việc sử dụng một void *loại tham số đơn giản . Nhưng tôi không biết làm thế nào để xác minh xem tham số này là loại này hay loại kia, một cách an toàn.


10
Bạn không thể! Ít nhất, bạn sẽ cần thêm một tham số thứ hai cho hàm, cho biết các void*điểm đó là gì .
Adrian Mole

4
... Và nếu bạn phải thêm một tham số thứ hai, bạn cũng có thể viết hai hàm riêng biệt func_strfunc_structkiểm tra kiểu tại thời gian biên dịch.
M Oehm

Vâng đó là lý do tại sao tôi đã suy nghĩ nếu nó chỉ có thể trong một chức năng
localhost

1
Bạn không thể một cách an toàn và di động. Nếu bạn đủ can đảm, bạn có thể thử sử dụng phương pháp phỏng đoán để thử và đoán xem các byte bộ nhớ đầu tiên có giống như những gì bạn có thể mong đợi cho các ký tự hay không, nhưng tôi sẽ không gọi đó là an toàn .
Serge Ballesta

Nếu bạn chỉ muốn một tên chung cho các hàm chuỗi và struct, bạn có thể sử dụng _Genericmacro. Bạn cũng có thể tạo các loại tự nhận dạng, ví dụ như với các hiệp hội được gắn thẻ , điều đó có nghĩa là bạn không thể vượt qua một char *chuỗi thô . Tất cả những điều đó có lẽ nhiều rắc rối hơn giá trị của nó.
M Oehm

Câu trả lời:


12

Việc dịch void*
"Dear trình biên dịch, đây là một con trỏ, một không có thêm thông tin cho bạn về vấn đề này.".

Thông thường trình biên dịch biết rõ hơn bạn (lập trình viên), vì thông tin anh ta nhận được trước đó và vẫn còn nhớ và bạn có thể đã quên mất.
Nhưng trong trường hợp đặc biệt này, bạn biết rõ hơn hoặc cần biết rõ hơn. Trong tất cả các trường hợp void*thông tin có sẵn, nhưng chỉ cho lập trình viên, người "tình cờ biết". Lập trình viên phải cung cấp thông tin cho trình biên dịch - hoặc tốt hơn cho chương trình đang chạy, bởi vì một lợi thế void*có là thông tin có thể thay đổi trong thời gian chạy.
Thông thường, điều đó được thực hiện bằng cách cung cấp thông tin qua các tham số bổ sung cho các hàm, đôi khi qua ngữ cảnh, tức là chương trình "tình cờ biết" (ví dụ: đối với mỗi loại có thể có một hàm riêng biệt, hàm nào được gọi là hàm).

Vì vậy, cuối cùng void*không chứa thông tin loại.
Nhiều lập trình viên hiểu nhầm điều này là "Tôi không cần biết thông tin loại".
Nhưng điều ngược lại là đúng, việc sử dụng void* làm tăng trách nhiệm của lập trình viên để theo dõi thông tin loại và cung cấp nó một cách thích hợp cho chương trình / trình biên dịch.


Ngoài ra, trình biên dịch thực sự biết loại dữ liệu được trỏ là gì. Vì vậy, nếu bạn nhảy vào một số void*chức năng, chuyển sang loại sai, sau đó hủy tham chiếu dữ liệu ... thì tất cả các cách thức của hành vi không xác định được gọi.
Lundin

5

void*là loại không dùng cho lập trình chung, hiện tại không có nhiều tình huống bạn nên sử dụng chúng. Chúng nguy hiểm vì chúng dẫn đến loại an toàn không tồn tại. Và như bạn đã lưu ý, bạn cũng mất thông tin loại, nghĩa là bạn phải kéo theo một số thứ cồng kềnh enumcùng với void*.

Thay vào đó, bạn nên sử dụng C11 _Genericcó thể kiểm tra các loại tại thời gian biên dịch và thêm an toàn loại. Thí dụ:

#include <stdio.h>

typedef struct
{
  int n;
} s_t; // some struct

void func_str (const char* str)
{
  printf("Doing string stuff: %s\n", str);
}

void func_s (const s_t* s)
{
  printf("Doing struct stuff: %d\n", s->n);
}

#define func(x) _Generic((x),              \
  char*: func_str, const char*: func_str,  \
  s_t*:  func_s,   const s_t*:  func_s)(x) \


int main()
{
  char str[] = "I'm a string";
  s_t s = { .n = 123 };

  func(str);
  func(&s); 
}

Hãy nhớ cung cấp constcác phiên bản đủ điều kiện ( ) của tất cả các loại bạn muốn hỗ trợ.


Nếu bạn muốn lỗi trình biên dịch tốt hơn khi người gọi vượt qua loại sai, bạn có thể thêm một xác nhận tĩnh:

#define type_check(x) _Static_assert(_Generic((x), \
  char*:   1,  const char*: 1,  \
  s_t*:    1,  const s_t*:  1,  \
  default: 0), #x": incorrect type.")

#define func(x) do{ type_check(x); _Generic((x),     \
  char*: func_str, const char*: func_str,            \
  s_t*:  func_s,   const s_t*:  func_s)(x); }while(0) 

Nếu bạn thử một cái gì đó như int x; func(x);bạn sẽ nhận được thông báo trình biên dịch "x: incorrect type".

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.