3

In the below code, I made an integer array and tried printing it with the function DisplayIntArray in order to try to understand how pointers work.

Some questions are embedded in the comments.

/*==========================================================
 * pointer tests
 *========================================================*/

#include <stdio.h>

void DisplayIntArray(char *Name, int *Data, int lenArray) {
  /* Display integer array data */
  int m;
  printf("%s = \n", Name);
  for(m = 0; m < lenArray; m++, printf("\n")){
    printf("%d ", *(Data+m));
    printf("%d ", Data[m]);
    // how come these two print statements display the same thing?
  }
}

void DisplayDoubleArray(char *Name, double *Data, int lenArray) {
  /* Display double array data */
  int m;
  printf("%s = \n", Name);
  for(m = 0; m < lenArray; m++, printf("\n")){
    printf("%f ", *(Data+m));
    printf("%f ", Data[m]);
  }
}


int main ()
{

  int int_array[5] = {1, 2, 3, 4, 5};
  int *int_array_p = &int_array[0];

  // print array with function DisplayIntArray
  // this works fine
  DisplayIntArray("int array", int_array_p, 5);
  printf("\n");


  // Curiously the function still works when passing the actual
  // array instead of the pointer.
  DisplayIntArray("int array", int_array, 5);
  printf("\n");


  // print array using the wrong function
  // This print should fail because I'm passing an integer value pointer
  // into a double. But shouldn't it print the first element
  // correctly? - Why not?
  DisplayDoubleArray("int array", int_array_p, 5);
  printf("\n");


  return 0;
}

The output of the code is:

int array = 
1 1 
2 2 
3 3 
4 4 
5 5 

int array = 
1 1 
2 2 
3 3 
4 4 
5 5 

int array = 
0.000000 0.000000 
0.000000 0.000000 
0.000000 0.000000 
-0.000000 -0.000000 
0.000000 0.000000
tux3
  • 7,171
  • 6
  • 39
  • 51
MoonMan
  • 33
  • 5

5 Answers5

2

In C, if you have a pointer p that points to an array and integral value i, then, p[i] is the same as *(p+i). They both evaluate to the i-th element of the array.

Given that, it makes sense that

printf("%d ", *(Data+m));
printf("%d ", Data[m]);

print the same thing.

You said:

// Curiously the function still works when passing the actual
// array instead of the pointer.

When an array variable is used in an expression such as the call to DisplayIntArray, the variable evaluates to the pointer to the first element. Hence,

   DisplayIntArray("int array", int_array_p, 5);
   DisplayIntArray("int array", int_array, 5);

are equivalent.

You said:

// This print should fail because I'm passing an integer value pointer
// into a double. But shouldn't it print the first element
// correctly? - Why not?
DisplayDoubleArray("int array", int_array_p, 5);

Here, the argument type and variable being used in the argument don't match. Using gcc, I get the following warning:

soc.c: In function ‘main’:
soc.c:51:35: warning: passing argument 2 of ‘DisplayDoubleArray’ from incompatible pointer type
   DisplayDoubleArray("int array", int_array_p, 5);
                                   ^
soc.c:18:6: note: expected ‘double *’ but argument is of type ‘int *’
 void DisplayDoubleArray(char *Name, double *Data, int lenArray) {

With that call, the program should exhibit undefined behavior.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • Thanks. So, just to confirm, it doesn't matter that p is not an array? – MoonMan Jun 12 '15 at 18:31
  • @Will - in this case, yes. If `p` is a pointer to `T` and `a` is an N-element array of `T`, and `p` points to the first element of `a`, then `p[i]` and `a[i]` give the same result. – John Bode Jun 12 '15 at 18:36
  • 1
    @Will, `p` can be an array or a pointer to an array of elements. Both behave the same way when used as `p[N]` and `*(p+N)`. – R Sahu Jun 12 '15 at 18:40
2

printf("%d ", *(Data+m));
printf("%d ", Data[m]);
// how come these two print statements display the same thing?

Because they mean the same thing; Data[m] and m[Data] would both be equivalent to *(Data+m) here, they all refer to the same element.

// Curiously the function still works when passing the actual
// array instead of the pointer.

When you try to use an array in an expression like this one, it automatically "decays" into a pointer to the array, this means that you can not pass an array itself, only a pointer to it.

// print array using the wrong function
// This print should fail because I'm passing an integer value pointer
// into a double. But shouldn't it print the first element
// correctly? - Why not?

This is undefined behavior, in theory anything could happen. In practice, your compiler will most likely try to interpret the raw bit pattern of your int array as if it was a double array, and that happens to correspond to floating point values very close to 0.0.
It most likely won't print even the first element correctly, because an int and a double are stored differently in memory.

Community
  • 1
  • 1
tux3
  • 7,171
  • 6
  • 39
  • 51
  • Data[m] and m[Data] are the same? m is just an int, I don't quite see how you can index into it. – Jouan Jun 12 '15 at 19:41
  • 1
    @Jouan it's a q̶u̶i̶r̶k̶ feature of the language. Related: http://stackoverflow.com/questions/381542/with-c-arrays-why-is-it-the-case-that-a5-5a – tux3 Jun 12 '15 at 19:44
  • Wow, I had no idea. Makes sense though :) – Jouan Jun 12 '15 at 19:46
1
printf("%d ", *(Data+m));
printf("%d ", Data[m]);

how come these two print statements display the same thing?

They display the same thing because for a pointer p to an array of at least m + 1 elements, the expressions *(p + m) and p[m] mean exactly the same thing. They are 100% synonymous. You can take that as part of a definition of pointer arithmetic, if you like.


int int_array[5] = {1, 2, 3, 4, 5};
int *int_array_p = &int_array[0];

this works fine:

DisplayIntArray("int array", int_array_p, 5);

Curiously the function still works when passing the actual array:

DisplayIntArray("int array", int_array, 5);

Yes, because in most contexts, an array name is automatically converted to a pointer to the first element of the array. This dovetails with your previous question, by the way.


Shouldn't [this] print the first element correctly? - Why [does it] not?

DisplayDoubleArray("int array", int_array_p, 5);

In DisplayDoubleArray() you pretend that the pointer argument points to the bytes of the representation of a double, but it doesn't. Dereferencing the pointer therefore produces undefined behavior.

Supposing that the actual behavior observed is to produce a value of type double -- which is reasonably likely but does not need to be the case, as the behavior is undefined -- you then tell printf() that it is receiving a value of type float, instead. For that reason, the behavior of printf() is also undefined. Possibly printf() interprets the first sizeof(float) bytes of the representation of the (undefined) double value as if they were the representation of a float value, but again, undefined. In any case, the representations of double and float are certainly different from the representation of int, and they are normally different from each other, as well. There is no reason whatever to think that any element of the array should be printed correctly this way.

Furthermore, the representation of type double is probably larger than that of type int in your system (8-byte doubles and 4-byte ints are common). If so, then executing DisplayDoubleArray() will attempt to access past the end of your underlying array unless some of the other aforementioned undefined behavior prevents it from doing so. Accessing past the end of the array also produces undefined behavior.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

Most is due to the fact that an array name is implicitly converted to a pointer to the first element of the array.

See the comments below each cited block for additional details.

printf("%d ", *(Data+m));
printf("%d ", Data[m]);
// how come these two print statements display the same thing?

That is pointer arithmetic. Du to the conversion of array names, indexing into an array is identical to adding the index to a pointer to the first element of an array (which can be the name of the array itself).

// Curiously the function still works when passing the actual
// array instead of the pointer.
DisplayIntArray("int array", int_array, 5);

See above.

// print array using the wrong function
// This print should fail because I'm passing an integer value pointer
// into a double. But shouldn't it print the first element   
// correctly? - Why not?  
DisplayDoubleArray("int array", int_array_p, 5);

It should generate a compiler warning about implicit pointer conversion. If not, enable warnings, if still nothing appears, trash your compiler or kick the vendor. Converting one pointer to another is only guaranteed to convert it back without loss of information. Any access to the converted pointer is undefined behaviour. That means anything can happen. Your computer might grow teeth and bite you. Speculating what might happen is worthless, just don't do it!

Just FYI: the binary representation of int and float/double differs significantly to no surprise.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
0
int *int_array_p = &int_array[0];

Is only useful if you want to manipulate memory addresses independent of the array scope. Otherwise you must know that arrays decay to a pointer to the first element of the array.

Therefore &int_array[0]; is same as just int_array. They both return the array's offset address. You don't have to use the reference operator & as it returns "address of" and pointers are already an "address of"


double is guaranteed to be max 8 bytes while int is guaranteed to be max 4 bytes (on x64 bit architectures excluding some rare cases). Giving reference to an integer data type to a pointer to double is undefined behavior and modern compilers will give you a warning. However if you request the value to be printed as %i or %d it might return the correct result for you, otherwise it reads larger memory areas and depending on your endianness it will give you a variety of unintended results.

Imobilis
  • 1,475
  • 8
  • 29
  • 1
    "while int is guaranteed to be max 4 bytes" That is wrong and depends on the actual architecture. Even for x64, there are models which use 64 bit `int`s. "However if you ..." Do not speculate about UB! Just avoid it. – too honest for this site Jun 12 '15 at 18:42
  • He asked specifically and he needs to know everything. We are not teachers right. 64 bit integers in x64 bit systems is exclusively rare case though. – Imobilis Jun 12 '15 at 18:44
  • 1
    It is allowed by the standard. Any restriction you impose is just beyond the standard and that will lead nowhere. (Expecially as that statement is plain unnecessary). And, yes, once we answer a question, we actually _are_ teachers. And as such, we should stick to to given constraints and not provide false information. – too honest for this site Jun 12 '15 at 18:49
  • Also note that `double` is not necessarily required to be [ISO60559](http://port70.net/~nsz/c/c11/n1570.html#F) compliant. An implementation can choose to use the same format as float or an even wider format (or none at all). It basically depends on `__STDC_IEC_559__` being present. However, if you assume that, double **is exactly 64 bits wide**, not just "max". – too honest for this site Jun 12 '15 at 18:57
  • Thanks, Malina. Your suggestion about switching to %d didn't work. But your comment did illuminate what was happening. I see that I was asking the system to read and print some 8byte-thing when, really, I only wanted the first 4byte-thing. Or something strange like that, hence, undefined behavior. Thanks. – MoonMan Jun 12 '15 at 19:03
  • @Olaf `printf` uses `%f` for both the float and double. lol we are? By definition there is no way we could be teachers. Informally.. yes but that doesn't change the thing. – Imobilis Jun 12 '15 at 19:08
  • No, it does not. `"%f"` does never take a float, but a double. What else is adoes a teacher? That word has a wider meaning than in "school teacher" where he can add pressure. – too honest for this site Jun 12 '15 at 19:12
  • @Olaf this is what I meant Olaf, it works for both on that way. If we take it literally.. it is someone who teach. I am not in condition to explain that though considering that I am not a native speaker. – Imobilis Jun 12 '15 at 19:13
  • You do not get the point. It _does_ not work for both, as it only takes `double`. So it does not work for both. The conversion is done when the expression is evaluated by the caller, so the callee does not even see a `float`. But if you cast/convert a pointer, you will not have any value conversion, but a reinterpretation of the bit-pattern. – too honest for this site Jun 12 '15 at 19:18
  • Well then it must be subjective - system-specific then. On my system it clearly works for both. And that with EiC compiler and gcc. And I've seen in several occasions sources to claim that it works for both. It is just like that: First you believe on your own experience then someone else's experience. But I will do an additional research. – Imobilis Jun 12 '15 at 19:23
  • That is the essence of [**undefined behaviour**](http://port70.net/~nsz/c/c11/n1570.html#3.4.3p1)! The set "anything" does include "it **behaves** as expected". That is the worst that can happen, as you do not notice something went wrong! Once you are in the area of UB, you are lost. If one of my students would present some code which exhibits UB, I would reject this, however brilliant it might be and even if it shows proper behaviour during a demonstration. And you will be thankful, if you are sitting a plane driven by software written by him lateron. – too honest for this site Jun 12 '15 at 19:32
  • Engineering is not a matter of "believe". That is the field of religion. But, yes, if I was that imprecise, I would also pray when sitting in the aforementioned plane. – too honest for this site Jun 12 '15 at 19:35
  • Well everyone has his own interpretation of the movement. But yes I am aware that UB can also make it *look* correct. It is a matter of additional research as it appears. – Imobilis Jun 12 '15 at 19:38
  • @Olaf Lol what did you wanted me to say? I said I will do a research and that leads to nowhere? What leads to somewhere then. – Imobilis Jun 12 '15 at 19:44
  • You were'nt clear about _who_ has to do some research. Sorry if I missunderstood. That sounded like a common phrase like "well, we might all be right to some extend", which is a trigger phrase for me, as there is a clear standard and there is not "partly" here. – too honest for this site Jun 12 '15 at 20:13
  • @Olaf Well to me it looks like you are making your life harder by making such an.. unneeded analysis over my straightforward speech. All I meant is that I must be the one who must re-read this "standard". I simply don't trust anyone it is nothing personal. Even if I listen to someone for something and he was wrong, I will still be the one who had mistaken. – Imobilis Jun 12 '15 at 20:18