【问题标题】:const char * VS char const * const (Not about what is const)const char * VS char const * const(不是关于什么是 const)
【发布时间】:2012-07-21 15:09:44
【问题描述】:

所以,我知道 char const *、char * const 和 char const * const 之间的区别。那些是:

char* the_string :我可以更改 the_string 指向的字符, 我可以修改它指向的字符。

const char* the_string :我可以将 char 更改为 the_string 点,但我无法修改它指向的字符。

char* const the_string : 我无法将 the_string 更改为 char 点,但我可以修改它指向的字符。

const char* const the_string : 我无法将 char 更改为 the_string 指向,我也不能修改它指向的字符。

(来自const char * const versus const char *?

现在,我的问题是:假设我正在编写一个不会修改传递给它的 C 字符串的函数,例如:

int countA(??? string) {
    int count = 0;
    int i;
    for (i=0; i<strlen(string); i++) {
         if (string[i] == 'A') count++;
    }
    return count;
}

现在,标题应该是什么?

int countA(char const * string); 
int countA(char const * const string);

我的感觉是我应该使用第二个,因为我不会修改指针本身,也不会修改数组的内容。但是当我查看标准函数的标题时,他们使用第一个。示例

char * strcpy ( char * destination, const char * source );

为什么?

(事实上char const *对我来说并没有什么意义,因为如果你在考虑抽象字符串,要么你没有修改字符串(所以,char const * const因为你没有修改指针,不是内容)或者你会修改字符串(所以只是char *,因为你可能会修改内容并且你可能需要分配更多的内存,所以你可能需要修改指针)

我希望有人能把这一切告诉我。谢谢。

【问题讨论】:

    标签: c string constants


    【解决方案1】:

    在这种情况下,指针本身是否为 const 并不重要,因为它无论如何都是按值传递的:无论 strcpysource 做什么都不会影响调用者的变量,因为 strcpy 会操作堆栈上调用者的source 的副本,而不是原始的。注意我说的是指针值,而不是指针指向,显然应该改变,因为它是参数。

    char dest[10];
    char const * source = "Hello";
    strcpy( dest, source );
    // no matter what strcpy does, the value of source will be unchanged
    

    strcpy 中,无论如何您都需要在destinationsource 指向的数组上迭代指针。不将参数声明为 const 允许函数直接使用堆栈中的值,而无需先复制/强制转换它们。

    【讨论】:

      【解决方案2】:

      const char * 代表合同。这是一个承诺,该函数不会使用该指针来修改用户传递的内容。指针本身是否为常量的价值较小。

      因此,对于调用者而言,指针本身是否为const 没有任何区别。

      在许多实现中,“字符串”函数会定期修改传递的指针而不修改内容。如果规范(C 标准)要求指针本身保持不变,这将是所有实现的限制因素,而不会给调用者带来任何好处。


      作为旁注,我认为你不应该在 const 做所有事情,然后按照自己的方式解决它。如果它感觉该功能不应该有理由更改它,只需制作const 的东西。简而言之,不要疯狂,这不是灵丹妙药可能会导致挫败感。

      【讨论】:

        【解决方案3】:

        非定义声明:

        int countA(char const * string);
        int countA(char const * const string);
        int countA(char const * foobar);
        int countA(char const *);
        

        都是等价的。 string 参数名称(实际上)是countA 执行内部的一个局部变量。实现是否修改该变量与调用者无关,也不影响函数的签名。

        函数是否修改stringreferand是调用者的事,所以第一个const很重要。变量的命名是调用者的事情,但这只是因为约定是在声明中命名参数作为提示它们的用途。文档是完全传达每个参数含义的方式,而不是其名称。

        如果你想要一个规则:省略第二个const,因为它会混淆声明,同时告诉调用者没有任何用处。

        您可以将它包含在函数定义中,但是这样做会出现一些问题。您要么需要使标头与定义保持同步(在这种情况下,您有时会发现自己更改了标头,因为实现细节的更改不会影响调用者)。否则您必须接受标题与定义不匹配,这有时会使看到的人感到不安:

        int countA(char const * const string) {
            return 0; 
        }
        

        并在标题中搜索int countA(char const * const string),反之亦然,查看标题并搜索源。他们需要更智能的搜索词。

        【讨论】:

        • 我不知道第二个const实际上影响签名!
        • @DevSolar:是的。我懒得在标准中查找它,但是 IIRC 有明确的文字可以这么说。
        【解决方案4】:

        当您将函数参数声明为const char * const 时,它与来自const char * 的调用者没有什么不同:他们根本不在乎您对该指针做什么,因为对他们而言,无论如何这都是“按值传递” .

        第二个const 是为您准备的,而不是为您的用户准备的。如果你知道你不会修改那个指针,那么一定要声明它const char * const:它将帮助你和其他维护你的代码的人在以后捕获错误。

        至于标准库,我的猜测是他们不想让它成为 const char * const,因为他们想要修改指针的能力:

        char * strcpy ( char * destination, const char * source ) {
            char *res = destination;
            while (*destination++ = *source++)
                ;
            return res;
        }
        

        【讨论】:

          【解决方案5】:
          • char const *s : s 是指向 const char 的指针。
          • char *const s : s 是一个指向 char 的常量指针。

          s 是函数参数时,第一种表示法比第二种表示法更有用。

          • 使用char const *,您无法修改指向的值。
          • 使用char *const,您无法修改指针的值。就像函数参数中的int const:你不能直接对你的参数进行操作。它不会改变调用函数的任何内容。 (现在,它对编译器几乎没用,但对程序员有意义)

          【讨论】:

          • 实际上,在这种情况下,一个优秀的程序员会知道s 无论如何都是按值传递的,所以const 无论如何都无关紧要......
          • 当然;正如我所说,它不会改变调用函数的任何内容,所以它只是以一种表达方式使用(作为对编译器优化器的帮助)。它就像参数中的整数常量,一些程序员使用它(我也是)。