char string1[3][4]={"koo","kid","kav"}; //This is a 2D array
char * string[3]={"koo","kid","kav"}; //This is an array of 3 pointers pointing to 1D array as strings are stored as arrays in memory
char (*string1Ptr)[4]=string1; //This is a pointer to a 1D array of 4 characters
除此之外,string
可以指向任何大小的字符串string1Ptr
只能指向大小为 4 的字符串(否则
指针算术会出错),我没有任何区别
他们。
它们是绝对的、根本上的不同,但 C 费尽心思向你隐藏这种区别。
string
is an array。它标识存储其元素的连续内存块。这些元素恰好是类型char *
在此示例中,但这只是一个相对较小的细节。我们可以将这里比作一栋包含多个房间的房子——这些房间实际上是房子的一部分,并且存在于房子的物理边界之内。我可以随心所欲地装饰房间,但它们始终是那所房子的房间。
string1Ptr
is a pointer。它标识一个内存块,其内容描述如何访问另一个不同的内存块,其中包含 4 个数组char
s 驻留。在我们的房地产类比中,这就像一张纸,上面写着“42 C Street,主卧室”。使用该信息,您可以找到房间并根据需要重新装饰它,就像其他情况一样。但你也可以用不同房间的定位器(也许在不同的房子里)或随机文本替换纸张,或者你甚至可以烧掉整个信封,而不会影响 C 街的房间。
string1
就其本身而言,是一个数组的数组。它标识存储其元素的连续内存块。每个元素本身就是一个由 4 组成的数组char
s,顺便说一句,它恰好是对象的类型string1Ptr
可以指点一下。
例如,
printf("%s\n", string1[2]); // All print the same thing, ie, the word "kav"
printf("%s\n", string1Ptr[2]);
printf("%s\n", string[2]);
它们似乎都执行相同的指针算术。(我的原因
假设string
and string1Ptr
除了以下方面几乎相似
我上面说的区别)
... and that is where C hiding the distinction comes in. One of the essential things to understand about C arrays is that in nearly all expressions,* values of array type are silently and automatically converted to pointers [to the array's first element]. This is sometimes called pointer "decay". The indexing operator is thus an operator on pointers, not on arrays, and indeed it does have similar behavior in your three examples. In fact, the pointer type to which string1
decays is the same as the type of string1Ptr
, which is why the initialization you present for the latter is permitted.
但您应该明白,这三种情况下操作的逻辑顺序并不相同。首先,考虑
printf("%s\n", string1Ptr[2]);
Here, string1Ptr
是一个指针,索引运算符直接适用于该指针。结果相当于*(string1Ptr + 2)
,其类型为char[4]
。作为数组类型的值,它被转换为指向第一个元素的指针(导致char *
).
现在考虑
printf("%s\n", string1[2]);
string1
是一个数组,因此首先将其转换为指向其第一个元素的指针,从而得到一个类型的值char(*)[4]
。这与以下类型相同string1Ptr1
,并且如上所述进行相应的评估。
但这一个有点不同:
printf("%s\n", string[2]);
Here, string
是一个指针,因此索引操作直接应用于它。结果相当于*(string + 2)
,其类型为char *
。不执行自动转换。
有什么理由使用其中一种而不是另一种?
很多,双向的,具体取决于您当时的特定需求。一般来说,指针更灵活,特别是因为它们需要使用动态分配的内存。但他们却面临着以下问题
- 指针可能在范围内,但不指向任何东西,并且
- 声明一个指针并不会创建任何它指向的东西。还,
- 即使一个指针在程序执行过程中一度指向某个东西,并且它的值随后没有被程序写入,它仍然可以停止指向任何东西。 (这通常是由于指针比它所指向的对象寿命更长的结果。)
此外,它既可以是优点,也可以是缺点
- 指针在其生命周期内可以任意次数地被自由地指定为指向新对象。
一般来说,数组更容易用于多种用途:
- 声明数组为其所有元素分配空间。您可以选择在声明时为它们指定初始值,或者在某些(但不是全部)情况下利用默认初始化。
- 数组的标识符是有效的,并且引用数组位于范围内的任何位置。
- 或者,如果提供了初始值设定项,则数组声明可以使用它来自动确定数组维度。
* But only nearly all. There are a few exceptions, with the most important being the operand of a sizeof
operator.