1

I have an array of unsigned 8 bit integer, however the length of this array can be higher than 255. I want to use pointer arithmetics instead of array indexing. Can someone explain me if the following code is acceptable or not? My doubt is that the ind variable has a different type with respect to buff and this might be seen as bad programming.

#include <stdio.h>
#include <stdint.h>

int main(){
    uint8_t buff[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int16_t ind;
    
    ind = 1;
    
    printf("%u\n", *(buff + 1));
    printf("%u\n", *(buff + ind));
}

Godbolt shows a small difference between *(buff + 1) and *(buff + ind) but it seems to work.

JJAlberto
  • 23
  • 5
  • 2
    It's not clear what you're trying to do. `*buff + 1` is the same as `buff[0] + 1`, and `*buff + ind` is the same as `buff[0] + ind`. Neither of which does pointer arithmetic. Perhaps you are trying to replace `buff[ind]`? That would be `*(buff + ind)`. But you'll find that the result is identical. In fact the language *defines* them to be identical. – Raymond Chen Apr 15 '21 at 14:27
  • You are adding `1` and `ind` to the integer stored to the first element of the array `buff`, not doing pointer arithmetic. – MikeCAT Apr 15 '21 at 14:27
  • I think you meant `*(buff + 1)` and `*(buff + ind)`... – Adrian Mole Apr 15 '21 at 14:28
  • I would say that the `*buff` as well as the `ind` is implicitely casted to an `int` in the second `printf` statement according to type promotion, because they both fit very well into the int datatype size. https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules So the sum of them will still be of type `int`. However, the `printf` format specifier expects an `unsigned int` which is not the case, here. Therefore it outputs the correct result only, if `*buff + ind >= 0`. Didn't your compiler issue a warning? – Wör Du Schnaffzig Apr 15 '21 at 14:32
  • *"Godbolt shows a small difference"* - What difference? – klutt Apr 15 '21 at 14:35
  • Apologize, I meant `*(buff + 1)` and `*(buff + ind)`. I have just edited the question. – JJAlberto Apr 15 '21 at 14:36
  • For any pointer or array `buff` and index `ind` the expression `*(buff + ind)` is *exactly* equal to `buff[ind]`. That e.g. `ind` is a literal integer or a variable doesn't matter, `buff[1]` is always going to be `buff[1]`. – Some programmer dude Apr 15 '21 at 14:38
  • You say "the length of this array can be higher than 255" and that is true. The array length doesn't depend on the element type. The type `uint8_t` is only for the elements themselves. – Some programmer dude Apr 15 '21 at 14:40
  • @Someprogrammerdude I think that the "the length of this array can be higher than 255" was just the reason for him to use `uint16_t` as index instead of `uint8_t`, prompting the actual question: "do I need to use `uint8_t` to do arithmetics on a `uint8_t` pointer" – Adalcar Apr 15 '21 at 14:47
  • Exactly @Adalcar, that was the right question. I'm going to edit the title – JJAlberto Apr 15 '21 at 14:53
  • "I want to use pointer arithmetics instead of array indexing" It's the same thing, just less readable. Please study [Do pointers support “array style indexing”?](https://stackoverflow.com/questions/55747822/do-pointers-support-array-style-indexing). – Lundin Apr 16 '21 at 07:45

3 Answers3

1

Can someone explain me if the following code is acceptable or not?

It's OK but it would be more correct to use size_t instead of int16_t. This is the integer type used to describe the size of things, including arrays. Whereas int16_t is a small, signed type.


I want to use pointer arithmetics instead of array indexing

Why? The only thing you achieve with that is to make the code less readable. Array indexing is the same as pointer arithmetic, it is just "syntactic sugar". Please study Do pointers support “array style indexing”?.


As a side note, the correct printf format specifier for uint8_t is PRIu8 from inttypes.h, not %u. So your code should be changed to:

printf("%" PRIu8 "\n", buff[1]);

or

printf("%u\n", (unsigned int)buff[1]);
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • `size_t` is used for size but what about indexing? Is it good or bad using `size_t` for, let me say, the counter in a for-loop? – JJAlberto Apr 16 '21 at 11:32
  • @JJAlberto Indexing is almost always about iterating over an array. Then `size_t` is the correct type to use, since you need a type with the same size as an array. In general, you don't need to worry about `size_t` being too large a type unless you are manually optimizing some 8-bit CPU code or such. – Lundin Apr 16 '21 at 11:35
0

array indexing is already pointer arithmetic as buff[i] is defined as *(buff+i). But the former is easier to read (and understand).

I also do not understand that whole 255 limit as the maximum value for an array index has nothing to do with the type of the array, only with its size.

An array index is always of type size_t which is an unsigned integer type able to hold any possible array index. If you use any other integer type as the index, the compiler will silently convert that value to a size_t.

So no you do not need to use uint8_t to do arithmetic on a uint8_t*.

koder
  • 2,038
  • 7
  • 10
  • I think he meant he couldn't write the size on an `uint8_t` if the array could be bigger. Which is why the question was about adding an `uint16_t` to an `uint8_t*` – Adalcar Apr 15 '21 at 14:35
  • Unfortunately I made a mistake writing the question. Sorry. I meant *(buff + ind) instead of *buff + ind. However, the question remains the same, `ind` can be a `int16_t` instead of `uint8_t`? – JJAlberto Apr 15 '21 at 14:42
0

Most types are converted to int before doing arithmetics (especially for pointer arithmetics), so whether you use uint8, uint16 or uint32 for ind has little consequence.

what you are doing is adding something to the pointer, which is an address (on 32 or 64 bits), meaning the type pointed by it (uint8_t in your case) has absolutely no effect on the type of the pointer.

However, do remember that the size of the object pointed matters a lot in pointer arithmetics, since it will not move byte per byte but object by object.

uint8_t *char_ptr = 0x1000;
uint32_t *int_ptr = 0x1000;

char_ptr += 1;
int_ptr += 1;

printf("0x%x", char_ptr); // 0x1001
printf("0x%x", int_ptr);  // 0x1004

 
Adalcar
  • 1,458
  • 11
  • 26
  • C spec says only about integer types. There is no problem with negative index as a pointer can point to an element inside the array. – tstanisl Apr 15 '21 at 17:49
  • `Most types (especially for pointers) are converted to unsigned integer before doing arithmetics` this is wrong. Pointers aren't promoted to anything, and types smaller than `int` will be promoted to `int`, **not `unsigned int`**, unless `sizeof(uint8_t) == sizeof(int)`. See [Implicit type promotion rules](https://stackoverflow.com/q/46073295/995714) – phuclv Apr 16 '21 at 08:14
  • @phuclv I meant `for pointer arithmetics`, of course pointers aren't promoted. Fixed the answer – Adalcar Apr 16 '21 at 09:42
  • `or uint for unsigned types` is still wrong as I said – phuclv Apr 16 '21 at 09:48