Go Down

Topic: Need to print pointer address to uart without using printf or sprintf (Read 4 times) previous topic - next topic

renasis

Hello,

I would like to print a pointer address to the uart without using printf or sprintf with %p parameter. Is this possible? Including these functions takes up too much space in my bootloader. I have found Can you store the pointer address in a variable and print that out? The only function that I have to print to uart is putchar(char ch).

Thanks,

-ren

Graynomad

What system are you using, I don't think those functions are implemented on Arduino.

To print the address  of something on Arduino do something like

int x;

Serial.println (&x, HEX);

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

davekw7x

Can you store the pointer address in a variable and print that out?


If you want to print the value of a pointer without using the format %p specifier, you can cast the pointer to an integer that has the same size and then print the integer value.


To print the address  of something on Arduino do something like

int x;
Serial.println (&x, HEX);


Actually, you can't do this without a cast.  (Did you try it?)

The Arduino print functions do not have a version that is defined to print a pointer to int.

The sketch is compiled as part of a C++ program, and C++ enforces a certain amount of data type integrity. The C++ compiler won't let you assign a pointer value to some non-pointer data type, so you have to use a cast of some kind.  For me, the simplest is just an old C-style cast.

The only thing is that you have to make sure the size of the integer data type is the same size as the pointer, so the method is not portable.
In other words, when you are casting a pointer to another data type it is up to you to make sure they are commensurate.

Now, with avr-gcc (Arduino's compiler) the size of an int is two bytes and the size of a pointer is two bytes.

You can verify this by running something like the following.  Note that sprintf does support printing with %p format specifier, so I use that to compare output with various ways of using casts.

Code: [Select]

// Printing pointer values
//   davekw7x

void setup()
{
   Serial.begin(9600);
}

void loop()
{
   char buffer[30];
   char x[3] = "Hi";
   char *y;
   int z;
   sprintf(buffer, "with %%p:  x    = %p\n", x);
   Serial.print(buffer);
   sprintf(buffer, "with %%p: &x[0] = %p\n", &x[0]);
   Serial.print(buffer);

   y = &x[0]; // Same as writing y = x;
   z = (unsigned int)y;
   Serial.print("sizeof(unsigned int) = ");Serial.print(sizeof(unsigned int));
   Serial.print(", sizeof(char *) = "); Serial.println(sizeof(char *));
   Serial.print("(unsigned int)y     = 0x");Serial.println(uint16_t(y),HEX);
   Serial.print("(unsigned int)x     = 0x");Serial.println((unsigned int)x,HEX);
   Serial.print("(unsigned int)&x[0] = 0x");Serial.println((unsigned int)&x[0],HEX);
   Serial.print(" z = 0x");Serial.println(z,HEX);
   Serial.print("&z = 0x");Serial.println((unsigned int)&z, HEX);

   Serial.println();
   delay(10000);
}



Output:

with %p:  x    = 0x8d5
with %p: &x[0] = 0x8d5
sizeof(unsigned int) = 2, sizeof(char *) = 2
(unsigned int)y     = 0x8D5
(unsigned int)x     = 0x8D5
(unsigned int)&x[0] = 0x8D5
z = 0x8D5
&z = 0x8D3




Bottom line: Whatever method you use to put integer values to your uart can be used to put pointer values.  At the receiving end, the program reading the integer value can cast it to a pointer type and use it for whatever it needs to do.

Regards,

Dave
Footnote:
Note that sprintf with "%p" always prints hex and the version in avr-libc (like all GNU compiler libraries that I have used) always puts "0x" in front of it.  By using a cast to unsigned int instead of sprintf, you can print with whatever base you want.  As long as the sender and receiver agree on the exact format, All is Good.

PaulS

Being able to do something does not mean that it is smart to do it. Typically, pointers are useful only on the system they were defined on. So, what value does the pointer you are trying to send have on another system? In other words, why are you trying to write a pointer's address to the serial port?

tim7

Using only putchar():

Code: [Select]

  int ptr = (int) &value;                 // store 16-bit address, and then pretend that memory is a character array
  putchar( *(1+ (unsigned char*) &ptr) ); // MSB
  putchar( *( (unsigned char*) &ptr) );   // LSB


tim7

Or as ASCII text:

Code: [Select]

  int ptr = (int) &value;                 // store 16-bit address, and then pretend that memory is a character array
  unsigned char tmp;
  char string[]="0123456789ABCDEF";

  tmp = *(1+ (unsigned char*) &ptr); // MSB
  putchar( string[tmp >> 4] );
  putchar( string[tmp & 0xF] );

  tmp = *(0+ (unsigned char*) &ptr); // LSB
  putchar( string[tmp >> 4] );
  putchar( string[tmp & 0xF] );


renasis

Wow,

Thanks for the replies. I wasn't sure if I would get any. Looks like I will have something to try out later today when I get back home.

The pointer is a uint8_t *, which I believe is an unsigned char. Any ideas on to put that to the putchar(char ch)?

To answer your questions. Can't use Serial.print, I am in the bootloader. I am not planning on using ptr address for my program, just debugging code, trying to figure what is going in a bootloader that I downloaded from internet.

Thanks,

-ren

tim7

The code above should be OK since the pointer will be a 16-bit address, no matter what variable-type it points to.  I suppose the pointer ought to be unsigned. eg.
Code: [Select]
uint8_t value
uint16_t ptr = (uint16_t) &value;  // store 16-bit address of 'value'

renasis

How do you know that the pointer will be a 16-bit address? I looked at the avr-libc pages. They indicate that a uint16_t is an unsigned integer and is 16 bit. On some other places on the internet an unsigned integer is a 4 byte variable (or 32 bit). Not sure why there is a difference here, maybe depends on the system you are using the variable on.

The pointer value that I am trying to display is iterating. I try to display it after each iteration, but the value doesn't update when outputed to uart. Are there issues regarding outputing a pointer at runtime?

Thanks,

-ren

Graynomad

Quote
How do you know that the pointer will be a 16-bit address?

Because in an 8-bit AVR it is, in fact it's less than that on most AVR chips.

Quote
On some other places on the internet an unsigned integer is a 4 byte variable (or 32 bit). Not sure why there is a difference here, maybe depends on the system you are using the variable on.

An unsigned int is not a pointer. But you are right, the size of ints and pointers depends on the system.

Quote
I try to display it after each iteration, but the value doesn't update when outputed to uart. Are there issues regarding outputing a pointer at runtime?

No, once cast (which I forgot to do in my example ) to an int it will print properly.

Quote
The pointer value that I am trying to display is iterating.

You don't "iterate" a value, do you mean "increment on each iteration of a loop"?

Show us the code you are using.

______
Rob



Rob Gray aka the GRAYnomad www.robgray.com

tim7


How do you know that the pointer will be a 16-bit address? I looked at the avr-libc pages. They indicate that a uint16_t is an unsigned integer and is 16 bit. On some other places on the internet an unsigned integer is a 4 byte variable (or 32 bit). Not sure why there is a difference here, maybe depends on the system you are using the variable on.


A pointer is just a memory address, so the size of the variable storing the pointer value has to match the size of the memory address.  I was assuming you were using an ATmega328 or 168, which have 16-bit memory addresses.  So my code would not work on a processor with a larger memory space.  A uint16_t is always 16-bit by definition, whereas the size of an int varies depending on the platform and compiler.

Quote

The pointer value that I am trying to display is iterating. I try to display it after each iteration, but the value doesn't update when outputed to uart. Are there issues regarding outputing a pointer at runtime?


You'll need to re-read the pointer each time it changes.  So after every iteration you'll need to do this line before writing to the serial port:
Code: [Select]
uint16_t ptr = (uint16_t) &value;  // store 16-bit address of 'value'

renasis

Yes, sorry, I meant increment.

I partially understand the code below, could you explain a little bit more on what is going on? Still kind of fuzzy on the bit-math.

   int ptr = (int) &value;                 // store 16-bit address, and then pretend that memory is a character array
  unsigned char tmp;
  char string[]="0123456789ABCDEF";

  tmp = *(1+ (unsigned char*) &ptr); // MSB
  putchar( string[tmp >> 4] );
  putchar( string[tmp & 0xF] );

  tmp = *(0+ (unsigned char*) &ptr); // LSB
  putchar( string[tmp >> 4] );
  putchar( string[tmp & 0xF] );
---------
   int ptr = (int) &value;                 // store 16-bit address, and then pretend that memory is a character array
  putchar( *(1+ (unsigned char*) &ptr) ); // MSB
  putchar( *( (unsigned char*) &ptr) );   // LSB


Thanks,
-ren

tim7

These versions may be slightly clearer.  They avoid typecasting the integer into a character array.  See any C reference for an explanation of the '>>' and '&' operators.

This writes out the address as a binary number
Code: [Select]

/* initialisation */
uint16_t ptr;

/* these lines go inside the loop */
ptr = (uint16_t) &value;               // store 16-bit address of 'value'
putchar( (char) (ptr >> 8) & 0xFF );   // Write out high-byte of memory address
putchar( (char) (ptr >> 0) & 0xFF );   // Write out low-byte of memory address


This writes out the address as human-readable hexadecimal text
Code: [Select]

/* initialisation */
char string[]="0123456789ABCDEF";       // array of characters corresponding to numbers from 0 to 15
uint16_t ptr;

/* these lines go inside the loop */
ptr = (uint16_t) &value;                // store 16-bit address of 'value'
putchar( string[ (ptr >> 12) & 0xF ] ); // Write out highest 4-bits of memory address
putchar( string[ (ptr >>  8) & 0xF ] );
putchar( string[ (ptr >>  4) & 0xF ] );
putchar( string[ (ptr >>  0) & 0xF ] ); // Write out lowest 4-bits of memory address

renasis

Thanks Graynomad, davekw7x, and especially tim7 for your help! Great replies!

Go Up