为什么GLib在这些函数中不使用“const”?

5
我是一个有用的助手,可以翻译文本。

我对GLib有一个简单的问题。

我有以下代码:

static const char *words[] = { "one", "two", "three", NULL };

void main() {
  puts(g_strjoinv("+", words));
}

这段代码打印出one+two+three。它使用了一个GLib函数来连接字符串。
该函数的签名为:
char *g_strjoinv (const char *separator, char **str_array);

准确来说,GLib使用gchar而不是char,但我们忽略这个。

现在,我想知道为什么参数是char **str_array而不是const char **str_array。这迫使我进行显式转换以消除编译器的警告(“预期为'char **',但参数类型为'const char **'”):

  puts(g_strjoinv("+", (char **)words));

我查看了GLib参考文档,发现所有函数都是这样定义的:它们接受char **而不是const char **。为什么呢?为什么GLib不使用const char **呢?使用显式强制转换来去除const会使我的代码更不安全(因为编译器不再检查参数的兼容性)。这也让我感到紧张,因为GLib没有“签署合同”说明它不会更改我的数据。

1
好问题,str_array肯定没有被改变(?), 在这种情况下使用const是有意义的。 - this
@self:是的,这不仅仅是一个函数的问题。在所有接受字符串数组的GLib函数中都存在这个问题。我猜他们有充分的理由这样做,我想知道是什么原因。 - Niccolo M.
3
您可能需要查看这个问题。如果您有一个常规的char **,完全有可能,您将无法将它传递给期望const char **的函数,因此通过声明为这样并不能解决问题。 - Crowman
这些讨厌的星号。@PaulGriffiths,你应该把它作为答案。 - this
4
"void main()" 应该改为 "int main(void)"。如果你从一本书中看到了 "void main()",建议换一本更好的书。 - Keith Thompson
1个回答

6
你问题的假设是,如果g_strjoinv()的第二个参数声明为const char **类型,则可以轻松地将const char **char **传递给它。但不幸的是,这并不正确。如comp.lang.c FAQ中的这个问题所解释的那样,在指针的顶层间接层次结构出现类型不匹配是允许的,即你可以将一个char *传递给需要const char *的函数,但是你不能(除非进行强制转换)将char **传递给需要const char **的函数,因为在这种情况下,类型不匹配发生在第二级间接层次结构上。链接的问题详细解释了其中可能有些晦涩的原因。
因此,没有类型(在C中)能够同时接受char **const char **,而不需要至少其中之一进行类型转换。既然如此,除了方便之外,也许没有太多理由去选择其中任何一种方式,而库作者显然选择了前者。纯属猜测,我认为想要传递char **的情况略微多一些,因此他们选择的选项可能更方便。
在你的情况下,可以从数组的定义中省略const修饰符。如果你喜欢const,这似乎不好,但由于你有一个指向字符串字面值的指针数组,如果有任何尝试写入它们的内容,程序很可能会响亮地抱怨并失败,所以在这种情况下你实际上没有失去任何有意义的安全性。即使企图修改它们是未定义的行为,但在C中,字符串字面值的类型为char的数组,而不是const char的数组(不像C++)。
稍微有些晦涩,但即使你可以将它作为const char **传递,这里的函数仍然可以修改指针,即使不能修改它们的指向内容,也可能以不同的方式造成混乱,因此函数所做出的承诺仍然不能保证传递给函数的内容不会以某种方式被更改。

这是一个非常好的答案,非常感谢!我之前遇到过这个问题,但我不知道它也与当前情况有关。现在,多亏了你,我完全明白了。顺便说一下,你说我可以放弃我的“const”,但当我这样做时,GCC会发出警告(“初始化丢弃'const'...”),因为,我猜,它将字符串字面值视为常量。 - Niccolo M.
(顺便说一句,感谢您上次的最后一段话。我的C语言技能有点生疏,我需要这个纠正。) - Niccolo M.
这听起来像是一个虚假的警告,你确定你没有将其编译为C++或使用某些扩展吗?编译为标准C不应该会出现那个警告。 - Crowman
没错。我发现gcc是带有“-Wwrite-strings”被调用的。看起来这是autoconf的责任。出于好奇,我现在会尝试找出我的'configure.ac'中是什么导致了这个问题。 - Niccolo M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接