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

时间:2020-01-03 23:30:40

标签: c pointers pointer-to-pointer

 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,但似乎没有回答我的问题。

2 个答案:

答案 0 :(得分:4)

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是一个数组。它标识一块连续存储器,其中存储了其元素。在此示例中,这些元素恰好是char *类型,但这是一个相对较小的细节。在这里可以比喻一个包含多个房间的房子-房间实际上是房子的一部分,并存在于房子的物理边界之内。我可以随意装饰房间,但它们始终是那间房子的房间。

string1Ptr指针。它标识了一块内存,其内容描述了如何访问其中包含4个char数组的另一个不同的内存块。在我们的房地产类比中,这就像一张纸,上面写着“ 42 C Street,master bedroom”。与其他情况一样,使用该信息,您可以找到房间并重新装修。但是,您也可以用定位器替换纸张,以放置在不同的房间(可能在另一间房子里),或者使用随机文本,或者甚至可以烧毁整个信封,而不会影响C街上的房间。

string1本身是一个数组数组。它标识存储其元素的连续内存块。这些元素中的每个元素本身都是4个char的数组,偶然地,它们恰好是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]);

它们似乎都执行相同的指针算术。(我的理由是 假设stringstring1Ptr除了 我上面所说的差异)

...这就是C隐藏区别的地方。关于C数组的基本知识之一是,几乎在所有表达式中,数组类型的 * 值都是自动无声的转换指向指针[指向数组的第一个元素]。有时称为指针“衰减”。因此,索引操作符是指针操作符,而不是数组操作符,在您的三个示例中,它确实具有相似的行为。实际上,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 *。不执行自动转换。

有什么理由要使用另一个?

根据您当时的特定需求,双向都有很多选择。一般来说,指针更灵活,特别是在使用动态分配的内存时需要使用指针。但是他们遭受的问题是

  • 指针可能在范围内,但没有指向任何内容,并且
  • 声明指针不会为其指向创建任何内容。还有,
  • 即使指针在程序执行期间一次指向某个对象,并且其值随后未被程序写入,它仍然可以停止指向任何内容。 (这通常是指针超出其指向的对象的结果。)

此外,

  • 可以自由分配一个指针,以在其生命周期内任意次数指向一个新对象。

通常来说,数组在许多方面都更易于使用:

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

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

答案 1 :(得分:0)

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的2D数组,可以保证是连续的。 string1Ptr是一个指向4个字符的数组的指针,当您对其进行索引时,将以该字符数递增,这将使您到达string1数组的下一行。

相关问题