1

I thought #1

uint8_t* start_ptr, end_ptr;

and #2

uint8_t* start_ptr;
uint8_t* end_ptr;

are generally the same. It seems to me they are not. Could somebody specify what the first one does other than the second?

What happened:

          if(strncmp(mseq,mseq_a,8) == 0){
              start_ptr = MY_UART_RingBuffer_getReadPointer();
              start_found = 1;

          }

          if(strncmp(mseq+3,mseq_z,5) == 0){
              end_ptr = MY_UART_RingBuffer_getReadPointer();

              if (start_found == 1){
                  if(!MY_UART_RingBuffer_getOverlap()){
                    end_ptr = end_ptr - 6;
                  }
                  else{
                      start_found = 0;
                      continue;
                  }

                ptrdiff_t length = end_ptr - start_ptr;
                  start_found = 0;
              }
          }

On using #1 the compiler will give me this for the length calculation:

../Core/Src/main.c:143:32: error: invalid operands to binary - (have 'int' and 'uint8_t * {aka unsigned char *}')
     ptrdiff_t length = end_ptr - start_ptr;

On using #2 everything works out fine.

I am somehow confused here. I see the solution but I don't really get the issue.

Thanks a lot ;-)

nEmai
  • 23
  • 3
  • 1
    `uint8_t* start_ptr, end_ptr;` declares `end_ptr` as a `uint8_t`, not a pointer. I expect this is a dupe question... – Paul Hankin Dec 12 '21 at 13:07
  • https://stackoverflow.com/questions/6990726/correct-way-of-declaring-pointer-variables-in-c-c – Mat Dec 12 '21 at 13:11
  • 1
    `On using #2 everything works out fine.` A **major** reason to always use style 2! :-) – BoP Dec 12 '21 at 13:11
  • I find that rather unintuitive. Is there a special reason why C behaves that way? I mean the line basically says "make this and this and this a uint8_t*. There must be a good reason? :-D Thx – nEmai Dec 12 '21 at 13:12
  • Ah got the problem with Mats answer! stupid of me, thanks ! – nEmai Dec 12 '21 at 13:13
  • @nEmai - There is a reason, but not a good one. The designer of C later regretted the attempt to make the declarations mimic the use of the variable. `int *p` means `*p` is an int. `int q` means `q` is an int. Now try `int *p, q;`... – BoP Dec 12 '21 at 13:13

3 Answers3

6

Grammatically, the type uint8_t is a declaration-specifier; whereas the pointer * is a declarator. According to C's grammar declarators bind to the declared name rather than the declaration-specifier. Consequently your first line is parsed like:

uint8_t (*start_ptr), end_ptr;

i.e. only the first name becomes a pointer.

This is one reason some tend to put the space before the pointer * rather than after. The correct way to declare those in one line would be:

uint8_t *start_ptr, *end_ptr; // both are pointers to uint8_t.
Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
2

Declarations in C consist of two main parts - a sequence of declaration specifiers followed by a comma-separated list of declarators. Pointer-ness, array-ness, and function-ness are specified as part of the declarator, so your declaration is being interpreted as

uint8_t (*start_ptr), end_ptr;

and only start_ptr is declared as a pointer.

The idea is that the structure of the declarator match the structure of an expression in the code. When you want to access the uint8_t value pointed to by start_ptr, you dereference it with the unary * operator:

x = *start_ptr;

The type of the expression *start_ptr is uint8_t, so the declaration is written as

uint8_t *start_ptr;

Whitespace is not significant except to distinguish tokens of the same type. Since * cannot be part of any identifier, you can write that declaration as any of

uint8_t *start_ptr;
uint8_t* start_ptr;
uint8_t*start_ptr;
uint8_t     *    start_ptr   ;

but it will always be tokenized as uint8_t, *, start_ptr, ;, and parsed as

uint8_t (*start_ptr);

So if you want to declare both start_ptr and end_ptr as pointers, then you will need to write either

uint8_t *start_ptr, *end_ptr;

or

uint8_t *start_ptr;
uint8_t *end_ptr;

Put another way, we declare pointers as

T *p, *q;

for the exact same reason we don’t declare arrays as

T[N] a, b;

because the operands of the postfix [] subscript operator are a and b, not T, and the operands of the unary (hint hint hint) * operator are p and q, not T.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

This declaration

uint8_t* start_ptr, end_ptr;

may be equivalently rewritten like

uint8_t ( * start_ptr ), ( end_ptr );

So there is declared the pointer start_ptr of the type uint8_t * and the variable end_ptr of the type uint8_t.

If you want to declare two pointers you have to write

uint8_t *start_ptr, *end_ptr;

Another approach is to introduce a new type specifier using a typedef declaration like for example

typedef uint8_t * uint8_t_ptr;

Using the declared typedef name you can write

uint8_t_ptr start_ptr, end_ptr;

In this case the both declared variables will have the pointer type uint8_t *.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • `Another approach is to introduce a new type specifier using a typedef` never hide pointers. Very bad. – 0___________ Dec 12 '21 at 13:51
  • 2
    @0___________ There is provided an example with the typedef to show how declarations can be interpreted. – Vlad from Moscow Dec 12 '21 at 14:01
  • 1
    @0___________ *never hide pointers. Very bad.* Function pointers excepted. `typedef`'ing function pointers can be the only way to write comprehensible code involving function pointers. Imagine a pointer to a function that takes a function pointer as an argument and returns another function pointer. Try writing that without `typedef`. Especially if the functions take a bunch of arguments. – Andrew Henle Dec 12 '21 at 15:40
  • @AndrewHenle `typedef` functions **not** pointers. Hiding function pointers is the same bad habit as hiding other pointers. If you `typedef` **functions** you will get much easier to read code: https://godbolt.org/z/c4crEovPz – 0___________ Dec 12 '21 at 15:59