5

The following program in C++ prints more output than I expected. Can anyone explain why this has happened? The program attempts to use pointers to loop through the integer array, printing each value along the way.

#include <cstdio>
using namespace std;

int main(int argc, char **argv) {
    puts("hi");
    int ia[5] = {1,2,3,4,5};
    for (int *p = ia; *p; ++p) {
        printf("Char is: %d\n", *p);
    }
    return 0;
}

/*
 hi
 Char is: 1
 Char is: 2
 Char is: 3
 Char is: 4
 Char is: 5
 Char is: 32767
 Char is: -811990796
 Char is: -133728064
 Char is: 1606416320
 Char is: 32767
 Char is: -1052593579
 Char is: 32767
 Program ended with exit code: 0
*/
none
  • 183
  • 1
  • 1
  • 6
  • 1
    Your loop is written to stop when it encounters a zero element in the array (whereupon `*p` is evaluated to `false`). But your array does not in fact contain a zero element. So the loop walks off the end of the array, therefore exhibiting undefined behavior. – Igor Tandetnik Jan 17 '17 at 02:04
  • FWIW, this is not c++ except for: completely irrelevant `using namespace std`, and an alias for `stdio.h`. http://ideone.com/saAZKk – luk32 Jan 17 '17 at 02:17
  • @luk It is entirely valid C++ code. –  Jan 17 '17 at 02:24
  • @latedeveloper Any C code is perfectly valid C++ code. Does that mean we could drop c tag? Let me ask another way, what in this problem is c++ specific? EDIT: Sorry, let's not go into a debate please. I just wanted to point out, this is not really c++ specific and make people reevaluate whether this is C or C++. Too many people confuse this. – luk32 Jan 17 '17 at 02:34
  • 1
    @luk "Any C code is perfectly valid C++ code" - no, it isn't. The OP's code is. –  Jan 17 '17 at 02:51
  • @latedeveloper I retract that, there are indeed few cases where valid c code will trip c++ compiler. That doesn't really change the point. – luk32 Jan 17 '17 at 03:27
  • Not just trip the compiler. Sometimes the differences compile and instead do something unexpected, if you expect the C behaviour. – user4581301 Jan 17 '17 at 03:59

4 Answers4

21

You will need to have a 0/NULL value to stop at, currently you do not.

Your loop condition will allow iteration until you get a value that evaluates to false (i.e 0) and your array does not contain that, so your iteration will continue on past the bounds of the array and will at some point exit when it access some memory its not supposed to.

There are several ways to fix it. You can add a 0 to the end of the array.

#include <cstdio>
using namespace std;

int main(int argc, char **argv) {
    puts("hi");
    int ia[] = {1,2,3,4,5, 0};
    for (int *p = ia; *p; ++p) {
        printf("Char is: %d\n", *p);
    }
    return 0;
}

Issue with this is that you now cant use 0 in your array, or it will terminate early.

A better way would be to pre calculate the address at which to stop, given the array length. This address is one off the end of the array.

#include <cstdio>
using namespace std;

int main(int argc, char **argv) {
    puts("hi");
    int ia[] = {1,2,3,4,5};
    int* end = ia + 5;
    for (int *p = ia; p != end; ++p) {
        printf("Char is: %d\n", *p);
    }
    return 0;
}

Now we are getting towards the method used by standard library iterators. Now templates can deduce the size of the array.

i.e.

#include <iterator>
...

for (auto it = std::begin(ia); it != std::end(ia); ++it) {
    printf("Char is: %d\n", *it);
}

...

and finally, range based for also supports arrays.

for (auto i: ia)
{
    /* do something */
}
Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
2

Can anyone explain why this has happened?

You are accessing the array out of bounds. Your program has undefined behavior.

The line

int ia[5] = {1,2,3,4,5};

creates an array with exactly 5 elements. Accessing *p after you have accessed the last element of the array is not good.

You can use:

for (int *p = ia; p != std::end(ia); ++p) {

to make sure that you don't access the array out of bounds.

You will need to add:

#include <iterator>

to use std::end.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
1

Alternatively use sizeof() operator to determine the number of elements:

for (int *p = ia; p < ia + sizeof(ia)/sizeof(*ia); ++p) {
    printf("Char is: %d\n", *p);
}
artm
  • 17,291
  • 6
  • 38
  • 54
0

in fact the condition:

for( ; *p; )//...

will check whether the value in the address is equal to 0 or not so it stops only if the value is zero and of course this is not what you wanted; you wanted to increment the address until the last element but your code checks the value inside the address not the address itself morever the address after the lat element is not NULL.

to solve your problem you can count how many elements in the array and then inside the loop increment the pointer accordingly:

#include <cstdio>
using namespace std;

int main(int argc, char **argv) {
    puts("hi");
    int ia[5] = {1,2,3,4,5};
   int* end = &ia[0] + 4;
   for (int *p = ia; p <= end; ++p) {
        printf("Char is: %d\n", *p);
    }
   return 0;
}
Raindrop7
  • 3,889
  • 3
  • 16
  • 27