14

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],...

manav m-n
  • 11,136
  • 23
  • 74
  • 97
  • 3
    I'm not sure how I'd react if I came across a C code base which used arrays that started with 1, but it is possible: `int array[10]; int *arrayOne = array - 1;` – Winder Mar 02 '10 at 15:57
  • Some related threads: http://stackoverflow.com/questions/671703/array-index-out-of-bound-in-c http://stackoverflow.com/questions/1239938/c-accesses-an-array-out-of-bounds-gives-no-error-why – Arun Mar 02 '10 at 16:03

12 Answers12

15

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
  • 391,730
  • 64
  • 469
  • 606
  • Why didn't gcc raised any warnings for this type of assignment? – manav m-n Mar 02 '10 at 09:12
  • 4
    Should it do so for `int *p = realarray+5; int *q = p-6;`? In C, you have to do your bounds checking. :-) – Alok Singhal Mar 02 '10 at 09:17
  • @Alok: how can we use assertion to check bounds in pointer arithmetic beforehand? – manav m-n Mar 02 '10 at 09:23
  • @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 Singhal Mar 02 '10 at 09:25
  • @Alok: i was thinking on the ways how certain memory profiling applications like mpatrol work, for debugging memory based errors. – manav m-n Mar 02 '10 at 09:29
  • 1
    @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 Singhal Mar 02 '10 at 09:36
  • 1
    OTOH, 1-indexed arrays are perfectly doable as a class with operator[]() overloaded. – SF. Mar 02 '10 at 09:45
  • 1
    @SF. not in C. It's debatable if that kind of overloading is good in C++. – Alok Singhal Mar 02 '10 at 09:50
10

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.

Šimon Tóth
  • 35,456
  • 20
  • 106
  • 151
  • If `a-1` is not valid for dereference (because it's out of bounds), then just doing the arithmetic is undefined behavior, even if you don't dereference it. Of course most compilers for sane systems without segmented memory ignore this and allow you to do out-of-bounds pointer arithmetic and get the natural results as long as you don't dereference anything out of bounds. – R.. GitHub STOP HELPING ICE Feb 06 '11 at 19:19
3

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
  • 22,454
  • 9
  • 63
  • 116
3

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 Singhal
  • 93,253
  • 21
  • 125
  • 158
  • @Alok: what is the logic behind `"...one past"`. Why can we access the one past element? – Lazer Mar 14 '10 at 03:28
  • @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 Singhal Mar 14 '10 at 06:06
  • so, basically "one past" means referencing to whatever might there be "after" `a`, right? – Lazer Mar 14 '10 at 10:15
3

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
  • 233,326
  • 40
  • 323
  • 462
2

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
  • 8,564
  • 2
  • 26
  • 15
1

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
  • 356
  • 2
  • 15
  • although your remark is correct for this compiler/implementation/configuration, it's not defined to be always so. (See Daniel's answer) – xtofl Mar 02 '10 at 09:13
  • 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 Mar 02 '10 at 09:19
1

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.

Community
  • 1
  • 1
Pavunkumar
  • 5,147
  • 14
  • 43
  • 69
0

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

manav m-n
  • 11,136
  • 23
  • 74
  • 97
  • 1
    While it might seem trivial for the compiler to find out about in this simple scenario, it isn't simple in general. Just assume that the array was passed (as a pointer) into a function and the arithmetic was done in the function. How would the compiler know that the function wasn't called with `&realarray[1]` and the calculation thus valid? It's easy to create scenarios where you can't find out about this except at run-time. As a C or C++ programmer you have to watch out for these errors anyway. IMO there's more important things compiler writers should spend their resources on. – sbi Mar 02 '10 at 09:27
  • 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 Mar 02 '10 at 13:58
  • 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!! – Component 10 Mar 02 '10 at 16:15
0

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

user266117
  • 56
  • 1
  • 5
0

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
  • 2,392
  • 18
  • 15
0

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.

Arun
  • 19,750
  • 10
  • 51
  • 60
  • Arun, not necessarily. Even evaluating such a pointer is not allowed. – Alok Singhal Mar 02 '10 at 16:33
  • @Alok: I see, thanks, I may have missed that. Can you give an example please? – Arun Mar 02 '10 at 16:37
  • 1
    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 Singhal Mar 04 '10 at 03:28
  • @Alok: Hmm.. Thanks again. Though I might never do it, but it is definitely good to know. +1 for the link. – Arun Mar 04 '10 at 04:03