What type should be used to loop through an array? [duplicate]
Asked Answered
T

3

-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

Transmission answered 22/3, 2019 at 16:7 Comment(25)
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...Inae
And you want i < sizeof arr, not <=.Ulceration
@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).Transmission
@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.Transmission
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?Inae
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.Arthro
@EugeneSh. No. C allows to have a pointer to one past the last element an array, as long as you don't dereference it.Transmission
There is no pointers in your code. Show your specific use-case.Inae
@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).Transmission
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.Inae
Use __int128 or an 80-bit or 128-bit long double then.Ulceration
unsigned types in general are broken for loops How are they "in general" broken?Arthro
@AndrewHenle Did you read the question and see the infinite loop? That's the reason.Transmission
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.Inae
@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.Transmission
@EugeneSh. isn't &* considered equivalent to doing nothing? I'd have to look that part of the standard up again.Colotomy
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.Arthro
@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..Inae
@ChristianGibbons That's an interesting lawyer question. On one hand using* with invalid pointer is UB. On the other hand - what you said.Inae
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.Vedi
With signed types I can reverse: for (i = sizeof(arr); i >= 0; i--) (if the context allows it)Transmission
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.Transmission
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.Vedi
@JohnBollinger Yeah, that's why I asked the other question first :)Transmission
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.Mim
C
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.

Colotomy answered 22/3, 2019 at 16:29 Comment(0)
V
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.

Vedi answered 22/3, 2019 at 16:29 Comment(0)
C
1

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

Committeewoman answered 22/3, 2019 at 16:16 Comment(1)
No, it's not my friend. I would avoid it as hell, and the reason is the snippet in my question.Transmission
C
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.

Colotomy answered 22/3, 2019 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.