26

I just recently started learning C++, and as most people (according to what I have been reading) I'm struggling with pointers.

Not in the traditional sense, I understand what they are, and why they are used, and how can they be useful, however I can't understand how incrementing pointers would be useful, can anyone provide an explanation of how incrementing a pointer is a useful concept and idiomatic C++?

This question came after I started reading the book A Tour of C++ by Bjarne Stroustrup, I was recommended this book, because I'm quite familiar with Java, and the guys over at Reddit told me that it would be a good 'switchover' book.

8
  • 13
    A pointer is just an iterator Commented Aug 1, 2014 at 3:17
  • 1
    It's one of the favourite tools to write computer viruses that read what they should not read. It's also one of the most common cases of vulnerability in apps (when one increments a pointer past the area where they are supposed to, then reads or writes it)> See the HeartBleed bug.
    – Sam
    Commented Aug 1, 2014 at 12:27
  • 2
    @vasile This is what is bad about pointers.
    – Cruncher
    Commented Aug 1, 2014 at 14:28
  • 4
    The nice/bad thing about C++ is that it allows you to do much more before calling a segfault. Usually you get a segfault when trying to access another process' memory, system memory or protected app memory. Any access inside the usual application pages is allowed by the system, and it's down to the programmer/compiler/language to enforce reasonable limits. C++ pretty much allows you to do whatever you want. As for openssl having its own memory manager - that's not true. It just has the default C++ memory access mechanisms.
    – Sam
    Commented Aug 1, 2014 at 15:38
  • 1
    @INdek: You'll only get a segfault if the memory you're trying to access is protected. Most operating systems assign protection at the page level, so you can usually access anything that's on the page your pointer starts on. If the OS uses a 4K page size, that's a fair amount of data. If your pointer starts out somewhere in the heap, it's anybody's guess how much data you could access.
    – TMN
    Commented Aug 1, 2014 at 16:31

3 Answers 3

48

When you have an array, you can set up a pointer to point to an element of the array:

int a[10];
int *p = &a[0];

Here p points to the first element of a, which is a[0]. Now you can increment the pointer to point to the next element:

p++;

Now p points to the second element, a[1]. You can access the element here using *p. This is different from Java where you would have to use an integer index variable to access elements of an array.

Incrementing a pointer in C++ where that pointer does not point to an element of an array is undefined behaviour.

15
  • 24
    Yep, with C++ you are responsible for avoiding programming errors such as access outside the bounds of an array. Commented Aug 1, 2014 at 1:45
  • 9
    No, incrementing a pointer that points to anything except an array element is undefined behaviour. However, if you are doing something low level and not portable, then incrementing a pointer is usually nothing more than accessing the next thing in memory, whatever that may happen to be. Commented Aug 1, 2014 at 1:56
  • 4
    There are a few things that are, or can be treated as, an array; a string of text is, in fact, an array of characters. In some cases, a long int is treated as an array of bytes, though this can easily get you into trouble. Commented Aug 1, 2014 at 2:42
  • 6
    That tells you the type, but the behaviour is described in 5.7 Additive operators [expr.add]. Specifically, 5.7/5 says that going anywhere outside the array except one-past-the-end is UB.
    – Useless
    Commented Aug 1, 2014 at 11:01
  • 4
    Last paragraph is: If both the pointer operand and the result point to elements of the same array object, the evaluation shall not produce an overflow; otherwise the behavior is undefined. So, if the result is neither in the array nor one-past the end, you get UB.
    – Useless
    Commented Aug 1, 2014 at 13:06
37

Incrementing pointers is idiomatic C++, because pointer semantics reflect a fundamental aspect of the design philosophy behind the C++ standard library (based off of Alexander Stepanov's STL)

The important concept here, is that the STL is designed around containers, algorithms, and iterators. Pointers are simply iterators.

Of course, the ability to increment (or add/subtract from) pointers goes back to C. A lot of C-string manipulation algorithms can be written simply using pointer arithmetic. Consider the following code:

char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));

This code uses pointer arithmetic to copy a null-terminated C-string. The loop automatically terminates when it encounters the null.

With C++, pointer semantics are generalized to the concept of iterators. Most standard C++ containers provide iterators, which can be accessed via the begin and end member functions. Iterators behave like pointers, in that they can be incremented, dereferenced, and sometimes decremented or advanced.

To iterate over an std::string, we would say:

std::string s = "abcdef";
std::string::iterator it = s.begin();
for (; it != s.end(); ++it) std::cout << *it;

We increment the iterator just like we would increment a pointer to a plain C-string. The reason this concept is powerful is because you can use templates to write functions that will work for any type of iterator that meets the necessary concept requirements. And this is the power of the STL:

std::string s1 = "abcdef";
std::vector<char> buf;
std::copy(s1.begin(), s1.end(), std::back_inserter(buf));

This code copies a string into a vector. The copy function is a template that will work with any iterator that supports incrementing (which includes plain pointers). We could use the same copy function on a plain C-string:

   const char* s1 = "abcdef";
   std::vector<char> buf;
   std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));

We could use copy on an std::map or a std::set or any custom container that supports iterators.

Note that pointers are a specific type of iterator: random access iterator, which means they support incrementing, decrementing, and advancing with the + and - operator. Other iterator types only support a subset of pointer semantics: a bidirectional iterator supports at least incrementing and decrementing; a forward iterator supports at least incrementing. (All iterator types support dereferencing.) The copy function requires an iterator that at least supports incrementing.

You can read about different iterator concepts here.

So, incrementing pointers is an idiomatic C++ way to iterate over a C-array, or access elements/offsets in a C-array.

10
  • 3
    Though I use pointers like in the first example, I never thought about it as an iterator, it makes a lot of sense now.
    – dyesdyes
    Commented Aug 1, 2014 at 8:02
  • 1
    "The loop automatically terminates when it encounters the null." This is a terrifying idiom. Commented Aug 1, 2014 at 14:58
  • 9
    @CharlesWood, then I guess you must find C pretty terrifying
    – Siler
    Commented Aug 1, 2014 at 16:18
  • 7
    @CharlesWood: The alternative is to use the length of the string as the loop control variable, which means traversing the string twice (once to determine the length, and once to copy the characters). When you're running on a 1MHz PDP-7, that can really start to add up.
    – TMN
    Commented Aug 1, 2014 at 16:56
  • 3
    @INdek: first of all, C and C++ try to avoid at all costs to introduce breaking changes - and I would say that altering the default behavior of string literals would be quite a modification. But most importantly, zero-terminated strings are just a convention (made easy to follow by the fact that string literals are zero-terminated by default and that the library functions expect them), nobody stops you from using counted strings in C - actually, several C libraries do use them (see e.g. OLE's BSTR). Commented Aug 1, 2014 at 23:05
18

Pointer arithmetic is in C++ because it was in C. Pointer arithmetic is in C because it's a normal idiom in assembler.

There are plenty of systems where "increment register" is faster than "load constant value 1 and add to register". Moreover, quite a few systems let you do "load DWORD into A from address specified in register B, then add sizeof(DWORD) to B" in a single instruction. These days you might expect an optimising compiler to sort this out for you, but this wasn't really an option in 1973.

This is basically the same reason that C arrays aren't bounds-checked and C strings don't have a size embedded in them: the language was developed on a system where every byte and every instruction counted.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.