views:

399

answers:

12

How does a pointer points to [-1]th index of the array produce legal output everytime. What is actually happening in the pointer assignment?

#include<stdio.h>
int main()
{
        int realarray[10];
        int *array = &realarray[-1];

        printf("%p\n", (void *)array);
        return 0;
}

Code output:

manav@workstation:~/knr$ gcc -Wall -pedantic ptr.c
manav@workstation:~/knr$ ./a.out
0xbf841140

EDIT: If this scenario is valid, then can i use this to define an array whose index start from 1 instead of 0, namely: array[1], array[2],...

+1  A: 

It simply points to the address of the item just ahead of the array in memory.

The array can simply be thought of as being a pointer. This is then simply decremented by one.

Decado
although your remark is correct for this compiler/implementation/configuration, it's not defined to be always so. (See Daniel's answer)
xtofl
True, but the question asks what's happening in the pointer assignment, rather than what should happen ;)As one text book memorably said it could well launch a nuclear assault on the isle of white as far as the standards go...
Decado
+11  A: 

Youre simply getting a pointer that contains the address of that "imaginary" location, i.e. the location of the first element &realarray[0] minus the size of one element.

This is undefined behavior, and might break horribly if, for instance, your machine has a segmented memory architecture. It's working because the compiler writer has chosen to implement the arithmetic as outlined above; that could change at any moment, and another compiler might behave totally differently.

unwind
Why didn't gcc raised any warnings for this type of assignment?
Manav MN
Should it do so for `int *p = realarray+5; int *q = p-6;`? In C, you have to do your bounds checking. :-)
Alok
@Alok: how can we use assertion to check bounds in pointer arithmetic beforehand?
Manav MN
@Manav: you can't. From my above code, `p[-1]` is valid. The thing is, as answered in the FAQ, *merely calculating a pointer* like the way you are doing is bad. So, any assertion you may think of doing isn't going to work.
Alok
@Alok: i was thinking on the ways how certain memory profiling applications like mpatrol work, for debugging memory based errors.
Manav MN
@Manav: for dynamically allocated data, these programs allocate a few bytes before and after the returned data, and set them to predictable values. If the values change, it means that your program is writing to where it isn't supposed to. But this technique wouldn't work for your example which does not have dynamic allocation, and which doesn't even write to any of the elements of the array.
Alok
OTOH, 1-indexed arrays are perfectly doable as a class with operator[]() overloaded.
SF.
@SF. not in C. It's debatable if that kind of overloading is good in C++.
Alok
+3  A: 

The behaviour is undefined.

What you have observed may have happened in your particular compiler and configuration, but anything may happen in a different situation. You cannot rely on this behaviour at all.

Daniel Daranas
A: 

array points to one location before the starting address of realarray. However, what confused me is why does this compiled without any warnings.

Manav MN
sbi
In this case, the array is not passed into the function as a parameter. It's scope is local, and checking the bounds in this case is something the compiler most certainly should do, since stack-smashing through stack-allocated arrays has been a security flaw in several programs. This sort of thing could only be useful in code where you know what the stack looks like, which depends on just about everything and the phase of the moon, and would better be done in assembler, where at least you have control over the stack use of your function.
Tim Schaeffer
A pointer can point to any location you like, whether you have reserved that memory or not. It's only when you come to read or write what's at that address that it becomes a problem since you will almost certainly get a segmentation fault. Try changing your example to read the int at the pointer location, and watch the sparks fly!!
Robin Welch
+2  A: 

The behavior is undefined. You can only calculate a pointer to any of the elements of an array, or one past, but that's it. You can only dereference a pointer to any of the elements of an array (not the one past pointer). Looking at your variable names, looks like you're asking a question from this C FAQ. I think that the answer on the FAQ is very good.

Alok
@Alok: what is the logic behind `"...one past"`. Why can we access the one past element?
Lazer
@eSKay: If an array `a` has 10 elements, you can calculate the pointers `a+0` through `a+10`, and you can access the *elements* `a[0]` through `a[9]`. You can't access `a[10]`, so the "one past" part only applies when calculating the pointer value, not dereferencing it.
Alok
@Alok: so, basically "one past" means referencing to whatever might there be "after" `a`, right?
Lazer
+1  A: 

Here you just performing the pointer arithmetic , It will get firs index address of the relarray

See, if you &relarray[+1] , you would get the second element address of the array. since

&relarray[0] is pointing the first index address.

pavun_cool
+3  A: 

Although, as others have noted, it is undefined behaviour in this case, it compiles without warnings because in general, foo[-1] might be valid.

For example, this is fine:

int realarray[10] = { 10, 20, 30, 40 };
int *array = &realarray[2];

printf("%d\n", array[-1]);
caf
is there any way to check array boundaries beforehand, particularly when using pointer arithmetic.
Manav MN
+5  A: 

a[b] is defined as *(a+b)

therefore a[-1] is *(a-1)

Whether a-1 is a valid pointer and therefore the dereference is valid depends on the context the code is used in.

Let_Me_Be
A: 

You're just pointing to the 4 bytes located before the array.

+2  A: 

In C and C++, array indexes are not checked at runtime. You are performing pointer arithmetic which may or may not end up giving defined results (not here).

However, in C++ you can use an array class that does provide bounds checks, e.g boost::array or std::tr1::array (to be added to standard library in C++0x):

#include <cstdio>
#include <boost/array.hpp>

int main()
{
    try {
        boost::array<int, 10> realarray;
        int* p =  &realarray.at(-1);
        printf("%p\n", (void *)p);
    } catch (const std::exception& e) {
        puts(e.what());
    }
}

Output:

array<>: index out of range

Also produces a compiler warning:

8 test.cpp [Warning] passing negative value -0x000000001' for converting 1 ofT& boost::array::at(size_t) [with T = int, unsigned int N = 10u]'

visitor
A: 

This is perfectly well defined. Your code is guaranteed to be accepted by all compilers, and never crash at run time. C/C++ pointers are a numeric data type that obey the rules of arithmetic. Addition and subtraction work, and the bracket notation [] is just a fancy syntax for addition. NULL is literally the integer 0.

And this is why C/C++ are dangerous. The compiler will let you create pointers that point anywhere without complaint. Dereferencing the wild pointer in your example, *array = 1234; would produce undefined behavior, anything from subtle corruption to a crash.

Yes, you could use it to index from 1. Don't do this! The C/C++ idiom is to always index from 0. Other people who saw the code indexing from 1 would be tempted to "fix" it to index from 0.

Daniel Newby
A: 

The experiment could have provided little more clue if it was the following. Instead of printing the pointer value as

printf("%p\n", (void *)array);

, print the array element value

printf("%d\n", *array);

Thats because printing a pointer with %p will always produce some output (without any misbehavior), but nothing can be deduced from it.

ArunSaha
Arun, not necessarily. Even evaluating such a pointer is not allowed.
Alok
@Alok: I see, thanks, I may have missed that. Can you give an example please?
ArunSaha
@Arun: from the C FAQ: "...could fail if, while subtracting the offset, an illegal address were generated (perhaps because the address tried to ``wrap around'' past the beginning of some memory segment)." http://c-faq.com/aryptr/non0based.html
Alok
@Alok: Hmm.. Thanks again. Though I might never do it, but it is definitely good to know. +1 for the link.
ArunSaha