1

I'm trying to make some UART code which will put a small string on the stack to be transmitted via a system of interrupts. This is specifically for an Atmel SAM microcontroller

#define UART_BUF_LEN 16

//GPS SERIAL INSTANCE
struct usart_module usart_instance; 
char buffer[UART_BUF_LEN];
uintptr_t bufferPtr = (uintptr_t)buffer;

bool transmitting(){
    return (uintptr_t)buffer == bufferPtr; //If the read head is at the top of the array, then we are not transmitting.
}

bool transmit(char* c, uint len){
    if(!transmitting() && len<=UART_BUF_LEN){
        bufferPtr= (uintptr_t)buffer + len;//set write head to top of the len stack
        while(transmitting()){
            *bufferPtr = *c; //Set the value at the address of bufferPtr to the value at the address of c.
            bufferPtr --; //Move the buffer closer to the head of the array
            c++; //Move the head of the array down some.
        }
    }else{
        return false;
    }
    bufferPtr= (uintptr_t)buffer + len; //reset the read head so that our transmit code knows where to read from.
    return true;
}

Trouble here is the line *bufferPtr = *c;. When I build the solution, the bufferptr seems to not be dereferencable. I get the following error:

invalid type argument of unary '*' (have 'uintptr_t {aka unsigned int}')

I've looked online, and all sources tell me that I must cast uintptr_t back into a pointer of the native datatype which the memory address points to. I am not sure how to do this, as using the typecast (char *), indicating that the buffer pointer is a character pointer does not compile, giving me the same error as above.

Now the rabbit hole goes one level deeper when I change the line to \*(char\*)bufferPtr = \*c;, which does not give me any error. What does this line mean?

I expect that it means that the value at the address of the typecast of bufferPtr to a char pointer is set to the value at the address of c. Is this correct?

Eliahu Aaron
  • 4,103
  • 5
  • 27
  • 37
tuskiomi
  • 190
  • 1
  • 15
  • 2
    uintptr_t is "unsigned int", so you had to cast it to pointer – Nouman Tajik Mar 17 '20 at 06:31
  • @NoumanTajik in microcontroller code, I think it's a bit different, especially in 8 bit micros, but I see what you are saying – tuskiomi Mar 17 '20 at 06:33
  • 2
    Whether it is a PC or MCU, the compiler had to know that you are referring to a memory location or a value. The error you stated above says the same thing i.e. invalid type argument of unary '*' (have 'uintptr_t {aka unsigned int}'). I think the uintptr_t naming had place some confusion, it should be typedef as (unsigned int *) instead of (unsigned int). – Nouman Tajik Mar 17 '20 at 06:37
  • @NoumanTajik so they are the exact same datatype, then? – tuskiomi Mar 17 '20 at 06:38
  • "uintptr_t" in your code means (unsigned int) NOT (unsigned int *) – Nouman Tajik Mar 17 '20 at 06:40
  • Why did you use uintptr_t? – user253751 Mar 17 '20 at 16:09

3 Answers3

2

Since bufferPtr is a uintptr_t, which is an integral type, not a pointer type, you will have to cast it back to the pointer type before dereferencing it.

*(char *)bufferPtr = *c;

This is fine, since the source of bufferPtr's value came from a char array.

char buffer[UART_BUF_LEN];
uintptr_t bufferPtr = (uintptr_t)buffer;
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Wait, so you're telling me that my convoluted logic may actually have been right?! It's good to know I'm still learning. – tuskiomi Mar 17 '20 at 06:27
0

Change this:

uintptr_t bufferPtr = (uintptr_t)buffer;

to this:

char *bufferPtr = buffer;

Delete (uintptr_t) everywhere in your program.

I don't know why you decided to use uintptr_t, but you do not need it.

user253751
  • 57,427
  • 7
  • 48
  • 90
0

uintptr_t is an integer type which is provided to be able to safely store the value of a pointer in an integer. As different machines and architectures have different pointer sizes, this type allows to convert a pointer to an integral type without having to worry about choosing the correct size of integer (e.g. 32 bits or 64 bits integers) depending on the machine. This makes writing portable code easier.

uintptr_t is not a pointer type. This means you cannot dereference it, just like you cannot dereference an int. To be able to use it as a pointer you have to cast it back to a pointer.

char* char_ptr = (char*)bufferptr;
*char_ptr =  *c;  

If you want to do pointer manipulation (including pointer arithmetic), I would recommend keeping your pointers as pointers and not converting them to uintptr_t.

Since char is guaranteed to have a size of 1 all the computations you do on a char* are exactly the same as the computations you would do on an integer e.g.

char c = 'A'
char* p0 = &c;
uintptr_t p_casted = (uintptr_t)p0;

char* p1 = p0 + 42;
char* p2 = (char*)(p_casted + 42);

In this example p1 and p2 will have the exact same value. p0 + 42 will add 42 times the size of char to the address contained in p0, but since the size of char is always 1, this is exactly the same as the second version.

The result would be different on a type whose size is not 1 (e.g. if p0 was an int* or float*). However, if you want to do simple integer calculations (say, adding or subtracting an offset) to the pointer value I would still recommend casting to char* instead of intptr_t unless you really need to do specialized integer operations on the pointer's value (e.g. invert some bits or something like this), as suggested by the answers to this question.

Louen
  • 3,617
  • 1
  • 29
  • 49