C/C++ int[]与int*(指针与数组表示法)有何区别?

63

我知道在C语言中,数组只是按顺序存储的数据的指针。但是,在使用[]和*符号时,有什么不同之处?我的意思是在所有可能的使用上下文中。

例如:

char c[] = "test";

如果您在函数体中提供此指令,则会在堆栈上分配该字符串。

char* c = "test";

将指向一个数据(只读)段。

你能列出这两种表示法在所有使用情况下的区别,以形成清晰的概览吗?


在C语言中,数组不是指针,除非作为参数传递。至于你的例子,请考虑当c是数组和指针时的sizeof c。顺便说一句,在阅读了你的整个问题后,我不认为我准备好列出所有的差异,因为它们基本上是不同的类型,就是这样。你可能需要阅读标准。 - Michael Krelin - hacker
2
尽管标题完全相同,但这个问题实际上更具信息量。我们应该重新打开它! - ltjax
@ltjax 你说得对。是我太心急了。已投票重新开放。 - Mysticial
3
char* c = "test"; 这段代码是非标准的,正确的写法应该是 const char* c = "test";。请注意,翻译后的内容保持原意不变且易于理解,不包含任何额外的解释或信息。 - Sergey K.
可能是重复的问题 - BlueRaja - Danny Pflughoeft
5个回答

40
根据C99标准:
数组类型描述了一组具有特定成员对象类型(称为元素类型)的连续分配的非空对象集。
数组类型由其元素类型和数组中元素的数量所特征化。如果其元素类型是T,则称数组类型派生自其元素类型,并且有时称其为“T的数组”。从元素类型构造数组类型称为“数组类型派生”。
指针类型可以从函数类型、对象类型或不完整类型(称为引用类型)派生而来。指针类型描述了一个对象,其值提供对引用类型实体的引用。从引用类型T派生的指针类型有时被称为“指向T的指针”。从引用类型构造指针类型称为“指针类型派生”。
根据标准声明。
char s[] = "abc", t[3] = "abc";
char s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

是相同的。数组的内容是可修改的。另一方面,声明

const char *p = "abc";

定义了类型为指向常量char的指针的p,并将其初始化为指向一个类型为常量字符数组在C++中)的对象,该数组长度为4,其元素用字符字符串字面值进行初始化。如果尝试使用p修改数组内容,则行为未定义。

根据6.3.2.1 数组下标,解引用和数组下标是相同的:

下标运算符[]的定义是E1[E2]等同于(*((E1)+(E2)))

数组和指针的区别在于:

  • 指针没有关于其后面的内存大小的信息(没有可移植的方法来获取它)
  • 无法构造不完整类型的数组
  • 指针类型可以派生自不完整类型
  • 指针可以定义递归结构(这是前两个的结果)

关于这个主题,更多有用的信息可以在http://www.cplusplus.com/forum/articles/9/找到。


4
“constant pointer to char”应该翻译为“指向常量字符的指针”,因为指针可以被修改,但它所指向的内容是常量。 - elmo
2
在C++中,它将初始化并指向一个具有“char常量数组”类型的对象。但在C语言中不是这样。在C语言中,对象“abc”的类型为char [4]。然而,试图修改它会导致未定义行为。可能需要指出这种差异。 - Daniel Fischer

13
char c[] = "test";
这将创建一个包含字符串“test”的数组,因此您可以修改/更改任何字符。
c[2] = 'p';
但是,
char * c = "test"

这是一个字符串字面量——它是一个const char类型。
因此,对这个字符串字面量进行任何修改都会导致段错误。所以

c[2] = 'p';

现在是非法的,并且会导致段错误。


4
char * c = "test" 这段 C 代码是不好的,会产生警告:deprecated conversion from string constant to 'char*' [-Wwrite-strings]。应该使用 const char*。请改为:const char* c = "test" - Sergey K.

4

char []表示“未知长度的char数组”,而char *表示“指向char的指针”。正如您所观察到的,当类型为“未知长度的char数组”的变量定义被初始化为字符串文字时,类型将转换为“array[N] of char”,其中N是适当的大小。同样适用于从数组聚合进行初始化的情况:

int arr[] = { 0, 1, 2 };

arr 被转换为类型 "array[3] of int"。

在用户定义类型的定义中(如 structclassunion),C++ 中禁止使用未知大小的数组类型,虽然在某些版本的 C 语言中,它们被允许作为结构体的最后一个成员,以便可以访问超出结构体末尾分配的内存;这种用法称为"灵活数组"。

递归类型构造是另一个区别;可以构造指向和数组类型的 char *(例如,char **char (*)[10]),但是对于未知大小的数组就不行了;不能写成 char []*char [][10](尽管 char (*)[]char [10][] 是可以的)。

最后,cv-qualifiying 的操作方式也不同;给定 typedef char *ptr_to_chartypedef char array_of_unknown_bound_of_char[],限定指针版本的行为与预期相同,而限定数组版本将使 cv-qualification 迁移到元素类型上:也就是说,const array_of_unknown_bound_of_char 等效于 const char [],而不是虚构的 char (const) []。这意味着在函数定义中,在构造原型之前将对参数执行数组指针衰减。

void foo (int const a[]) {
    a = 0;
}

是合法的;没有办法使未知边界数组参数不可修改。


1
如果您知道声明指针变量并不会创建它所指向的变量类型,而是创建一个指针变量,那么整个过程就会变得清晰明了。
因此,在实践中,如果您需要一个字符串,则需要指定一个字符数组,稍后可以使用指针。

const char *p = "abc"; 创建了一个数组。 - Mehdi

0

实际上,数组等同于常量指针

此外,char c[]为数组分配内存,其基地址为c本身。没有单独的内存用于存储该地址。

写char *c会为字符串分配内存,其基地址存储在c中。同时,还使用单独的内存位置来存储c。


4
我们显然都知道我们在谈论什么,但是说它们是等效的是鲁莽的。数组会衰变成指针,但这并不意味着它们和指针相同。同样地,即使有隐式转换,MyClassint也不是等价的。 - tenfour

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