正如标题所说,
“What is the difference between”之间的差异是什么?
char a[] = ?string?; and
char *p = ?string?;
这个问题是在面试中问我的,我甚至不理解这个陈述。
char a[] = ?string?
问号操作符?
是什么?它是字符串的一部分还是有特定的含义?
?
似乎是一个拼写错误,它并不符合语义规范。因此,答案假设?
是一个打字错误,并解释了面试官可能实际想要询问的内容。
首先,两者截然不同:
继续阅读以获取更详细的解释:
char a[] = "string";
NULL
终止符。数组string
被初始化为字符串常量"string"。此后,数组可以在以后的时间内修改。同时,即使在编译时,数组的大小也是已知的,因此可以使用sizeof
运算符来确定其大小。
char *p = "string";
创建一个指向字符串字面值"string"的指针。与数组版本相比,这更快,但是指针指向的字符串不应该被修改,因为它位于只读实现定义的内存中。修改这样的字符串字面值会导致未定义的行为。
事实上,C++03已经[Ref 1]弃用了没有带有const
关键字的字符串字面值的使用。所以声明应该是:
const char *p = "string";
strlen()
函数来查找字符串的大小,而不是使用 sizeof
,因为 sizeof
运算符只会给出指针变量的大小。
取决于用途。
注意:这不是 C++,而是 C 专用。
请注意,在 C 中,使用字符串字面值而没有 const
关键字是完全有效的。
然而,在 C 中修改字符串字面值仍然是未定义行为[Ref 2]。
这带来了一个有趣的问题,
在 C 中,char* 和 const char* 在使用字符串字面值时有什么区别?
对于 Standerdese 粉丝:
[Ref 1]C++03 标准:§4.2/2
一个非宽字符串字面值(2.13.4)可以转换为类型为“指向 char 的指针”的 rvalue;一个宽字符串字面值可以转换为类型为“指向 wchar_t 的指针”的 rvalue。在任一情况下,结果都是数组的第一个元素的指针。只有当存在显式适当的指针目标类型时才考虑此转换,而不是当存在从 lvalue 转换为 rvalue 的一般需要时。[注意:此转换已被弃用。请参见附录 D。] 为了在重载分辨率(13.3.3.1.1)中进行排名,这种转换被认为是数组到指针转换,然后是限定符转换(4.4)。[例如:“abc”被转换为“指向 const char 的指针”,作为数组到指针的转换,然后作为限定符转换而转换为“指向 char 的指针”。]
C++11 只是删除了上述引文,这意味着它在 C++11 中是非法代码。
[Ref 2]C99 标准 6.4.5/5 "String Literals - Semantics":
在第七个翻译阶段,对于由字符串字面量或多个字面量组成的多字节字符序列,会在其末尾添加一个值为零的字节或代码。然后使用这个多字节字符序列来初始化一个具有静态存储期和长度足以容纳该序列的数组。对于字符字符串字面量,数组元素的类型为char,并用多字节字符序列的各个字节进行初始化;对于宽字符串字面量,数组元素的类型为wchar_t,并用宽字符序列进行初始化...const char a [] =“string”;
,即只需添加一个const
。这样可以避免在启动时(至少在Linux上)动态链接器执行其工作时进行重定位。有关更长的讨论,请参见如何编写共享库第2.4.1节。 - Frerich RaabeThe array declaration
char a[6];
requests that space for six characters be set aside, to be known by the namea
. That is, there is a location nameda
at which six characters can sit. The pointer declarationchar *p;
on the other hand, requests a place which holds a pointer. The pointer is to be known by the namep
, and can point to any char (or contiguous array of chars) anywhere.The statements
char a[] = "string"; char *p = "string";
would result in data structures which could be represented like this:
+---+---+---+---+---+---+----+ a: | s | t | r | i | n | g | \0 | +---+---+---+---+---+---+----+ +-----+ +---+---+---+---+---+---+---+ p: | *======> | s | t | r | i | n | g |\0 | +-----+ +---+---+---+---+---+---+---+
It is important to realize that a reference like
x[3]
generates different code depending on whetherx
is an array or a pointer. Given the declarations above, when the compiler sees the expressiona[3]
, it emits code to start at the locationa
, move three elements past it, and fetch the character there. When it sees the expressionp[3]
, it emits code to start at the locationp
, fetch the pointer value there, add three element sizes to the pointer, and finally fetch the character pointed to. In the example above, botha[3]
andp[3]
happen to be the characterl
, but the compiler gets there differently.
const char *p = "string"; p = "another string"; printf("%c", p[3]);
(是的,char *p = "string";
将会出现编译错误)。 - nodakaichar a[] = "string";
这会在堆栈上分配字符串。
char *p = "string";
这在堆栈上创建一个指向进程数据段中字面值的指针。
?
是指谁写的不知道自己在做什么。
char a[]
...在栈上分配...”假设它在一个函数内而不是全局,并且进一步指的是 a[]
,但没有提到在函数内部实际上有一个运行时从常量数据段到栈的整个文本的副本。使用创建非“const”指针的 char*
- 在堆栈上或作为数据段中的全局变量 - 并在运行时或(很可能)编译时初始化以寻址常量文本。 - Tony Delroyconst
限定符已经被弃用了,因此第二个代码片段不是合法的C++代码。 - Alok Savechar *p = "string";
printf("%s", p);
p = "start";
printf("%s", p);
这是允许的。在这里,我们正在更改指针变量p
中存储的地址,以指向字符串start
的地址(再次提醒,start
也是文本段中的只读数据)。如果您想修改*p
中存在的值,请选择动态分配内存。
char *p = NULL;
p = malloc(sizeof(char)*7);
strcpy(p, "string");
p[0] = 'x'
操作,因为我们现在是在堆中写入。char *p = "string";
创建一个指向只读内存的指针,其中存储着字符串字面量"string"
。试图修改指向p
的字符串会导致未定义的行为。
char a[] = "string";
创建一个数组,并使用字符串字面量"string"
来初始化其内容。
它们存在存储内存的位置不同。理想情况下,第二个应该使用const char*。
第一个
char buf[] = "hello";
创建一个自动缓冲区,大小足以容纳字符并将它们复制进去(包括空终止符)。
第二个
const char * buf = "hello";
应该使用const并简单地创建一个指向通常存储在静态空间中的内存的指针,这是不合法的。
相反(你可以安全地修改第一个而不是第二个事实的相反),从函数返回第二个指针是安全的,但不是第一个。这是因为第二个指针将在函数范围之外保持有效的内存指针,而第一个则不会。
const char * sayHello()
{
const char * buf = "hello";
return buf; // valid
}
const char * sayHelloBroken()
{
char buf[] = "hello";
return buf; // invalid
}
a
声明了一个由 char
值组成的数组 -- 一个以 null 结尾的 char
数组。
p
声明了一个指针,它指向一个不可变的、以 null 结尾的 C 字符串,其确切的存储位置是实现定义的。请注意,这应该是带有 const
限定符的 (例如:const char *p = "string";
)。
如果你使用 std::cout << "a: " << sizeof(a) << "\np: " << sizeof(p) << std::endl;
将它打印出来,你会看到它们的大小有所不同 (注意:值可能因系统而异):
a: 7
p: 8
Here what is ? operator? Is it a part of a string or it has some specific meaning?
char a[] = ?string?
我猜它们曾经是双引号"string"
,可能被转换为“智能引号”,但在过程中无法表示为此类字符,因此被转换为?
。
char
缓冲区时才会很少使用。 - justinchar
缓冲区,则应该是[static] const char a[] = "xyz";
,而不是const char* p = "xyz";
或const char* const p = "xyz";
- 前者意味着p可能被移动到其他地方,如果这不是预期的结果,则最好不要允许这种可能性,并且两者都要求编译器为指针和文本分配空间 - 在我看来 - 只是显示了对编译器所要求的准确模型的缺乏,浪费了空间和时间在未优化的构建中。 - Tony Delroy[static] const char[]
如何导致“浪费空间和时间”。 - Tony DelroyC和C++具有非常相似的指针与数组关系...
我无法确定你所询问的两个语句的确切内存位置,但我发现这些文章很有趣,对于理解char指针声明与char数组声明之间的某些差异非常有用。
为了清晰明了:
我认为重要的是记住,在C和C++中,数组是指向数组第一个元素的常量指针。因此,您可以对数组执行指针算术运算。
char *p = “string”; <--- 这是一个指针,它指向字符字符串的第一个地址。
以下也是可能的:
char *p;
char a[] = "string";
p = a;
此时,p 现在引用 a 的第一个内存地址(即第一个元素的地址)
因此 *p == 's'
*(p++) == 't',以此类推。 (或 *(p+1) == 't')
对于 a,同样的事情也适用:*(a++) 或 *(a+1) 也等于 't'
#define ? "
。不知道是否可以编译通过。 - Residuum