我应该如何定义/声明字符串常量

时间:2019-03-04 01:47:06

标签: c

我一直在C中使用字符串常量作为以下内容之一

char *filename = "foo.txt";
const char *s = "bar";    /* preferably this or the next one */
const char * const s3 = "baz":

但是,在阅读this之后,现在我想知道是否应该将我的字符串常量声明为

const char s4[] = "bux";

请注意,建议作为重复项的链接问题有所不同,因为该问题专门询问关于恒定字符串的问题。我知道类型如何不同以及如何存储。该问题中的数组版本不是 const限定的。这是一个简单的问题,即我是否应该对常量字符串使用常量数组与我一直使用的指针版本。在SO和Google进行为期两天的搜索均未得出确切答案时,这里的答案已经回答了我的问题。多亏了这些答案,我才知道当数组标记为const时,编译器可以执行特殊操作,并且确实有(至少一种情况)我将使用数组版本。

3 个答案:

答案 0 :(得分:16)

指针和数组不同。将字符串常量定义为指针或数组适合不同的目的。

当您定义一个不受更改的全局字符串常量时,我​​建议您将其设置为const数组:

const char product_name[] = "The program version 3";

将其定义为const char *product_name = "The program version 3";实际上定义了2个对象:字符串常量本身(将驻留在常量段中)和指针,可以将其更改为指向另一个字符串或设置为NULL

相反,将字符串常量定义为局部变量最好作为const char *类型的局部指针变量来完成,并使用字符串常量的地址进行初始化:

int main() {
    const char *s1 = "world";
    printf("Hello %s\n", s1);
    return 0;
}

如果将此数组定义为一个数组,则取决于编译器和函数内部的用法,代码将在堆栈上为该数组腾出空间,并通过将字符串常量复制到该数组中来对其进行初始化,这将是更长时间的昂贵操作字符串。

还要注意,const char const *s3 = "baz";const char *s3 = "baz";的冗余形式。它不同于const char * const s3 = "baz";,后者定义了一个指向常量字符数组的常量指针。

最后,字符串常量是不可变的,因此应该具有类型const char []。 C标准特意允许程序员像在char *s2 = "hello";中那样将其地址存储在非const指针中,以避免对遗留代码产生警告。在新代码中,强烈建议始终使用const char *指针来操作字符串常量。当函数不更改字符串内容时,这可能会迫使您将函数参数声明为const char *。此过程称为定义,可避免细微的错误。

请注意,某些函数违反了const的传播方式:strchr()不会修改收到的声明为const char *的字符串,但会返回char *。因此,可以通过以下方式将指向字符串常量的指针存储到普通的char *指针中:

char *p = strchr("Hello World\n", 'H');

此问题在C ++中通过重载得以解决。 C程序员必须解决此缺点。更为烦人的是strtol()的情况,其中传递了char *的地址,并且需要强制转换以保留适当的常数。

答案 1 :(得分:8)

链接的文章探索了一个小小的人为情况,如果您在const中的*后面插入const char *ptr = "Lorum ipsum";(在使用lang-1000.11的Apple LLVM 10.0.0中进行了测试),显示的差异消失了。 45.5)。

编译器必须加载ptr的事实完全是由于可以在编译器不可见的其他某些模块中对其进行更改。使用指针const可以消除这种情况,并且编译器可以直接准备字符串的地址,而无需加载指针。

如果您要声明一个指向字符串的指针并且从不更改该指针,则将其声明为static const char * const ptr = "string";,并且只要ptr的值,编译器就可以愉快地提供字符串的地址。用来。它实际上不需要从内存中加载ptr的内容,因为它永远不会改变,并且众所周知,它指向编译器选择存储字符串的任何位置。这样就与static const char array[] = "string";相同,只要需要数组的地址,编译器就可以根据其选择存储数组的位置来提供它。

此外,使用static指定符,ptr在翻译单元(正在编译的文件)之外是未知的,因此编译器可以在优化过程中将其删除(只要您没有使用它)。地址,可能是将其传递给翻译单元外部的另一个例程时)。结果应该是指针方法和数组方法之间没有差异。

经验法则:尽可能多地告诉编译器有关这些内容的信息:如果它永远不会改变,请将其标记为const。如果它在当前模块本地,请标记为static。编译器拥有的信息越多,它可以优化的越多。

答案 2 :(得分:2)

从性能的角度来看,这是一个相当小的优化,对于需要以尽可能低的延迟运行的低级代码来说是有意义的。

但是,我认为const char s3[] = "bux";从语义角度来看更好,因为右侧的类型更接近左侧的类型。因此,我认为使用数组语法声明字符串常量是有意义的。