jk93117
jk93117

Reputation: 11

terminating array looping using pointer notation in C

I’m taking a C class on Udemy. Unfortunately the instructor isn’t replying to my question so I thought I’d try this site. My assumption is that it is probably fairly common when developing a program to not know how many elements may be part of an array. When initializing an array the instructor recommends not specifying a size but to let the compiler do it.

Example: int array[ ] = {2,3,4,5,6,7,8};

Obviously, using this method there is no index to use to terminate looping. According to “C Primer Plus” by Stephen Prata the element after the last element in the array is a valid pointer location:

(pg. 406) - C guarantees that when it allocates space for an array, a pointer to the first location after the end of the array is a valid pointer.

If I’m using pointer notation (array++) to loop through the array, what condition can I use to terminate the looping? Is there a value in that location after the final element that I can use? Is that value always the same or does it change depending on the type of array?

Upvotes: 0

Views: 98

Answers (2)

Adrian Mole
Adrian Mole

Reputation: 51904

Your basic idea (looping through an array using pointers) is sound; however, there are a number of points in your question that need some attention.

Is there a value in that location after the final element that I can use? Is that value always the same or does it change depending on the type of array?

Yes, there is a (almost certainly) some value in that location, but it's not something you can ever use! The pointer to the 'one-past-the-end' element is valid only for use in pointer arithmetic or comparison operations; attempting to dereference it (to read the value at that address) is undefined behaviour.

You can get that 'one-past-the-end' pointer by adding the number of elements in the array to the address of the array's first element (or the array 'name' itself). The idiomatic way to get the number of elements in an array is to divide the size of the entire array by the size of its first element. So, for your array, we can declare and initialize our "end pointer" like this, using the sizeof operator:

    int* end = array + sizeof(array) / sizeof(*array);
//  int* end = array + sizeof array / sizeof *array; // Alternative form: "()" optional

Another important point: In your question you mention using array++ to loop through your array variable. You cannot do this, because array isn't actually a (modifiable) pointer variable – it's the name of a variable (an array) whose location is fixed at the point when main (or whatever function it is declared inside) is entered. Instead, you will need to copy the address of the array into another int* pointer, and increment that in the loop.

With those points in mind, here's an illustrative example of how you can loop through your array using a pointer:

#include <stdio.h>

int main(void)
{
    int array[] = { 2,3,4,5,6,7,8 };
    int* end = array + sizeof(array) / sizeof(*array);
    for (int* p = array; p < end; ++p) { 
        // Note that, when we reach p == end, the loop will not run, so ...
        printf("%d\n", *p); // ...we never attempt the *p operation on that
    }
    return 0;
}

A couple of other points of clarification:

  1. The int* p = array assignment works (and is perfectly valid C) because an array variable name can readily decay into a pointer to its first element (as it will if you pass that array as an argument to a function, for example). See: What is array to pointer decay?
  2. Because of that last point above, you cannot use the sizeof(a)/sizeof(*a) paradigm to determine the size of an array in a function it is passed to as an argument; in such cases, you need to pass the array's size as an additional argument. See: How do I determine the size of my array in C?

Upvotes: 1

mevets
mevets

Reputation: 10445

In C pointers are signed. That has consequences dealing with array-like data structures where you might:

while (a <= a+last) {
        ...
        a++;
   }

if the index one beyond the end of a could have a change of sign, then that code could fail. Idiomatic C does not suggest the above; but it needs to be preserved, thus this limitation. In system code, it is possible that you deal with allocations that do not conform to this, thus you should try to work with the idiomatic:

while (a < a+len) {
    ...
    a++
}

So, for your exact question:

for (size_t i = 0; i < sizeof array/sizeof array[0]; i++) {
    ...
}

or

for (int *p = array; p < array + sizeof array / sizeof array[0]; p++) {
    ...
}

Upvotes: 1

Related Questions