【问题标题】:Difference between array of pointers and pointer to array?指针数组和指向数组的指针之间的区别?
【发布时间】:2020-01-03 23:30:40
【问题描述】:
 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,它似乎没有回答我的问题。

【问题讨论】:

    标签: c pointers pointer-to-pointer


    【解决方案1】:
    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 个chars 的数组。在我们的房地产类比中,这就像一张纸,上面写着“42 C 街,主卧室”。使用该信息,您可以找到房间并根据需要重新装修,就像在其他情况下一样。但是您也可以用定位器替换纸张,用于不同的房间,可能在不同的房子里,或者用随机文本,或者您甚至可以烧掉整个信封,而不会影响 C 街上的房间。

    string1 就其本身而言,是一个数组数组。它标识存储其元素的连续内存块。这些元素中的每一个本身都是一个由 4 个chars 组成的数组,顺便说一下,它恰好是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 运算符的操作数。

    【讨论】:

    • 那么主要的区别是内部工作是否正确(隐式转换和指针运算的顺序,如果有的话)?此外,“string1 衰减的指针类型与string1Ptr 的类型相同,这就是允许为后者提供的初始化的原因。”是我们几乎可以互换使用指针和数组的原因吗?另外对于您的最后两点:1)默认初始化可以帮助我的情况是什么? 2)“自动确定数组维度。”只指最高维度吧?
    • 我的意思是“指针和数组表示法几乎可以互换”
    • 不,@Leon,你不太明白。我们不会“几乎可以互换使用指针和数组表示法”。相反,如果我们完全使用数组,那么我们几乎不可避免地会将它们指针一起使用。特别是,索引运算符 ([]) 严格来说是 C 中的指针运算符——它与数组的使用通过从数组到指针的自动转换来操作。几乎没有直接在数组上的运算符——sizeof_Alignof&(地址)是唯一的。
    • 我在这个答案的中间花了很多篇幅解释不同表达式的评估以及为什么你的程序可以以不同的方式构造数据但产生相同的输出,但我 领导 主要区别在于:它们是关于数据的结构和布局。这些对于指针和数组的使用之间的区别有很多影响,其中一些更重要的我在最后详细说明。
    • (1) 默认初始化不适用于非static 的块范围变量。您可以对出现在文件范围内或static 的变量使用默认初始化。 (2) 是的,只有一个数组维度,即词汇最左边的维度,可以自动确定。请注意,在现实世界的程序中,一维数组的使用频率远远高于任何其他类型,而二维以上的数组很少出现。
    【解决方案2】:

    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 数组的下一行。

    【讨论】:

    • 如果我没记错的话,这似乎回答了“stringstring1 之间的差异”,而不是“stringstring1Ptr 之间的差异”这个问题。或者您是否试图暗示stringstring1Ptr 之间的区别在于string1Ptr 指向的内容可以修改,但您不能为string 这样做(基于您的最后一段)?如有错误,请见谅。
    • 我以为他在问所有这些。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    相关资源
    最近更新 更多