0

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?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
jk93117
  • 11
  • 2
  • 3
    Within the same scope you have a chance of using `(sizeof array / sizeof *array)`. If you need to pass the `array` to a function, you would either also have to pass the number of elements as a second parameter, or 0-terminate (or use some other magic value as last value). – Cheatah Sep 13 '21 at 01:06
  • Pointer is valid address but value is not. So you will have t store your array size somehow, that is the best way in C. –  Sep 13 '21 at 01:08
  • To answer you second question: there is no one value that will always terminate an array of arbitrary type. That is because with your very example, every `int` will contain a valid value. In case of a `struct` you would have to check for example for a member pointing to `NULL`. – Cheatah Sep 13 '21 at 01:18

2 Answers2

1

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++) {
    ...
}
mevets
  • 10,070
  • 1
  • 21
  • 33
  • 1
    Pointers are not signed, as specified by the C standard. Given some pointer `p` to an element of an array `a` with size `n`, `p <= a+last` cannot fail to correctly indicate whether `p` points to an element at or earlier than the element with index `last` as long as `last` is less than `n`. If `last` equals `n`, then `a+last` is a defined expression, but `p <= a+last` is always true if `p` points to an element of the array or one beyond it and is not defined if `p` does not so point. The lack of definition is unrelated to signedness. – Eric Postpischil Sep 19 '21 at 15:40
  • What is (p+2) - (p + 3)? – mevets Sep 19 '21 at 23:01
  • The type of `(p+2) - (p+3)` is `ptrdiff_t`; it is not a pointer. – Eric Postpischil Sep 19 '21 at 23:13
1

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?
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • You are absolutely correct. One problem I was having was trying to determine the size of the number of elements in the array based on the pointer inside a function. Once I used sizeof inside of main it worked perfectly and I then passed the size as an additional parameter. Thanks for the help. – jk93117 Sep 19 '21 at 17:28
  • @jk93117 Always happy to help - that's what Stack Overflow is for! Feel free to [explore how SO works](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work).‎ – Adrian Mole Sep 19 '21 at 17:30