-8

Let's have this array:

char arr[SIZE_MAX];

And I want to loop through it (and one past its last element):

char *x;

for (i = 0; i <= sizeof(arr); i++)
        x = &arr[i];

(Edited to add some valid code with pointers).

What is the most adequate type for i?

I accept non-standard types such as POSIX's types.

Related question:

Assign result of sizeof() to ssize_t

  • The most adequate type for array indexing is `size_t` if you have to ask, but using any other integer type which is capable to represent the whole range is not uncommon as well. You have other issues in the posted snippet though... – Eugene Sh. Mar 22 '19 at 16:10
  • 1
    And you want `i < sizeof arr`, not `<=`. – Maxim Egorushkin Mar 22 '19 at 16:12
  • @Eugene I know it's supposed to be `size_t` in Standard C, but clearly this valid snippet proves that if I used that type, I would have a problem, an infinitely lasting problem :). Standard C is broken for that reason, and that's the reason I accept POSIX for this (which is also broken, by the way). – alx - recommends codidact Mar 22 '19 at 16:15
  • @MaximEgorushkin No, I do want `i <= sizeof(arr)`, and that's the reason I explicitly mentioned one past the last element. And that's the big problem of the question. – alx - recommends codidact Mar 22 '19 at 16:16
  • 1
    I don't understand your comment. If you intend to index the array with `i` inside the loop, then you will have an out-of-bounds access. Is your question about which type to use in this specific case where you *don't* access the one-past-the end element? – Eugene Sh. Mar 22 '19 at 16:18
  • 1
    *Standard C is broken for that reason* If you're going to make a claim like that, you are going to have to back it up with a lot more than just handwaving and ranting about `size_t`. – Andrew Henle Mar 22 '19 at 16:20
  • @EugeneSh. No. C allows to have a pointer to one past the last element an array, as long as you don't dereference it. – alx - recommends codidact Mar 22 '19 at 16:23
  • There is no pointers in your code. Show your specific use-case. – Eugene Sh. Mar 22 '19 at 16:23
  • @AndrewHenle My simple snippet shows it. unsigned types in general are broken for loops (or you have to be very careful and add checks before looping). – alx - recommends codidact Mar 22 '19 at 16:25
  • 2
    They are not broken, they are limited as any tool to their use-cases. The compiler will give a warning about "the condition is always true". Yes, you have to be careful when walking on the edge, and this code is there. – Eugene Sh. Mar 22 '19 at 16:26
  • 1
    Use `__int128` or an 80-bit or 128-bit `long double` then. – Maxim Egorushkin Mar 22 '19 at 16:27
  • 1
    *unsigned types in general are broken for loops* How are they "in general" broken? – Andrew Henle Mar 22 '19 at 16:27
  • @AndrewHenle Did you read the question and see the infinite loop? That's the reason. – alx - recommends codidact Mar 22 '19 at 16:29
  • Minor - In your edit - `&arr[i]` is dereferencing (it is equivalent to `&(*(arr+i))`), so no, this code is not valid, but get your point. – Eugene Sh. Mar 22 '19 at 16:30
  • @EugeneSh. The problem is that if I don't use this simple case with `SIZE_MAX`, but a case where the size is given by a `size_t` variable, the compiler will accept the code, and the program will break at run-time. – alx - recommends codidact Mar 22 '19 at 16:32
  • 3
    @EugeneSh. isn't `&*` considered equivalent to doing nothing? I'd have to look that part of the standard up again. – Christian Gibbons Mar 22 '19 at 16:32
  • 1
    *Did you read the question and see the infinite loop?* So because you can contrive an infinite loop, you're claiming that C is broken? If **you** write bad code, that's on you. – Andrew Henle Mar 22 '19 at 16:33
  • @CacahueteFrito Then you should claim that any integer type is broken, as it might overflow in the runtime. I don't understand your reasoning. You as programmer should be aware of the limitations and avoid these issues.. – Eugene Sh. Mar 22 '19 at 16:34
  • 1
    @ChristianGibbons That's an interesting lawyer question. On one hand using`*` with invalid pointer is UB. On the other hand - what you said. – Eugene Sh. Mar 22 '19 at 16:35
  • 2
    I'm not following why the possibility of writing an infinite loop involving an unsigned iteration variable makes it preferable to use a signed iteration variable. You can get yourself into at least as much trouble with a signed iteration variable, in exactly the same way. – John Bollinger Mar 22 '19 at 16:37
  • With signed types I can reverse: `for (i = sizeof(arr); i >= 0; i--)` (if the context allows it) – alx - recommends codidact Mar 22 '19 at 16:41
  • But in this case, the only option to loop through what is a valid size, would seem a 128 bit type (or `long double`) as Maxim said. – alx - recommends codidact Mar 22 '19 at 16:44
  • But you're *not* iterating in reverse. Moreover, even if the iteration order doesn't matter, you can only iterate in reverse if the (signed) type of `i` can represent the size of the array, which is unlikely for the array size presented in the example. And if there *were* such a signed type, then its corresponding unsigned type would be perfectly suitable for the type of `i` in a forward iteration. – John Bollinger Mar 22 '19 at 16:45
  • @JohnBollinger Yeah, that's why I asked the other question first :) – alx - recommends codidact Mar 22 '19 at 16:49
  • 2
    As you've demonstrated in the question, it's entirely possible to shoot yourself in the foot by using an unsigned type as the indexer. Similarly, though, it's entirely possible to shoot yourself in the foot by using a *signed* type as the indexer due to the language's automatic integer promotion rules. When you try to *use* that index, and it gets implicitly converted to an unsigned, boom. So, in summary, the C language gives you lots of ways to shoot yourself in the foot. Do consider using a pointer as the indexer if you actually want a pointer. – Cody Gray - on strike Mar 22 '19 at 17:44

3 Answers3

2

Let's have this array:

char arr[SIZE_MAX];

Note that many C implementations will reject that, SIZE_MAX being larger than any array they can actually support. SIZE_MAX is the maximum value of type size_t, but that's not the same thing as the (implementation-dependent) maximum size of an object.

And I want to loop through it (and one past its last element):

for (i = 0; i <= sizeof(arr); i++)

What is the most adequate type for i?

There is no type available that we can be confident of being able to represent SIZE_MAX + 1, and supposing that arr indeed has size SIZE_MAX, you need i's type to be able to represent that value in order to avoid looping infinitely. Unless you want to loop infinitely, which is possible, albeit unlikely.

On the other hand, if we have

char arr[NOT_SURE_HOW_LARGE_BUT_LESS_THAN_SIZE_MAX];

then the appropriate type for i is size_t. This is the type of the result of the sizeof operator, and if the only assumption we're making is that the object's size is smaller than SIZE_MAX then size_t is the only type we can be confident will serve the purpose.

If we can assume tighter bounds on the object's size, then it may be advantageous to choose a different type. In particular, if we can be confident that it is smaller than UINT_MAX then unsigned int may be a slightly more performant choice. Of course, signed types may be used for i, too, provided that they can in fact represent all the needed values, but this may elicit a warning from some compilers.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

size_t is your friend in this case. It guarantees you access to any array index.

Jordan Motta
  • 188
  • 1
  • 5
1

Hmm, interesting case. You want to be able to loop with, let's just call it hypothetically the largest possible number, and thus wrap-around rather than ever having the comparison return false. Something like this might do the trick:

char arr[SIZE_MAX];
size_t i = 0;
do {
    /* whatever you wanna do here */
    ++i;
}
while (i <= sizeof(arr) && i != 0);

Because a do-while will always perform the first iteration, we can put a condition such that the loop will end when i == 0, and it will only happen upon wrapping back around to 0 rather than on the first iteration. Add another condition for i <= sizeof(arr) to take care of cases where we don't have wrap-around.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Christian Gibbons
  • 4,272
  • 1
  • 16
  • 29