Difference between array of pointers and pointer to array?

前提是你 提交于 2021-02-08 03:29:28

问题


 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

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 see any differences between them.

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)

So what are the differences between string and string1Ptr? Any reason to use one over the other?

PS: I'm a newbie so please go easy on me. Also, I did check C pointer to array/array of pointers disambiguation, it didn't seem to answer my question.


回答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

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.

They are absolutely, fundamentally different, but C goes to some trouble to hide the distinction from you.

string is an array. It identifies a block of contiguous memory wherein its elements are stored. Those elements happen to be of type char * in this example, but that's a relatively minor detail. One can draw an analogy here to a house containing several rooms -- the rooms are physically part of and exist inside the physical boundaries of the house. I can decorate the rooms however I want, but they always remain the rooms of that house.

string1Ptr is a pointer. It identifies a chunk of memory whose contents describe how to access another, different chunk of memory wherein an array of 4 chars resides. In our real estate analogy, this is like a piece of paper on which is written "42 C Street, master bedroom". Using that information, you can find the room and redecorate it as you like, just as in the other case. But you can also replace the paper with a locator for a different room, maybe in a different house, or with random text, or you can even burn the whole envelope, without any of that affecting the room on C Street.

string1, for its part, is an array of arrays. It identifies a block of contiguous memory where its elements are stored. Each of those elements is itself an array of 4 chars, which, incidentally, happens to be just the type of object to which string1Ptr can point.

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)

... 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.

But you should understand that the logical sequence of operations is not the same in those three cases. First, consider

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

Here, string1Ptr is a pointer, to which the indexing operator is directly applicable. The result is equivalent to *(string1Ptr + 2), which has type char[4]. As a value of array type, that is converted to a pointer to the first element (resulting in a char *).

Now consider

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

string1 is an array, so first it is converted to a pointer to its first element, resulting in a value of type char(*)[4]. This is the same type as string1Ptr1, and evaluation proceeds accordingly, as described above.

But this one is a bit more different:

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

Here, string is a pointer, so the indexing operation applies directly to it. The result is equivalent to *(string + 2), which has type char *. No automatic conversions are performed.

Any reason to use one over the other?

Many, in both directions, depending on your particular needs at the time. Generally speaking, pointers are more flexible, especially in that they are required for working with dynamically allocated memory. But they suffer from the issues that

  • a pointer may be in scope, but not point to anything, and
  • declaring a pointer does not create anything for it to point to. Also,
  • even if a pointer points to something at one time during an execution of the program, and its value is not subsequently written by the program, it can nevertheless stop pointing to anything. (This most often is a result of the pointer outliving the object to which it points.)

Additionally, it can be be both an advantage and a disadvantage that

  • a pointer can freely be assigned to point to a new object, any number of times during its lifetime.

Generally speaking, arrays are easier to use for many purposes:

  • declaring an array allocates space for all its elements. You may optionally specify initial values for them at the point of declaration, or in some (but not all) cases avail yourself of default initialization.
  • the identifier of an array is valid and refers to the array wherever it is in scope.
  • Optionally, if an initializer is provided then an array declaration can use it to automatically determine the array dimension(s).

* But only nearly all. There are a few exceptions, with the most important being the operand of a sizeof operator.




回答2:


The difference between string1 and string is the same as the difference between:

char s1[4] = "foo";
char *s2 = "foo";

s1 is a writable array of 4 characters, s2 is a pointer to a string literal, which is not writable. See Why do I get a segmentation fault when writing to a string initialized with "char *s" but not "char s[]"?.

So in your example, it's OK to do string1[0][0] = 'f'; to change string1[0] to "foo", but string[0][0] = 'f'; causes undefined behavior.

Also, since string is an array of pointers, you can reassign those pointers, e.g. string[0] = "abc";. You can't assign to string1[0] because the elements are arrays, not pointers, just as you can't reassign s1.

The reason that string1Ptr works is because the string1 is a 2D array of char, which is guaranteed to be contiguous. string1Ptr is a pointer to an array of 4 characters, and when you index it you increment by that number of characters, which gets you to the next row of the string1 array.



来源:https://stackoverflow.com/questions/59586561/difference-between-array-of-pointers-and-pointer-to-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!