字符 a[] = ?字符串?有什么区别;和字符 *p = ?字符串?;

What is the difference between char a[] = ?string?; and char *p = ?string?;?

本文关键字:字符 字符串 区别 什么      更新时间:2023-10-16

正如标题所说,两者之间有什么区别

char a[] = ?string?; and 
char *p = ?string?;  

这个问题是在采访中问我的。我什至不明白这句话。

char a[] = ?string?

这里?运算符是什么?它是字符串的一部分还是具有某些特定的含义?

?似乎是一个错别字,它在语义上无效。因此,答案假设?是一个错别字,并解释了面试官可能真正想问的问题。

<小时 />

首先,两者明显不同:

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

请继续阅读以获取更详细的说明:

阵列版本:

char a[] = "string";  

创建一个足够大的数组,以保存字符串文本"字符串",包括其NULL终止符。数组string使用字符串文本"string"进行初始化。数组可以在以后修改。此外,数组的大小即使在编译时也是已知的,因此可以使用sizeof运算符来确定其大小。

<小时 />

指针版本:

char *p  = "string"; 

创建一个指向字符串文本"字符串"的指针。这比数组版本更快,但指针指向的字符串不应更改,因为它位于只读实现定义的内存中。修改此类字符串文本会导致未定义的行为

事实上,C++03 弃用[Ref 1] 使用没有 const 关键字的字符串文字。所以声明应该是:

const char *p = "string";

此外,您需要使用 strlen() 函数,而不是sizeof来查找字符串的大小,因为 sizeof 运算符只会为您提供指针变量的大小。

<小时 />哪个版本

更好,我应该使用哪个版本?

取决于使用情况。

  • 如果不需要对字符串进行任何更改,请使用指针版本。
  • 如果要更改数据,请使用数组版本。
<小时 />

注意:这不是C++,但这是特定于 C 语言的。

请注意,使用不带 const 关键字的字符串文字在 C 中是完全有效的。但是,修改字符串文本仍然是 C[Ref 2] 中的未定义行为。

这就提出了一个有趣的问题,
在 C 语言中与字符串文字一起使用时,char* 和常量 char* 有什么区别?

<小时 />

对于斯坦德德斯球迷:
[参考文献 1]C++03 标准: §4.2/2

不是宽字符串文本的

字符串文本 (2.13.4( 可以转换为类型为"指向字符的指针"的右值;宽字符串文本可以转换为类型为"指向wchar_t的指针"的右值。在任一情况下,结果都是指向数组第一个元素的指针。仅当存在显式的适当指针目标类型时,才考虑此转换,而不是在一般需要从左值转换为右值时考虑此转换。[注意:此转换已弃用。见附件D。为了在重载分辨率 (13.3.3.1.1( 中进行排名,此转换被视为数组到指针的转换,然后是限定转换 (4.4(。[示例:"abc"作为数组到指针的转换转换为"指向常量字符的指针",然后转换为"指针到字符"作为限定转换。

C++11 只是删除了上述引文,暗示它是 C++11 中的非法代码。

[参考文献 2]C99 标准 6.4.5/5 "字符串文字 - 语义":

在转换阶段 7 中,值为零的字节或代码将追加到由一个或多个字符串文本生成的每个多字节字符序列。然后,使用多字节字符序列初始化静态存储持续时间和长度刚好足以包含该序列的数组。对于字符串文本,数组元素的类型为 char,并使用多字节字符序列的各个字节进行初始化;对于宽字符串文本,数组元素的类型为 wchar_t,并使用宽字符序列进行初始化...

如果这些数组

的元素具有适当的值,则无法指定这些数组是否不同。如果程序尝试修改此类数组,则行为未定义。

第一个是数组,另一个是指针。

数组声明char a[6];请求留出六个字符的空间,以名称 a 表示。也就是说,有一个名为 a 的位置,六个角色可以坐在该位置。另一方面,指针声明char *p;请求一个保存指针的位置。指针的名称为p,并且可以指向任何地方的任何字符(或连续的字符数组(。

声明

 char a[] = "string";
 char *p = "string"; 

将产生可以像这样表示的数据结构:

     +---+---+---+---+---+---+----+
  a: | s | t | r | i | n | g |  |
     +---+---+---+---+---+---+----+
     +-----+     +---+---+---+---+---+---+---+ 
  p: |  *======> | s | t | r | i | n | g | |    
     +-----+     +---+---+---+---+---+---+---+ 

重要的是要认识到,像x[3]这样的引用会根据x是数组还是指针生成不同的代码。给定上面的声明,当编译器看到表达式a[3]时,它会发出代码以从位置a开始,将三个元素移过它,然后在那里获取字符。当它看到表达式p[3]时,它会发出代码以从位置p开始,在那里获取指针值,向指针添加三个元素大小,最后获取指向的字符。在上面的例子中,a[3]p[3]恰好是字符l,但编译器到达那里的方式不同。

来源: comp.lang.c 常见问题列表 ·问题 6.2<</strong>

div class="answers">
char a[] = "string";

这会在堆栈上分配字符串。

char *p = "string";

这会在堆栈上创建一个指针,该指针指向进程数据段中的文本。

?写它的人不知道他们在做什么。

堆栈、堆、数据段(和 BSS(和文本分隔是进程内存的四个段。定义的所有局部变量都将在堆栈中。使用 malloccalloc 以动态方式分配的内存将位于堆中。所有全局变量和静态变量都将位于数据段中。文本段将具有程序的汇编代码和一些常量。

在这 4 个部分中,文本段是READ ONLY段,在所有其他三个部分中用于READWRITE

char a[] = "string"; - 此 statemnt 将为堆栈中的 7 个字节分配内存(因为局部变量(,并将保留所有 6 个字符(s, t, r, i, n, g(加上 NULL 字符 ( ( 在末尾。

char *p = "string"; - 此语句将为堆栈中的 4 个字节(如果是 32 位机器(分配内存(因为这也是一个局部变量(,它将保存值为 "string" 的常量字符串的指针。这个 6 字节的常量字符串将位于文本段中。这是一个常量值。指针变量p只指向该字符串。

现在a[0](索引可以是 0 到 5(意味着,它将访问堆栈中该字符串的第一个字符。所以我们也可以在这个位置写作。 a[0] = 'x' .允许此操作是因为我们在堆栈中具有READ WRITE访问权限。

但是p[0] = 'x'会导致崩溃,因为我们只能READ访问文本隔离。如果我们对文本段进行任何写入,就会发生分段错误。

但是你可以改变变量p的值,因为它的局部变量在堆栈中。

char *p = "string";
printf("%s", p);
p = "start";
printf("%s", p);

这是允许的。在这里,我们将存储在指针变量中的地址更改为字符串start p的地址(同样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"初始化其内容。

它们在内存存储位置方面确实有所不同。理想情况下,第二个应该使用常量字符 *。

第一个

char buf[] = "hello";

创建一个足够大的自动缓冲区来容纳字符并将它们复制到其中(包括 null 终止符(。

第二个

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值数组 - 一个终止的char数组。

p声明一个指针,该指针引用一个不可变的、终止的 C 字符串,其确切的存储位置由实现定义。请注意,这应该是const合格的(例如 const char *p = "string"; (。

如果使用 std::cout << "a: " << sizeof(a) << "np: " << sizeof(p) << std::endl; 打印出来,您将看到大小的差异(注意:值可能因系统而异(:

a: 7
p: 8

这里是什么? 运算符?它是字符串的一部分还是具有某些特定的含义?

char a[] = ?string?

我假设它们曾经是双引号"string",它们可能被转换为"智能引号",然后在此过程中无法表示,并被转换为?

C 和 C++ 具有非常相似的指向数组的指针关系...

我不能说你问的两个语句的确切内存位置,但我发现它们的文章很有趣,对于理解char指针声明和char数组声明之间的一些差异很有用。

为清楚起见:

C 指针和数组关系

C++指向数组的指针

我认为重要的是要记住,在 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">