将char *转换为char * const *

5

非常抱歉这个问题比较基础。我有时候在使用指针时会感到困惑。我有一个char *,但我需要将它转换为char * const *,以便能够在fts()函数中正确使用它。我该怎么做呢?

谢谢。


你清楚这些东西即使没有 const 也不等价吗?第一个是指向字符的指针,第二个是指向字符指针的常量指针。第二个有更深的间接层级。第一个需要一次跳转到达字符,而第二个需要两次。 - Codie CodeMonkey
相关链接:http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.17 - Billy ONeal
@DeepYellow 是的,我知道,因此我不想直接转换。这样做可以吗?声明一个 char * const * 变量,比如说 x。让 char * 变量为 p。然后将 x 设为 &p。 - Lipika Deka
我的评论在以下类似答案之后几秒钟发表。 - Lipika Deka
如果你这样做,那就是在自找麻烦。 - Matt K
3个回答

12

由于类型不兼容,您不应该进行这种类型的转换。

关于指针和指向指针的指针

char*是指向字符字符串的指针,而char**是指向字符字符串的指针的指针。(const是一个奖励)。这意味着您应该提供一个字符字符串的数组,而不是提供一个字符字符串。

那两个东西显然是不兼容的。不要使用强制转换混合它们。

关于fts_* API

要找到解决问题的方法,我们需要阅读fts_*函数API(例如在http://linux.die.net/man/3/fts),我看到:

FTS *fts_open(char * const *path_argv, int options,
          int (*compar)(const FTSENT **, const FTSENT **));

使用你的char * const *参数path_argv时,描述说明:

[...] 如果compar()参数为NULL,则目录遍历顺序按照根路径在path_argv中列出的顺序 [...]

这证实了fts_open函数确实需要一组路径,而不是仅一个路径。

因此,我猜你需要像以下这样向它传递参数:

char *p[] = { "/my/path", "/my/other/path", "/another/path", NULL } ;

关于 const

C和C++中的类型是从右向左读取的。因此,如果你有:

  • char *:指向字符的指针
  • char const *:指向常量字符的指针(即您无法修改指向的字符串,但可以修改指针)
  • const char *:与 char const * 相同
  • char * const:指向字符的常量指针(即您可以修改所指向的字符串,但不能修改指针)
  • char **:指向指向字符的指针
  • char * const *:指向指向常量字符的指针(即您可以修改字符指针,同时可以修改 char 的字符串,但不能修改中间的指针)

这可能会令人困惑,但按照从右到左的顺序阅读它们将在您更熟悉指针时变得清晰明了(而如果您编程使用C或C ++,您希望熟悉指针)。

如果我们回到最初的例子(在 C99 上发送一堆警告):

char ** p = { "/my/path", "/my/other/path", "/another/path", NULL } ;

我已经使用这个API,你可以通过两种方式来提供路径:

    char * p0 = "/my/path" ;
    char * p1 = "/my/other/path" ;
    char * p2 = "/another/path" ;

    /* with a fixed-size array */
    char * pp[] = {p0, p1, p2, NULL} ;

    FTS * fts_result = fts_open(pp, 0, NULL);

编辑于2011年11月10日:snogglethorpe 指出这个解决方案不是C89有效的解决方案,即使它在gcc下编译成功(不包括pendantic + C89标志)。请查看Error: initializer element is not computable at load time以获取更多信息。

或者:

    /* with a malloc-ed array */
    char ** pp = malloc(4 * sizeof(char *)) ;
    pp[0] = p0 ;
    pp[1] = p1 ;
    pp[2] = p2 ;
    pp[3] = NULL ;

    FTS * fts_result2 = fts_open(pp, 0, NULL);
    free(pp) ;

编辑

在阅读其他答案后,只有两个答案(mkbmoshbear)避免了“只是强制转换数据”的错误。

在我的回答中,我忘记了数组的NULL终止符(但我不知道Linux API,也不知道fts_*类函数,所以...)


请注意,使用char *p[]时,p将被评估为char **,而char **可以隐式转换为char * const * - caf
@Juggler:是的...花了一些时间,我把一些错误由mkb和caf纠正了...这些类型声明可能会让人感到困惑,但不要害怕。你会逐渐习惯它们(并且在几个月后编写C#代码时很容易不用它们,就像我做的那样...) - paercebal
@caf 和 @mkb:感谢您们的纠正。我太专注于解释“const”和其他内容,以至于错过了“char * p[]”,直到我在gcc上测试代码。 - paercebal
哇,你真的很用心地回答了这个问题!顺便说一句,我认为在数组初始化器中使用变量(如{p1, p2, p3, NULL})只在C99中有效,可能不被所有编译器支持,例如,微软已经宣布他们不支持C99... - snogglethorpe
@paercebal 不是C++11,而是C99。传统的C(C89)仅支持静态数组初始化程序,但您正在使用包含变量(p1p2p3)的初始化程序。默认情况下,即使在C89模式下,gcc也支持此扩展,但如果您像这样编译gcc -std=c89 -pedantic-errors,它将失败。我认为这在C++中是有效的,所以如果您的朋友尝试将其作为C++程序运行,它应该可以工作... - snogglethorpe
显示剩余3条评论

2
您需要创建一个第二个数组,它必须以NULL结尾(因为fts()的第一个参数是argv)。
例如:
char *const buf2[2] = { buf, NULL };
fts(buf2);

这并不是你想象中的那样。buf2 只是一个标量,因此只有 buf 被用来初始化它,而 NULL 被忽略了。 - caf
现在你又有了另一个问题,虽然不大——在C语言中无法将const char * const *隐式转换为char *const *(尽管在C++中可以)。 - caf
@caf 已经修复,再次提交。在C++中,我认为仍然需要使用const_cast - moshbear

2

我假设你说的是 fts_open

FTS *fts_open(char * const *path_argv, int options,
                 int (*compar)(const FTSENT **, const FTSENT **));

它需要的是一个数组,其中包含const char*指针,也就是一组字符串。 const只是告诉你它不会修改你的字符串,并为你提供传递const字符串的机会。
const变量可以被视为const,但通常不应反过来处理。
第一个参数就像传递给mainargv一样,你可以这样写:
char *path_argv[] = { "/first_path/", "/second_path/", NULL };

为了表示数组的结尾,最后一个元素必须是NULL

请注意,path_argv也可以声明为:

char **path_argv

OR*

char * const *path_argv

任何这些类型都是作为第一个参数传递给fts_open的合适类型。然而,显然你需要与上面不同的方式初始化它,但那是你可以“声明”path_argv的其他方法。我之前表述不够清晰。
然后只需使用fts_open(path_argv,options,compar),其中options是您的选项,compar是您的比较函数。

@mkb 哎呀,我的错,谢谢你发现了这个问题。复制、粘贴失败了。 - AusCBloke
如果您使用该初始化程序,则char *path_argv[]不能被重写为其他两种形式之一。多元素初始化程序仅适用于初始化数组或结构,而不适用于标量(是否有人尝试编译他们的答案?)。 - caf
@caf 抱歉,那不是我的意思,我是指 path_argv 可以被声明为其中任何一个,然后传递给 fts_open。让我修正一下我的措辞。 - AusCBloke

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