何时使用const char *,何时使用const char []?

78
我知道它们是不同的,我知道它们之间的区别,而且我已经阅读了所有关于char* vs char[] 的问题。但是所有这些答案都没有说明应该在什么情况下使用哪种类型。因此我的问题是:在什么情况下使用:
const char *text = "text";

你什么时候使用它?

const char text[] = "text";

有任何准则或规则吗?

以一个例子来说,哪个更好:

void withPointer()
{
    const char *sz = "hello";
    std::cout << sz << std::endl;
}

void withArray()
{
    const char sz[] = "hello";
    std::cout << sz << std::endl;
}

(我知道std::string也是一种选择,但我特别想了解char指针/数组)


3
它们不同吗?我一直以为 []* 的语法糖。 - Dave
7
@Dave: 它们是不同的:请看https://dev59.com/G1rUa4cB1Zd3GeqPilOq。指针版本使用6个字节+指针大小,数组版本使用6个字节。 - rve
1
请看这里 - https://dev59.com/D3M_5IYBdhLWcg3wThV3#1431607 - Michael Krelin - hacker
1
@Nikko:你为什么加了C标签? - rve
2
@Dave 请参考 https://dev59.com/OnI95IYBdhLWcg3w7CfL - BlueRaja - Danny Pflughoeft
显示剩余3条评论
7个回答

81

两者有明显的区别,首先:

  1. 第一个创建指针。
  2. 第二个创建数组。

继续阅读以获取更详细的解释:

数组版本:

char text[] = "text"; 

创建一个足够大以容纳字符串字面量"text"(包括其NULL终止符)的数组。该数组text使用字符串字面量"text"进行初始化。该数组可以在以后的时间内进行修改。此外,即使在编译时,也知道数组的大小,因此可以使用sizeof运算符来确定其大小。


指针版本:

char *text  = "text"; 
创建一个指针,指向字符串字面量“text”。与数组版本相比,这更快,但是指针所指的字符串不应该被更改,因为它位于只读实现定义内存中。修改这样的字符串字面量会导致未定义的行为。 事实上,C++03不建议使用没有const关键字的字符串字面量。因此,声明应该是:
const char*text = "text";

此外,你需要使用 strlen() 函数来获取字符串的大小,而不是使用 sizeof 操作符,因为 sizeof 操作符只会返回指针变量的大小。


哪一种版本更好?

这取决于使用情况。

  • 如果你不需要对字符串进行任何更改,请使用指针版本。
  • 如果你打算更改数据,请使用数组版本。

编辑:在评论中提到了 OP 寻求以下两者之间的区别:

const char text[]const char* text

上述差异仍然适用,除了涉及修改字符串字面值的那个。使用 const 限定符后,数组 test 现在包含元素类型为 const char 的数组,这意味着它们无法被修改。

鉴于此,我会选择数组版本而不是指针版本,因为指针可以很容易地(通过错误)重新设置为另一个指针,并且字符串可能会通过该另一个指针进行修改,导致 UB。


2
我想补充一下,指针(除非是char *constchar const *const)可以被重置以指向不同的缓冲区,因此在这种受限的意义上,“text”是可以被更改的。 - Fred Foo
@Als:faenel在他/她的回答中提到的静态常量字符数组怎么样? - rve
@rve:我的昵称拼写为“Fanael”。不过,“faenel”也吸引了我一部分 :) - user784668
@rve:通过类型转换或其他方式修改“const”变量会导致未定义的行为,我认为这经常不会被注意到。顺便说一下,为了澄清您的评论:static const char[]const char *不同的,它们仍然具有完全相同的差异,我的上面的答案指出了这一点。使用static并不能以任何方式使字符串字面量可修改。 - Alok Save
1
如果您不需要修改数据,则使用const char text []可以减少目标文件中的重定位数量,并且需要更少的机器代码指令来访问。 - Dietrich Epp
显示剩余5条评论

9

可能最大的区别是,你不能使用指针来获取所指向缓冲区的大小,而对于 const char[] 版本,你可以使用数组变量上的 sizeof 来获取数组的内存占用大小(以字节为单位)。因此,这实际上取决于你想要做什么以及如何使用指针或缓冲区。

例如,执行以下操作:

void withPointer()
{
    const char *sz = "hello";
    std::cout << sizeof(sz) << std::endl;
}

void withArray()
{
    const char sz[] = "hello";
    std::cout << sizeof(sz) << std::endl;
}

将会给你非常不同的答案。


8
我想借此机会指出,sizeof不是一个函数。 - unwind
3
从技术上讲,但当我解析 std::cout << sizeof sz << std::endl; 时,我的眼睛会感到困惑,因为连续两个标识符通常表示类型声明。我的大脑会将 sizeof(sz) 解析为另一个函数。 - Mooing Duck

6

通常来说,回答这些类型的问题时,使用最明确的那个

在这种情况下,const char[] 胜出,因为它包含有关数据更详细的信息 - 即缓冲区的大小。


4

注意:

我建议将其写成static const char sz[] = "hello";。这样声明的好处是,对该常量字符串进行更改会通过写入只读内存使程序崩溃。没有static,则可能会忽略强制转换常数性并更改内容。

此外,static使数组仅位于常量数据段中,而不是在堆栈上创建并从常量数据段复制每次调用函数时。


1
我认为很少有人会忽略掉强制转换常量的问题。 - Mooing Duck
有时候确实会这样。这只是为了防止墨菲定律的保护。 - user784668
@Fanael: 那么 static const char[] 几乎与 const char* 表现相同吗?只有一份副本,在初始化期间没有开销吗? - rve
1
是的,您仍然可以在其上使用 sizeof 并获取数组的实际大小,因为它仍然是一个数组。 static 是一个存储说明符,与类型无关。 - user784668
我还发现了这个链接:https://dev59.com/OnI95IYBdhLWcg3w7CfL#2097649。它指出,如果指针版本相等,则可能指向相同的字符串,但数组版本始终是唯一的,即使字符串相等。 - rve
我完全看不出崩溃程序如何成为优势点。如果你正在编写一个玩具应用程序,那么可以接受。但如果它是运行重要进程的软件,比如控制硬件或其他重要任务,崩溃可能会给用户造成巨大损失甚至导致事故。 - cauchi

2

如果您使用数组,则数据在运行时初始化。如果使用指针,则运行时开销(可能)较小,因为只需要初始化指针。(如果数据比指针的大小小,则数据的运行时初始化小于指针的初始化。)所以,如果您有足够多的数据,并且关心初始化的运行时成本,应使用指针。您几乎不需要关心这些细节。


编译器可能会将这两个版本优化成相同的代码。运行时无需进行数组初始化。 - David Heffernan
3
我认为编译器无法生成相同的代码。对于一个常量数组,用户可以强制转换掉const属性,然后就可以向数组中写入内容,因此这个数组需要被放在可写内存中。 - Nemanja Trifunovic

2
我几年前曾受到Ulrich Drepper的博客文章的大力帮助:so close but no cigarmore array fun
该博客的要点是,应优先选择const char[],但仅限于全局或静态变量。
使用指针const char*的缺点包括:
  1. 需要额外的变量
  2. 指针可写
  3. 额外的间接寻址
  4. 通过指针访问字符串需要2次内存加载

0

只是提一下一个小细节,表达式:

const char chararr[4] = {'t', 'e', 'x', 't'};

还有一种方法可以用来初始化数组,确保它恰好包含4个字符。


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