指针数组和指向数组的指针之间的区别?

Difference between array of pointers and pointer to array?

 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

//I want to know differences between string1Ptr(pointer to array mentioned in question) and string(array of pointers mentioned in question). I only typed string1 here to give string1Ptr an address to strings

除了 string 可以指向任何大小的字符串而 string1Ptr 只能指向大小为 4 的字符串(否则指针运算会出错)之外,我看不到它们之间的任何差异。

例如,

   printf("%s\n", string1[2]);   // All print the same thing, ie, the word "kav"
   printf("%s\n", string1Ptr[2]);
   printf("%s\n", string[2]);

它们似乎都执行相同的指针运算。(我假设 stringstring1Ptr 的原因除了我上面提到的差异之外几乎相似)

那么string和string1Ptr有什么区别呢?有什么理由使用一个而不是另一个吗?

PS: 我是新手所以请放轻松。 另外,我确实检查了C pointer to array/array of pointers disambiguation,它似乎没有回答我的问题。

string1string的区别与

的区别相同
char s1[4] = "foo";
char *s2 = "foo";

s1是4个字符的可写数组,s2是指向字符串字面量的指针,不可写。参见 Why do I get a segmentation fault when writing to a string initialized with "char *s" but not "char s[]"?

因此在您的示例中,可以执行 string1[0][0] = 'f';string1[0] 更改为 "foo",但 string[0][0] = 'f'; 会导致未定义的行为。

此外,由于 string 是一个指针数组,您可以重新分配这些指针,例如string[0] = "abc";。您不能分配给 string1[0] 因为元素是数组,而不是指针,就像您不能重新分配 s1.

string1Ptr 起作用的原因是因为 string1 是一个 char 的二维数组,保证是连续的。 string1Ptr 是一个指向 4 个字符数组的指针,当您对其进行索引时,您会增加该字符数,这将使您到达 string1 数组的下一行。

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

Besides the fact that string can point to strings of any size and string1Ptr can only point to strings of size 4 only(otherwise pointer arithmetic would go wrong), I don't any differences between them.

它们完全不同,根本不同,但 C 会费些力气向您隐藏区别。

string 是一个 数组 。它标识存储其元素的连续内存块。在此示例中,这些元素恰好属于 char * 类型,但这是一个相对次要的细节。人们可以在这里类比一个包含多个房间的房子——这些房间在物理上是房子的物理边界的一部分,并且存在于房子的物理边界内。我可以随心所欲地装饰房间,但它们永远是那个房子的房间。

string1Ptr 是一个 指针 。它标识一块内存,其内容描述如何访问另一个不同的内存块,其中驻留一个 4 char 的数组。在我们的房地产比喻中,这就像一张纸,上面写着“42 C 街,主卧室”。使用这些信息,您可以找到房间并根据需要重新装修它,就像在其他情况下一样。但是你也可以用不同房间的定位器替换纸张,也许在不同的房子里,或者用随机文本,或者你甚至可以烧掉整个信封,而不会影响 C 街的房间。

string1,就其本身而言,是一个数组的数组。它标识存储其元素的连续内存块。这些元素中的每一个本身都是一个 4 char 的数组,顺便说一下,这恰好是 string1Ptr 可以指向的对象类型。

For example,

printf("%s\n", string1[2]);   // All print the same thing, ie, the word "kav"
printf("%s\n", string1Ptr[2]);
printf("%s\n", string[2]);

They all seem to perform the same pointer arithmetic.(My reason for assuming string and string1Ptr are almost similar besides for the difference I stated above)

... 这就是 C 隐藏区别的地方。了解 C 数组的基本内容之一是,在几乎所有表达式中,* 数组的值type 会默默地自动 converted 为指针 [指向数组的第一个元素]。这有时称为指针 "decay"。因此,索引运算符是指针上的运算符,而不是数组上的运算符,实际上它在您的三个示例中确实具有类似的行为。事实上,string1 衰减到的指针类型与 string1Ptr 的类型相同,这就是为什么你为后者提供的初始化是允许的。

但是你应该明白,在这三种情况下,操作的逻辑顺序是不一样的。首先考虑

printf("%s\n", string1Ptr[2]);

这里,string1Ptr是一个指针,索引运算符直接适用于该指针。结果等效于 *(string1Ptr + 2),其类型为 char[4]。作为数组类型的值,它被转换为指向第一个元素的指针(导致 char *)。

现在考虑

printf("%s\n", string1[2]);

string1 是一个数组,因此首先将其转换为指向其第一个元素的指针,从而产生 char(*)[4] 类型的值。这与 string1Ptr1 的类型相同,并且评估会相应地进行,如上所述。

但是这个有点不同:

printf("%s\n", string[2]);

这里,string是一个指针,所以直接对其进行索引操作。结果等效于 *(string + 2),其类型为 char *。不执行自动转换。

Any reason to use one over the other?

很多,双向的,这取决于你当时的特殊需要。一般来说,指针更灵活,尤其是在处理动态分配的内存时需要指针。但他们遭受的问题是

  • 指针可能在范围内,但不指向任何东西,并且
  • 声明一个指针不会为它创建任何指向。另外,
  • 即使一个指针在程序执行期间一次指向某物,并且它的值随后没有被程序写入,它仍然可以停止指向任何东西。 (这通常是指针比它指向的对象寿命更长的结果。)

此外,

既可以是优势也可以是劣势
  • 一个指针可以在其生命周期内任意多次分配以指向一个新对象。

一般来说,数组更容易用于许多用途:

  • 声明数组会为其所有元素分配 space。您可以选择在声明时为它们指定初始值,或者在某些(但不是全部)情况下使用默认初始化。
  • 数组的标识符是有效的,并且在范围内的任何地方都引用数组。
  • 可选地,如果提供了初始值设定项,则数组声明可以使用它来自动确定数组维度。

* 但只有 几乎 全部。有一些例外,最重要的是 sizeof 运算符的操作数。