1

Hello I am new to C and its pointers. I thought I understood pointers untill I stumbled upon Array of Pointers.

Why is the output of these two code fragements exactly the same. I would expect that my normal array gives me the values. And the array that holds of pointers gives me the addresse of the values.

char *array[] = {'a','b','c','d'};
    for(int i=0; i<4; i++){
        printf("%c\n", array[i]);
}
char array[] = {'a','b','c','d'};
    for(int i=0; i<4; i++){
        printf("%c\n", array[i]);
}

I know that '[]' is used to dereference and get the value of the adress the pointer is pointing at but it is also used to access array elements and the only reasonable explanation here is that it does both at the same time. Is this how I should think about it?

4
  • 5
    enable warnings in your compiler Commented May 12, 2021 at 20:23
  • yeah I am new to this ide i didint realize I caused so many warnings. Commented May 12, 2021 at 21:23
  • 1
    *enable errors in your compiler. This code is a constraint violation and it will help you to get an error message. Commented May 12, 2021 at 21:41
  • 1
    They made us use codeblocks its a real downgrade to what I am used to. The warnings were there I just didint notice them. Commented May 12, 2021 at 22:39

5 Answers 5

2

In the first code snippet, you are initializing an array of pointers with character constants. This results in an integer-to-pointer conversion of those constants. So for example the first element of the array contains the address 97 (assuming ASCII encoding).

When you later attempt to print, you are passing a char * where a char is expected. Using the wrong format specifier triggers undefined behavior. One of the ways that UB can manifest is that things appear to work properly which is the case here.

What probably happened is that pointers and integers get passed to functions in the same manner. And if your system uses little-endian byte representation (which it appears it does), it will end up reading the value used to initialize the array.

Regarding the array index operator [], the expression E1[E2] is exactly the same as *((E1) + (E2)). In the first code snippet array[i] has type char * while in the second code snippet it has type char because that is the type of the respective array elements.

Sign up to request clarification or add additional context in comments.

15 Comments

The typical system these days has 64 bit addresses so that the data type passed is different from the data type extracted via var_args or the like inside the printf implementation. Little endian is indeed the reason for the same value. The test case would be to do printf("%c %c %c %c\n", array[0], array[1], array[2], array[3] which may produce the output a ^@ b ^@ c ^@ (if the output chain ending at the terminal displays binary zeros as ^@).
Thanks for your very usefull answer. I now see why char *array[] = {'a','b','c','d'}; makes no sense at all. Cause my pointers should receive adresses as their values. And the reason why I did this is because I acctually confused it with char *wort[] = {"a", "b", "b", "d"}; which apperently is valid but I dont understand why.
@NoCodeNoBlunder char *wort[] = {"a", "b", "b", "d"} is valid because double quotes are for string constants which have type char[] which decays to char *.
@NoCodeNoBlunder Yes, an array of pointers behaves like a 2D array but is not the same. Each of the pointers may point to disjoint areas of memory of differing size, while a true 2D array is contiguous and all subarrays are the same size.
@NoCodeNoBlunder It does host multiple char *. In your example, wort[0] holds the address of the first byte of the string "a".
|
1

Like already explained by dbush, your first example is wrong. A more practical use case to demonstrate the pointer part with a string:

 #include <stdio.h>

 int main(int argc, char** argv) {
     char *str = "abc"; //string literal - constant!
     printf("%p address of str[0] - str[0] = %c \n", str, *str);
     printf("%p address of str[1] - str[1] = %c \n", str+1, *(str+1));
     printf("%p addredd of str[2] - str[2] = %c \n", &str[2], str[2]); //alternative notation
 return 0;
 } 

Also there are several similarities between arrays and pointers, for the string example you can not change the string (the string literal is constant), but you can change the pointer to another string. Other with an array, where you can change the content, but you can not iterate over the array with ++arr, like you can do with the pointer notation, nor can you change the address of arr. Another fact, the array name is the address of the array. And yeah its the same address as the first array element.

With time you will find several useful cases for arrays of pointers, because you already know, that switching the pointer (the adresses of the object the pointer points to) often provide a more performant method than using the objects themselves...

Comments

0

I would expect that my normal array gives me the values. And the array that holds of pointers gives me the addresse of the values.

You put the same data into each array, so it's not surprising that you got the same data out of them. Specifically:

char *array[] = {'a','b','c','d'};

In this case, you created an array containing four values, and you told the compiler that those values are of type char *. But whatever their type, they're still the same values, 'a', 'b', and so on. You might as well have written:

char *array[] = {97, 98, 99, 100);

because that's exactly the same thing. Specifying the type of the contents of the array as char * doesn't make the compiler treat the values in the array differently -- it doesn't say "oh, I guess in this case I should take the addresses of those values instead of using the values themselves" just because the type is char *. If you want an array of pointers to some set of values, you'll need to get those pointers yourself using other means, such as the & operator, allocating space for each one, etc.

Note: In a comment below, M.M. points out that using values of type char to initialize an array of type char * isn't allowed by the C standard. In my experience, compilers typically warn about this kind of thing (you should've gotten several warnings like warning: incompatible integer to pointer conversion initializing 'char *' with an expression of type 'int' when you compiled your code), but they'll still soldier on and compile the code. In summary: 1) Don't do that, and 2) different compilers may do different things in this situation.

I know that '[]' is used to dereference and get the value of the address the pointer is pointing at but it is also used to access array elements and the only reasonable explanation here is that it does both at the same time.

An array in C is a single contiguous piece of memory in which a number of values, all of the same type and size, are arranged one after another. The [] operator accesses individual values within the array by calculating an offset from the array's base address and using that to get the value. I think the thing that's confusing in your example is that you've created an array of char *, but with values that look like char. As far as the compiler is concerned, though, 'a' (a.k.a. 97, a.k.a. 0x61) is an acceptable value for a pointer to a character, and you could dereference that and get whatever character is stored at location 0x61. (In reality, doing that might cause an exception; the lowest region of memory is reserved on many machines.)

2 Comments

@M.M Noted. However, my compiler (clang 12.0) compiles the code just fine even though it emits warnings (warning: incompatible integer to pointer conversion initializing 'char *' with an expression of type 'int'), and a test program is consistent with the behavior I described. I believe that the OP's question is less about what the C standard does or doesn't define and more about what the [] operator does and how the two snippets could possibly do the same thing, and I think I've answered that.
@M.M Added a note that I think gets that across.
0

I know that [] is used to dereference and get the value of the adress the pointer is pointing at but it is also used to access array elements and the only reasonable explanation here is that it does both at the same time. Is this how I should think about it?

No. [] is used (in the place you use it) to indicate an array in which the initializer will state the number of elements the array has. Derreferencing a pointer is done with the * left unary operator.

The expression

char *array[] = ...

is used to declare an array of pointers to chars, that will be initialized (normally) with a list of string literals, like these:

char *array[] = { "First string", "Second string", "third string" };

and

char array[] = { 'a', 'b', 'c', 'd' };

making use of the information given above, declares an array of characters with space for four characters, and initialized with array[0] = 'a', array[1] = 'b', array[2] = 'c', array[3] = 'd'.

So the first example will give you an error when trying to initialize an array cell (of pointer to char type) with an integer value (a char is a type of integer) and the second example will compile and execute fine.

The reason you get the same output in both case is unknown(Undefined Behaviour) (well I have some idea) because you have assigned to a pointer variable an integer value (this is probably the thing as the value stored is small enough that can be reinterpreted back without losing information) but the thing is that you have stored the same thing in the array cells, so why do you ask why the output is the same..... what did you expect? You store a char value in a pointer variable and then get it back and print in both samples with exactly the same format specifier, why do you expect (except for conversion errors back to an integer) both codes printing different things? (well, it's undefined behaviour, so you can expect anything and the computer can do otherwise)

Comments

0

There are two compiler warnings you elegantly skip:

 warning: initialization of 'char *' from 'int' makes pointer from 
integer without a cast [-Wint-conversion]
    6 | char *array[] = {'a','b','c','d'};
      |                  ^~~

And the second warning:

warning: format '%c' expects argument of type 'int', but argument 2
 has type 'char *' [-Wformat=]
    8 |         printf("%c\n", array[i]);
      |                 ~^     ~~~~~~~~
      |                  |          |
      |                  int        char *
      |                 %s

The suggested %s of course would not help. To get valid char-pointers into array[] it would take a "..." string literal, a malloc or a & adress-of on char variable.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.