Thread: C arrays and pointers etc. Help!

  1. #1
    Registered User
    Join Date
    Jul 2008
    Posts
    15

    C arrays and pointers etc. Help!

    I have the code

    Code:
    int k = 2;
    int a[10];
    int b = 0;
    
    for(b=0;b<10;b++)
    {
    a[b] = 2;
    }
    
    printf("Normally with a[k] we get %d\n", a[k]);
    
    int c;
    
    c = *(int *) ((int)&a[0] + k*sizeof(int));
    
    printf("So c = %d\n", c);
    Where I'm just printing out a value from the array "a". However, I don't get why

    Code:
    c = *(int *) ((int)&a[0] + k*sizeof(int));
    works exactly the same as a[k].

    I've been told

    *(int *) = indirect
    (int)&a[0] = base address
    k*sizeof(int) = offset

    I know that the end bit selects which value in the array I want (how many objects away from the start of the array the thing I want is), but I don't get what the other two parts do.

    Thanks

  2. #2
    * noops's Avatar
    Join Date
    Jun 2008
    Posts
    108
    Does this help?

    & = address of
    (int *) = cast to pointer to int
    (int) = cast to int
    * = dereference (get the value at address)

    I think I have that right.

  3. #3
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    When you add an integer (say, 4) to a pointer, C will not just add 4 bytes to your pointer. Rather, it scales the integer to the size of the pointed-to object, so if you added 4 to a pointer to int, you get the fourth int, not the fourth byte.

    This is why the code is casting &a[0] (a pointer) to an int: now when an integer is added, scaling is not done. But casting a pointer to an int is really not a good idea. To achieve what this code is trying to do, you want to cast to a pointer to char. A char is a byte, so no scaling is needed, and the conversion to a char pointer is much less dicey than the conversion to an int. So, with that modification, here's what's going on:
    Code:
    int a[10] = { 0, 1, 2 };
    int k = 2;
    
    /* Now I'll break it down into some rather useless
     * C statements for illustrative purposes.
     */
    
    /* This is a pointer to the first element of your array.
     * The pointer has type int *.
     */
    &a[0];
    /* This expression is also a pointer to the first element,
     * but more properly, to the first byte of the first element.
     * It has type char *.
     */
    (char *)&a[0];
    /* Now k * sizeof(int) bytes are being added to the char
     * pointer.  The resulting value is a pointer to the "k"th
     * element of the array (since k is 2, this is the 2nd element).
     */
    (char *)&a[0] + k * sizeof(int);
    /* Great, we have a pointer to the 2nd element.  But our
     * pointer has type char *, which means if we try to 
     * dereference it, we'll only get a single byte.  So, convert
     * the pointer to an int *, so a dereference will get the
     * whole int.
     */
    (int *)((char *)&a[0] + k * sizeof(int));
    /* And finally, the dereference. */
    *(int *)((char *)&a[0] + k * sizeof(int));
    printf("%d\n", *(int *)((char *)&a[0] + k * sizeof(int)));
    Pointing a char * at any object (like I've done above) is legal, at least in C99. Converting it back to an int * I'm not so sure about, but an implementation would probably have to go to some extra effort to prevent you from doing it. It's not too useful, of course, since we have the [] operator for array access.

  4. #4
    Ugly C Lover audinue's Avatar
    Join Date
    Jun 2008
    Location
    Indonesia
    Posts
    489
    That awesome car!

  5. #5
    Registered User
    Join Date
    Jul 2008
    Posts
    15
    Cool

    So when you have

    Code:
    *(int *)
    at the beginning of the code what is the (int*) bit doing? Does the * bit at the very biginning just mean "print out the value at the memory location [whatever]"?

    Also, what is the difference between

    Code:
    (char *)&a[0];
    and

    Code:
    (char )&a[0];
    without the * ?

    I'm still learning C, so sorry if my questions seem basic/idiotic.

    Thanks!

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    First question: pretty much. The asterisk (*) is the pointer dereference operator in C. For example:
    Code:
    int i = 5;
    int *p;
    p = &i;
    *p = 4;
    Here the * operator tells the compiler to look at the value of the variable p and treat that value as an address, and then store the value 4 into that location in memory.

    Basically, *p and i refer to the same thing.

    The reverse of * is &, which takes the address of a variable. &i is the address in memory where i is located.

    Also, what is the difference between
    Code:
    (char *)&a[0];
    and
    Code:
    (char )&a[0];
    without the * ?
    A world of a difference. The first tries to convert &a[0] (which is an int *) into a char *, that is, a pointer-to-char. The second tries to convert it to an ordinary char; since most memory addresses are outside the range of a char (128 for a max, generally), it will almost certainly cause an overflow.

    Perhaps you should read a few tutorials about pointers. Just stick something like "c pointer tutorial" into Google and you'll find something. There are even some on this site. cprogramming.com/tutorial.html
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  7. #7
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    lets say I have an array of int's
    Code:
    int foo[5];
    char a;
    and I want to access the LSB bytes in that array for sme reason, I can get those by

    Code:
    a = *(char*)&foo[1];
    this takes the address of f[1] and recasts it to a char* then takes the data at that address, which being a char pointer is a char and on a little endian machine will be the LSB of the original int foo[1].

    I can then convert that value back in to an int by
    Code:
    foo[1] = (int)a;
    so now foo[1] is equal to the LSB of the original value it held.

  8. #8
    Registered User
    Join Date
    Jul 2008
    Posts
    29
    Ok, this is quite stupid.

    int foo[5];

    foo IS A POINTER!! Test it out, try inserting printf("%d.\n", foo). You'll see that this prints an address, not a value.

    There is no need to cast foo since it is a pointer. That is, foo is the same as &foo[0]. Lets look at whats going on in a bit more depth. When the compiler sees this declaration it creates an array of 5 ints on the stack. Foo is assigned the address of the first int. The compiler knows that the ints are side by side in memory so, knowing the first and the size allows it to get the rest of them. So when it gets an index such as foo[1] you can think of it as the compiler changing the pointer to point to the correct thing and then finding that value. So how does it do this? It takes the base (which is foo) and adding the offset (which is index * the size of the array element, in our case an int) and then finding the value at that memory location. So what does that look like in code? It is * (foo + 1 * sizeof(int)). Look familiar? Thats the uncluttered version of what you have above. foo is the base. 1*sizeof(int) is the offset (note that the *sizeof(int) part is only what happens under the hood, in code what would work is *(foo + 1) (see below for more details) the compiler automatically adds the *sizeof(int) and the star at the beginning is a dereference. So lets look at foo[0]. This evaluates to *(foo + 0 * sizeof(int)) which is the same as *foo. Then you did an & which gets the address of the variable so &foo[0] is the same as &*foo which is the same as foo (since the address of a dereference is the variable).

    Now, the rest is an error which happens to not have an effect on your system. You took this pointer and casted it to an int. On some systems, the size of an int is 4 bytes and the size of a pointer is 8 bytes. This would cause the compiler to truncate the result. You then took that int and recast it to a pointer and tried to dereference it. This works given that the size of an int is greater than or equal to the size of an pointer (in our case a pointer to an int).

    So now, the only reason that cas casted (hehe) the pointer to a char * is because the sizeof a char is 1 byte. The reason this matters is that when you are adding an int to a pointer the int gets scaled by the size of the type of the pointer. So, adding 2 to an int* is really adding 2*sizeof(int) to the pointer. So this means that *(foo + 1) is the same as foo[1] (because the compiler adds the *sizeof(int) bit. The better way to think about it is as actually adding *sizeof(*foo). The compiler would evaluate and find that foo points to an int and then replace *foo with int.

    Hope this clears some things up.
    chacham15

  9. #9
    Registered User
    Join Date
    Oct 2001
    Posts
    2,129
    > Converting it back to an int * I'm not so sure about

    char * and void * have the same representation and alignment, so you could just cast to void * and then cast to int *.

    I think.

  10. #10
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    char * and void * have the same representation and alignment, so you could just cast to void * and then cast to int *.
    Well, the problem as far as I see it is that the standard doesn't guarantee that a conversion back to the original type will be meaningful if it's been modified. It's not so much a matter of types as it is values.

    Realistically, it should be fine. Technically it's probably not legal.

  11. #11
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > foo IS A POINTER!!
    Bzzt - wrong, no it isn't.
    http://c-faq.com/aryptr/index.html
    It's true that in many places where you use foo, it decays into "pointer to int", but that doesn't make foo itself "pointer to int". foo (as you've written it) is an "array of 5 int"

    Not convinced?
    Code:
    void func ( int *a ) {
      printf( "%ld\n", sizeof(a) );
    }
    int main ( ) {
      int foo[5];
      int *bar = foo; // now this IS a pointer
      printf( "%ld\n", sizeof(foo) );
      func( foo );
      printf( "%ld\n", sizeof(bar) );
      func( bar );
      return 0;
    }
    Your suggestion that foo is just a pointer would have these producing the same answer.


    > c = *(int *) ((int)&a[0] + k*sizeof(int));
    If some book is teaching you this, then get another book.
    Casting a pointer to an int doesn't work on some machines.
    IMO, you're learning a bunch of bad habits, and you've only just started.

    As cas says, the only pointer cast which is guaranteed is the round trip T* to void* and back to T*
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  12. #12
    Registered User
    Join Date
    Jul 2008
    Posts
    29
    Quote Originally Posted by Salem View Post
    > foo IS A POINTER!!
    Bzzt - wrong, no it isn't.
    http://c-faq.com/aryptr/index.html
    It's true that in many places where you use foo, it decays into "pointer to int", but that doesn't make foo itself "pointer to int". foo (as you've written it) is an "array of 5 int"

    Not convinced?
    Code:
    void func ( int *a ) {
      printf( "%ld\n", sizeof(a) );
    }
    int main ( ) {
      int foo[5];
      int *bar = foo; // now this IS a pointer
      printf( "%ld\n", sizeof(foo) );
      func( foo );
      printf( "%ld\n", sizeof(bar) );
      func( bar );
      return 0;
    }
    Your suggestion that foo is just a pointer would have these producing the same answer.


    > c = *(int *) ((int)&a[0] + k*sizeof(int));
    If some book is teaching you this, then get another book.
    Casting a pointer to an int doesn't work on some machines.
    IMO, you're learning a bunch of bad habits, and you've only just started.

    As cas says, the only pointer cast which is guaranteed is the round trip T* to void* and back to T*
    Bzzt - wrong, no it isn't.

    Wow. Just wow. What you are missing is that sizeof is a preprocessor directive. That means that in the function all the preprocessor knows is that the argument is a int* so it will place the size of the pointer there. The same with sizeof(bar). The problem is sizeof(foo). While internally the compiler treats foo as an int* the preprocessor realizes that its an array and returns the size of the array*sizeof(int). A lot of things go on before the compiler has its way or even that that compiler does. For example, creating a 2D array. There is actually no such thing (unless you manually create it with pointers). So, what does the compiler actually do with that? It creates a 1D array and places the next element of the 2d array right after the first. Then when you do a double index, the first index uses sizeof(array)*sizeof(element in array) while the second index uses sizeof(element in array) so you then get a single index into a larger array.

    Casting a pointer to an int doesn't work on some machines.
    Bzzt - wrong.

    Casting a pointer to an int works on all machines. On some machines this will drop some data, but its not a compile error. Plus, I've said this in my previous post.

    If you dont believe me about the foo being a pointer i suggest you try and do what I said (or at least explain what is going on, as I have in your situation). In case you forgot, that is do a printf("%x", foo); and see what that gets you.

  13. #13
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Salem is right, you are wrong.

    1. sizeof() is not a preprocessor directive. It is compiler proper - that's why you can't do something like
    Code:
    #if sizeof(a) == sizeof(b) 
    // do something here
    #else
    // do something else
    #endif
    That won't compile, because sizeof is not resolved at pre-processor time.

    2. You can CERTAINLY create a 2D, 3D or 4D array:
    Code:
    int oned[10];
    int twod[10][10];
    int threed[10][10][10];
    int fourd[10][10][10][10];
    3.
    Casting a pointer to an int doesn't work on some machines.
    Bzzt - wrong.
    Yes, of course, you can cast it to an integer - but the result is not particularly meaningful for the purpose of converting it BACK to a pointer. For example if you drop the upper 32 bits of a 64-bit pointer and then convert that back to a 64-bit pointer, it is quite likely no longer a valid pointer, and it will certainly no longer point to the original data [unless the upper bits were all 0 or all 1 - meaning that the pointer was within the first or last 2GB of memory - and many OS's purposely make sure that addresses of 64-bit applications are OUTSIDE of the 2GB at either end to catch applications making that mistake].

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  14. #14
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > Casting a pointer to an int works on all machines. On some machines this will drop some data
    How can that ever be acceptable?

    Even on ancient DOS, when you had 16 bit integers and 32 bit far pointers, all that you got was a world of pain.

    As for the rest of your wibble on sizeof, go read a book!


    > If you dont believe me about the foo being a pointer i suggest you try and do what I said (or at least explain what is going on, as I have in your situation).
    Because that's one of the situations in which an array name decays to a pointer.
    But just because it does, DOES NOT IMMEDIATELY MAKE the whole thing a pointer.
    Did you bother to read the c.l.c FAQ, I bet you didn't.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  15. #15
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    C performs special additional mathematical changes when a pointer of any type is being added to, in order to make up for the size of each element. C actually distinguishes between arrays and pointers in this regard and will adjust accordingly. This is usually discovered when dealing with multidimensional arrays.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. pointers & arrays and realloc!
    By zesty in forum C Programming
    Replies: 14
    Last Post: 01-19-2008, 04:24 PM
  2. Pointers and multi dimensional arrays
    By andrea72 in forum C++ Programming
    Replies: 5
    Last Post: 01-23-2007, 04:49 PM
  3. Passing pointers to arrays of char arrays
    By bobthebullet990 in forum C Programming
    Replies: 5
    Last Post: 03-31-2006, 05:31 AM
  4. pointers
    By InvariantLoop in forum C Programming
    Replies: 13
    Last Post: 02-04-2005, 09:32 AM
  5. Help understanding arrays and pointers
    By James00 in forum C Programming
    Replies: 2
    Last Post: 05-27-2003, 01:41 AM