1

Is the same

char* s1[size];

To

char** s2 = malloc(size * sizeof(char*));

They have any difference?

cheroky
  • 755
  • 1
  • 8
  • 16
  • Yes, if you use `sizeof` or `_Alignof` on them, or take the address `&`. – EOF Jun 20 '16 at 22:22
  • They can both be used the same, but the first has automatic storage duration, while the second does not. There are also situations in which the first can be used that the second cannot. – Alyssa Haroldsen Jun 20 '16 at 22:35
  • 1
    @Kupiakos: Nonsense, they are different types and are not the same! – too honest for this site Jun 20 '16 at 23:15
  • 1
    Same as the difference between arr[] and *arr – user253751 Jun 20 '16 at 23:26
  • @Olaf More accurately, they can be used the same in many situations. – Alyssa Haroldsen Jun 21 '16 at 03:34
  • @Kupiakos: Most exactly: no, they can not be used the same! It is just that the array is **converted** to a _pointer to the first element_ for most usages. Due to this conversion, it looks **as if** they are used the same. Note that even the index-operator `[]` does not work on an array, but only a pointer. – too honest for this site Jun 21 '16 at 11:42
  • @Olaf I'm not saying they are the same. I'm stating they *can* used the same in many situations. The conversion to a pointer to the first element is the key here. The end result is the same for the majority of cases - they're still *used* the same by the programmer. When I use `someList[i]` with `int someList[N]`, I see an array being indexed, regardless of the details *I'm aware of*. There are many situations where the difference in type is significant - but the automatic decaying hides most of them. You say it only looks as if they're used the same - but they are being used the same. – Alyssa Haroldsen Jun 21 '16 at 16:01
  • @Kupiakos: They can not even be used in the same situations - technically. Read my comment very carrefully, then read the standard. You don't use the name of the array anymore once it has been converted to the pointer, but the pointer. This (correct) interpretation makes the whole array/pointer stuff much easier and straight-forward. – too honest for this site Jun 21 '16 at 16:04
  • @Olaf I've read your comment. I'm normally a big pedant about this stuff. Enough to, yes, read the standard. In terms of full usage in the language, you are 100% correct. I generally prefer to not overwhelm a beginner with details that will be irrelevant for the majority of situations. In retrospect, that *is* what the question's about. That's why I left it as a comment instead of elaborating with details in an answer. Unless one is programming embedded systems where taking advantage of the full power of the language is key - operations will be simple and the decaying is irrelevant. – Alyssa Haroldsen Jun 21 '16 at 16:12
  • @Kupiakos: Your approach will eventually result in more missunderstanding. By simply learning when an array-name is converted automatically and when not, all is learned about arrays. Next step is to learn how to use pointer, no arrays involved anymore. Sound much more reasonable to me. – too honest for this site Jun 21 '16 at 16:30
  • @Olaf And you're probably right. I likely shouldn't have mentioned how arrays can appear to be used similarly to arrays in a question dedicated to the difference. However, from helping people brand-new to programming, the details between the two are only confusing when the concept of a type itself isn't fully concrete. – Alyssa Haroldsen Jun 21 '16 at 16:50
  • @Kupiakos: Agreed there are more fundamental things to understand first. But that is true for almost all programming - there is always something more basic to know first. That is the reason every good progrmming books starts with integer types, not compound types. Anyway, this is no tutorial site and every asker here is required to know the basics at least to understand what her problem is. If that is not true (and there are many actually), they should just not try to run before they can crouch. – too honest for this site Jun 21 '16 at 17:06

4 Answers4

2

Theoretically, *arr[] and **arr are different. For example :

char *arr[size]; //case 1

Here arr is a an array of size size whose elements are of the type char*

Whereas,

char **arr; //case2

Here arr is itself a pointer to the type char*

Note: In case 1 array arr degrades to a pointer to become the type char** but it's not possible the other way around i.e, pointer in case 2 cannot become an array.

Cherubim
  • 5,287
  • 3
  • 20
  • 37
-1

There are few differences:

  • s1 is not an lvalue, so it cannot be modified (e.g. using assignment or increment operators). Because of this it resembles type char** const s1 which also does not allow modifications (but in this case this is caused by const modifier).
  • operator & used on address of array will return address of array (i.e. address of 1st element). When & will be used on variable, it will return its address:

    assert((void*)&s1 == (void*)s1);
    assert((void*)&s2 != (void*)s2);
    
  • sizeof() used on array will return array size, while sizeof() used on pointer will return pointer size - usually it will be the same as sizeof(void*), but C standard does not require this (see comments below):

    assert(sizeof(s1) == size * sizeof(char*));
    assert(sizeof(s1) == size * sizeof(s1[0])); // this is the same
    assert(sizeof(s2) == sizeof(void*)); // on some platforms this may fail
    
  • and of course obvious one - s1 is allocated on stack, s2 on heap. Because of this s1 will be destroyed automatically when execution leaves current scope, and s2 requires call to free to release memory.

Update: here is example code which checks above asserts:

#include <assert.h>
#include <stdlib.h>

int main()
{
    const int size = 22;

    char* s1[size];
    char** s2 = (char**)malloc(size * sizeof(char*));

    assert((void*)&s1 == (void*)s1);
    assert((void*)&s2 != (void*)s2);

    assert(sizeof(s1) == size * sizeof(char*));
    assert(sizeof(s1) == size * sizeof(s1[0])); // this is the same
    assert(sizeof(s2) == sizeof(void*)); // on some platforms this may fail

    free(s2);

    // Attempts to modify value
    char** const s3 = s1;
    ++s2; 
    //++s1; // compilation error - lvalue required as increment operand
    //++s3; // compilation error - increment of read-only variable ‘s3’

    return 0;
}
Daniel Frużyński
  • 2,091
  • 19
  • 28
  • The equality-operators in your `assert()`s for `&` are constraint violations. – EOF Jun 20 '16 at 23:15
  • That is plain wrong! An array is not a pointer is not an array. – too honest for this site Jun 20 '16 at 23:16
  • Ahh, I forgot about casting pointers to void*. I corrected this and added example code with these assertions. – Daniel Frużyński Jun 20 '16 at 23:24
  • 2
    Your claim that `type *arr[]` is equivalent to `type **const ptr` is still wrong. You'll find some useful reading in C11 draft standard n1570, *6.3 Conversions, 6.3.2.1 Lvalues, arrays, and function designators*. – EOF Jun 20 '16 at 23:33
  • My bad, I read this in a book that this is the case when I learnt C++ in the past and did not try to check if this was really true. Thanks for pointing this out for me, I learnt something new :). I updated my answer for this. – Daniel Frużyński Jun 20 '16 at 23:53
  • 1
    Note that size of pointer to `void` is not necessarily the same as `sizeof(char **)`. The C Standard only makes such provision for pointer to `char` (N1570 6.2.5/28): `A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.`. The other pointers' types may be of whatever size: `Pointers to other types need not have the same representation or alignment requirements.`. – Grzegorz Szpetkowski Jun 20 '16 at 23:59
-1
char* s1[size];

Is an array of pointers of type char that is allocated on the stack.

char** s2 = malloc(size * sizeof(char*));

Is a pointer of type char ** that is allocated on the stack but points to a dynamic array of pointers of type char * allocated on the heap.

The two differ in terms of scope and the usual difference between arrays and pointers.

machine_1
  • 4,266
  • 2
  • 21
  • 42
-1

s1 is an array, s2 is a pointer. s2 points to the first element of the malloced array.

The array s1 has automatic storage duration, while the array which s2 points to has dynamic storage duration.

Also, in C89 char* s1[size]; is valid only if size is a constant expression, because C89 doesn't support variable-length arrays.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157