char*和int*的区别

10
什么是char*int*之间的区别?它们是不同的类型,但我怎么能够这样写?
char* s1="hello world";

as

"hello world"

它不是一个字符,而是字符数组,我无法写成

*s1

as

char* s1 = {'h','e','l','l','o',' ','w','o','r','l','d'};

并且

int* a = {2,3,1,45,6};

有什么不同之处吗?


1
字符串字面值的特殊处理,即 ".." 这个东西。理论上,我认为你也可以将其用于其他类型的初始化(类似于 C99 复合字面量)。 - dyp
1
@user2485710 不,{..} 不是一个对象,甚至不是一个表达式,也没有类型。它是一个 花括号初始化列表 - dyp
1
@user2485710 例如,可以查看https://dev59.com/E5Tjs4cB2Jgan1zn4ba7。在某些情况下,可以使用花括号初始化列表来构造`initializer_list`,但它是一个更通用的结构。 - dyp
1
实际上,你的第一行也是不被允许的。C++曾经支持非const指向字符串字面值的指针以保持与C的兼容性,但现在不再支持了。请使用**const** char *s1 = "hello world";代替。 - Ben Voigt
显示剩余7条评论
2个回答

8
很简单:一个字符串常量,例如"foobar"被编译为一个字符数组,存储在程序的静态区域(即所有常量都存储的区域),并以空字符结尾。将其赋值给变量只是将指向该内存的指针赋给变量。例如,const char* a = "foo"; 将把存储"foo"的地址赋给a
简而言之,字符串常量已经携带了它所要存储的内存。
相比之下,使用初始化列表(即花括号中的元素列表)初始化指针未被定义。非正式地说,与字符串常量不同,使用初始化列表的问题在于它没有自己的内存,因此我们必须提供初始化列表可以存储其字符的内存。这可以通过声明数组而不是指针来完成。以下代码可以编译通过:
char s1[11]={'h','e','l','l','o',' ','w','o','r','l','d'}

现在,我们通过将 s1 声明为数组来提供字符存储空间。
请注意,您可以使用大括号初始化指针,例如:
char* c2 = {nullptr};

然而,尽管语法看起来相等,这是一种完全不同的初始化方式,称为统一初始化,并且将仅使用 nullptr 初始化c2

我认为你可以让 int *a = {1,2,3,4}; 与复合字面量类似地工作。然而,字符串字面量总是具有静态存储期;因此,在块作用域内使用 int *a = {1,2,3,4}; 而不是 int a[] = {1,2,3,4}; 没有太多好处(由于可变性,它需要具有自动存储期)。另一方面,int const a* = {1,2,3,4}; 可以再次具有静态存储期。但我不认为你可以轻松地将其扩展到类类型。 - dyp
1
简而言之,字符串常量已经将其存储位置的内存带入其中。与其他文字不同,我们不通过值来存储字符串文字。也许是因为这会花费更多的内存?或者因为你不能将它们保留在寄存器中/编码成汇编指令?我认为这导致了能够将它们存储为指针的要求。 - dyp
这个答案的第二部分是错误的,brace-init-list可以与指针一起使用,正如我在我的答案中所演示的那样,而且带有自己的内存的整个概念是没有意义的。赋值失败的结果是因为它们违反了shall约束。 - Shafik Yaghmour
@ShafikYaghmour:当然,您可以在指针上使用统一初始化语法。但是在这里,我们正在讨论将指针初始化为指向元素数组的情况,也就是所提到的“(即花括号内的元素列表)”,而您无法使用指针进行此操作。因此,尽管语法相似,但语义完全不同。 - gexicide
@ShafikYaghmour:“带有自己的内存的整个概念是没有意义的。”为什么?虽然这种说法相当不正式,但它确实是完全有道理的,也是为什么这是不可能的原因。当然,“规范”并没有说明为什么要按照某种方式指定某些内容,它只是使用“X应该……Y”。但规范中的每一件事都有其原因,在这种情况下就是这样。 - gexicide

3
在第一个情况中,字符串字面量会衰减为指向const char的指针。尽管s1应该是const char *,但一些编译器允许将其作为扩展形式。
const char* s1 = "hello world" ;

一个字符串字面量是一个const char组成的数组,我们可以从draft C++ standard2.14.5String literals中看到这一点,该节说道(接下来强调我的重点):

普通字符串字面量和UTF-8字符串字面量也称为窄字符串字面量。 窄字符串字面量具有类型“n const char的数组”,其中n是下面定义的字符串的大小,并具有静态存储期(3.7)。

数组到指针的转换在第4.2Array-to-pointer conversion中介绍,该节说:

[...] 具有类型“类型的数组”的表达式被转换为具有类型“指向数组对象的初始元素的指针”的表达式,并且不是lvalue。[...]

您的其他案例无法工作,因为标量只能用花括号内的单个元素进行初始化,这可以是算术类型、枚举类型或指针类型。这在C++标准草案第8.5.1节中有所涵盖,其中第3段提到:

“对象或类型T的引用的列表初始化定义如下:”

然后列举了不同的情况,适用于此情况的右侧的唯一情况是以下符号:

“否则,如果初始化程序列表具有E类型的单个元素,并且T不是引用类型或其引用类型与E相关,则对象或引用从该元素初始化; 如果需要缩小转换(见下文)将元素转换为T,则程序是非法的。”

这要求列表只有一个元素,否则将应用最终的符号:

“否则,程序是非法的。”
在您的两种情况下,即使将初始化器减少为一个变量,类型也是不正确的。h是一个char,2是int,无法转换为指针。
可以通过将结果分配给数组来使赋值工作,例如以下方式:
  char s1[] = { 'h', 'e', 'l', 'l', 'o',' ', 'w', 'o', 'r', 'l', 'd' } ;
  int  a[]  = { 2, 3, 1, 45, 6 } ;

这将在第 8.5.1 部分 集合 中涉及,其内容如下:

An array of unknown size initialized with a brace-enclosed initializer-list containing n initializer-clauses, where n shall be greater than zero, is defined as having n elements (8.3.4). [ Example:

int x[] = { 1, 3, 5 };

declares and initializes x as a one-dimensional array that has three elements since no size was specified and there are three initializers. —end example ] An empty initializer list {} shall not be used as the initializer-clause for an array of unknown bound.104

注意:

说指针不能使用花括号初始化列表是不正确的,它可以完全用于指针:

int x   = 10 ;
int *ip =  &x ;
int *a  = {nullptr} ;
int *b  = {ip} ;

也许我通过提到C99和复合字面量来欺骗了你和AndreyT? - dyp
@dyp,这完全是可能的,我可能会稍后编辑并附上我的原始C99答案作为参考。 - Shafik Yaghmour

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