[英]idiomatic C for const double-pointers
I am aware that in C you can't implicitly convert, for instance, char**
to const char**
(cf C-Faq , SO question 1 , SO Question 2 ).我知道在 C 中,您不能将例如
char**
隐式转换为const char**
(参见C-Faq , SO question 1 , SO Question 2 )。
On the other hand, if I see a function declared like so:另一方面,如果我看到一个 function 声明如下:
void foo(char** ppData);
I must assume the function may change the data passed in. Therefore, if I am writing a function that will not change the data, it is better, in my opinion, to declare:我必须假设 function 可能会更改传入的数据。因此,如果我正在编写不会更改数据的 function,我认为最好声明:
void foo(const char** ppData);
or even:甚至:
void foo(const char * const * ppData);
But that puts the users of the function in an awkward position.但这会让 function 的用户陷入尴尬的 position 中。 They might have:
他们可能有:
int main(int argc, char** argv)
{
foo(argv); // Oh no, compiler error (or warning)
...
}
And in order to cleanly call my function, they would need to insert a cast.为了干净地调用我的 function,他们需要插入一个演员表。
I come from a mostly C++ background, where this is less of an issue due to C++'s more in-depth const rules.我主要来自 C++ 背景,由于 C++ 更深入的 const 规则,这不是一个问题。
What is the idiomatic solution in C? C 中的惯用解决方案是什么?
Declare foo as taking a char**
, and just document the fact that it won't change its inputs?将 foo 声明为采用
char**
,并仅记录它不会更改其输入的事实? That seems a bit gross, esp.这似乎有点恶心,尤其是。 since it punishes users who might have a
const char**
that they want to pass it (now they have to cast away const-ness)因为它会惩罚可能拥有想要传递它的
const char**
的用户(现在他们必须抛弃const-ness)
Force users to cast their input, adding const-ness.强制用户转换他们的输入,添加 const-ness。
Something else?还有什么?
Although you already have accepted an answer, I'd like to go for 3) namely macros. 虽然你已经接受了答案,但我想找到3)即宏。 You can write these in a way that the user of your function will just write a call
foo(x);
你可以用你的函数用户只写一个调用
foo(x);
的方式来编写它们foo(x);
where x can be const
-qualified or not. 其中x可以是
const
-qualified或不是。 The idea would to have one macro CASTIT
that does the cast and checks if the argument is of a valid type, and another that is the user interface: 这个想法将有一个宏
CASTIT
执行转换并检查参数是否是有效类型,另一个是用户界面:
void totoFunc(char const*const* x);
#define CASTIT(T, X) ( \
(void)sizeof((T const*){ (X)[0] }), \
(T const*const*)(X) \
)
#define toto(X) totoFunc(CASTIT(char, X))
int main(void) {
char * * a0 = 0;
char const* * b0 = 0;
char *const* c0 = 0;
char const*const* d0 = 0;
int * * a1 = 0;
int const* * b1 = 0;
int *const* c1 = 0;
int const*const* d1 = 0;
toto(a0);
toto(b0);
toto(c0);
toto(d0);
toto(a1); // warning: initialization from incompatible pointer type
toto(b1); // warning: initialization from incompatible pointer type
toto(c1); // warning: initialization from incompatible pointer type
toto(d1); // warning: initialization from incompatible pointer type
}
The CASTIT
macro looks a bit complicated, but all it does is to first check if X[0]
is assignment compatible with char const*
. CASTIT
宏看起来有点复杂,但它只是首先检查X[0]
是否与char const*
兼容。 It uses a compound literal for that. 它使用复合文字。 This then is hidden inside a
sizeof
to ensure that actually the compound literal is never created and also that X
is not evaluated by that test. 然后将其隐藏在
sizeof
以确保实际上永远不会创建复合文字,并且X
不会被该测试评估。
Then follows a plain cast, but which by itself would be too dangerous. 然后是一个普通的演员,但这本身就太危险了。
As you can see by the examples in the main
this exactly detects the erroneous cases. 正如您在
main
示例中所看到的,这可以准确地检测出错误的情况。
A lot of that stuff is possible with macros. 宏可以实现很多这样的东西。 I recently cooked up a complicated example with
const
-qualified arrays . 我最近用
const
限定数组编写了一个复杂的例子。
2 is better than 1. 1 is pretty common though, since huge volumes of C code don't use const at all. 2比1好.1虽然很常见,因为大量的C代码根本不使用const。 So if you're writing new code for a new system, use 2. If you're writing maintenance code for an existing system where const is a rarity, use 1.
因此,如果您正在为新系统编写新代码,请使用2.如果您正在为const很少的现有系统编写维护代码,请使用1。
Go with option 2. Option 1 has the disadvantage that you mentioned and is less type-safe. 使用选项2.选项1具有您提到的缺点,并且类型安全性较低。
If I saw a function that takes a char **
argument and I've got a char *const *
or similar, I'd make a copy and pass that, just in case. 如果我看到一个带有
char **
参数的函数,并且我有一个char *const *
或类似的char *const *
,我会复制并传递它,以防万一。
Modern (C11+) way using _Generic
to preserve type-safety and function pointers:现代(C11+)方式使用
_Generic
来保持类型安全和 function 指针:
// joins an array of words into a new string;
// mutates neither *words nor **words
char *join_words (const char *const words[])
{
// ...
}
#define join_words(words) join_words(_Generic((words),\
char ** : (const char *const *)(words),\
char *const * : (const char *const *)(words),\
default : (words)\
))
// usage :
int main (void)
{
const char *const words_1[] = {"foo", "bar", NULL};
char *const words_2[] = {"foo", "bar", NULL};
const char *words_3[] = {"foo", "bar", NULL};
char *words_4[] = {"foo", "bar", NULL};
// none of the calls generate warnings:
join_words(words_1);
join_words(words_2);
join_words(words_3);
join_words(words_4);
// type-checking is preserved:
const int *const numbers[] = { (int[]){1, 2}, (int[]){3, 4}, NULL };
join_words(numbers);
// warning: incompatible pointer types passing
// 'const int *const [2]' to parameter of type 'const char *const *'
// since the macro is defined after the function's declaration and has the same name,
// we can also get a pointer to the function
char *(*funcptr) (const char *const *) = join_words;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.