Array[0] vs Pointer

Discussion in 'C Programming' started by Varun Tewari, Jul 17, 2013.

  1. Varun Tewari

    Varun Tewari Guest

    People,

    Hope everyone's here's rocking.

    I wrote a small code today, expecting something, but the result wasn't what I was expecting.

    here's the code

    void main()
    {
    int arr[] = {1,2,3,4};
    int *p = NULL;

    printf("\narr[0]: %x, &arr[0]: %x, &arr: %x, arr: %x", arr[0], &arr[0], &arr, arr);

    p = arr;
    printf("\np: %x, &p: %x", p, &p);
    }

    EXAMPLE: ONe execution on my machine
    arr[0]: 1, &arr[0]: bf8086cc, &arr: bf8086cc, arr: bf8086cc
    p: bf8086cc, &p: bf8086dc

    On executing this, as you probably know, all prints except the last one (&p) are same.
    Rest looks ok, how come &arr and arr are same here?
     
    Varun Tewari, Jul 17, 2013
    #1
    1. Advertisements

  2. "void main" is very useful, mostly as a way of detecting C books and
    tutorials written by someone who doesn't really know the language. "int
    main(void)" would be correct.
    You need a "#include <stdio.h>" at the top of your program.

    The "\n" should be at the end of the output line not at the beginning.

    The correct format to print a pointer value is "%p", not "%x", and it
    expects a void* argument, so a pointer of another type should be
    converted via a cast. "%x" is likely to work *if* unsigned int is the
    same size as a pointer, but it's risky and incorrect.

    Using "%x" for an int is ok (as long as it's non-negative), but it's
    really for unsigned int; unless you really need hexadecimal output (and
    for the value 1 it doesn't matter), just use "%d". (A special-case rule
    says that int and unsigned int are interchangeable as function arguments
    as long as the value is within the range of both types, which it is in
    this case.)
    Here's a corrected version of you program:

    #include <stdio.h>
    int main(void)
    {
    int arr[] = {1,2,3,4};
    int *p = NULL;

    printf("arr[0]: %d, &arr[0]: %p, &arr: %p, arr: %p\n",
    arr[0], (void*)&arr[0], (void*)&arr, (void*)arr);

    p = arr;
    printf("p: %p, &p: %p\n", (void*)p, (void*)&p);
    return 0;
    }

    and the output I get on my system:

    arr[0]: 1, &arr[0]: 0xbfbfd9c0, &arr: 0xbfbfd9c0, arr: 0xbfbfd9c0
    p: 0xbfbfd9c0, &p: 0xbfbfd9bc

    Quick answer: An array expression is, in most but not all contexts,
    implicitly converted to a pointer to the array's first element. The
    expression `arr` is converted to a pointer to `arr[0]`. `&arr` is the
    address of the whole array; it's the same memory address, but has a
    different type (pointer to array vs. pointer to int).

    The relationship between arrays and pointers in C is tricky and often
    counterintuitive. Section 6 of the comp.lang.c FAQ,
    <http://www.c-faq.com/>, explains it better than I have time to here.
    Read it and come back if you stil have questions.
     
    Keith Thompson, Jul 17, 2013
    #2
    1. Advertisements

  3. Varun Tewari

    James Kuyper Guest

    I had almost finished this when Keith's message showed up. There's a lot
    of overlap between our messages, but I decided to post this one anyway,
    because I take a slightly different tack to explaining the problems than
    Keith did.

    The variable arguments of that call to printf() have the types 'int',
    'int*', 'int (*)[4]', and 'int*', respectively.
    This time, the variable arguments have the types 'int*' and 'int**'.

    The 'x' specifier requires that the corresponding argument be an
    unsigned int. When one of printf()'s variable arguments has a promoted
    type that doesn't match the corresponding format specifier, the behavior
    (of your ENTIRE program) is undefined (7.21.6.1p9). That means that the
    standard imposes no requirements on what your program will do. If you
    had any expectations about what would happen when you executed your
    program - any expectations at all, such as that it won't start playing
    "The Star Spangled Banner" - those expectations were not justified by
    anything it says in the C standard.

    The C standard specifies only one format specifier for pointers, 'p'.
    The corresponding arguments must be of type (void*). To print a pointer
    of any other type, you need to cast it to void*.

    'int' is required to have the same size and alignment as 'unsigned int',
    and the same representation for positive 'int' values, so using '%x'
    with arr[0] is a little more reasonable than using it for the pointers -
    but it's still technically undefined behavior.

    Whether or not the last character of a text stream must be an '\n' is
    implementation-defined (7.21.2p2). Therefore, portable code should
    always write a '\n' at the end of text streams.
    Despite what I said above, on many implementations your code will
    actually work. The results that you actually got imply that, under the
    implementation of C that you're using, all of the different pointer
    types that you've tried to print out had exactly the same size as
    unsigned int. The standard allows incompatible pointer types to have
    different sizes, though it's quite common for all of them to be same.
    Even if they're all the same size, it's not guaranteed to be the same as
    the size of unsigned int. You just got unlucky - the defect in your code
    happened to have no ill effects, so you lost an opportunity to realize
    that it was defective.

    In most contexts, an lvalue of array type, such as 'arr', is
    automatically converted into a pointer to the first element of the
    array. One of the three exceptions to that rule is when it is an
    argument of the & operator. That's why &arr has the type int(*)[4],
    while arr has the type int*. &arr points at the start of 'arr', while
    arr points at the start of a[0]. Since a[0] is the very first element of
    arr, both pointers point at the same location in memory, which is why
    they printed out the same.
     
    James Kuyper, Jul 17, 2013
    #3
  4. Varun Tewari

    Varun Tewari Guest

    @Keith,@James,

    Sorry guys, I forgot to mention that sizoe(int) = sizeof(unsigned int) = sizeof(void *) = 4 on my machine.
    Yes, I indeed missed the %p aspect in the real code too. Thnx for pointing this.
     
    Varun Tewari, Jul 18, 2013
    #4
  5. Varun Tewari

    Les Cargill Guest

    If you use %x or %lx instead of %p, chances are it'll be fine. Since
    that sort of thing is very nearly always used in debug prints, it's less
    sinful. :) when else do you need to serialize pointers?
     
    Les Cargill, Jul 18, 2013
    #5
  6. Varun Tewari

    James Kuyper Guest

    Why use anything other than the correct specifier? It might work, but
    it's pretty unlikely to do so on any machine where sizeof(unsigned int)
    != sizeof(void*).
    If it ever goes wrong, which it could, you could end up wasting a lot
    more time trying to figure out why, than you would have spent simply
    typing in the correct format specifier. The last thing you need when
    you're debugging a problem is to be distracted by such issues.
     
    James Kuyper, Jul 18, 2013
    #6
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.
Similar Threads
There are no similar threads yet.
Loading...