-1

I'm working with the atmega2560 controller and avr-g++. I'm debugging code that works on my Ubuntu 18.04 host (when compiled with g++) but not on the atmega2560. I thought it might be a problem of different data type sizes, and used this code to investigate (also given below):

int integerType;
float floatType;
double doubleType;
char charType;

printf("Size of int: %ld bytes\n",sizeof(integerType));
printf("Size of float: %ld bytes\n",sizeof(floatType));
printf("Size of double: %ld bytes\n",sizeof(doubleType));
printf("Size of char: %ld byte\n",sizeof(charType));

The results on the host look ok but the avr-g++ compiled code on atmega2560 gives wild sizes for the different data types:

g++:

Size of int: 4 bytes
Size of float: 4 bytes
Size of double: 8 bytes
Size of char: 1 byte

avr-g++:

Size of int: 2 bytes
Size of float: 41680900 bytes
Size of double: 43253764 bytes
Size of char: 44957697 byte

which is completely unrealistic. What could be the reason for this?

update

It was suggested that I try %zu instead of %ld to print the std::size_t outputs from sizeof(...). This, unfortunately, didn't work, and the output looks as follows:

Size of int: Size of float: Size of double: Size of char: 
borizzzzz
  • 620
  • 1
  • 6
  • 17
  • 1
    According to [this](https://stackoverflow.com/a/5943869/10411602) answer, the correct specifier is `%zu`. Perhaps that's the issue. – Blaze Jun 27 '19 at 09:27
  • Thanks for the comment, @Blaze! Didn't work for me, unfortunately but I'll add it as an update. – borizzzzz Jun 27 '19 at 09:36
  • `sizeof` doesn't return a long. Your code is incorrect. – user207421 Jun 27 '19 at 09:42
  • @user207421, thanks for pointing that out. I also tried %lu which seems to be the usual way to cast size_t for printf (a myriad of hits on stack overflow), but it gave me the same output as in my question. I decided to leave the code as I found it on the linked website to avoid confusion. – borizzzzz Jun 27 '19 at 09:51
  • 1
    If you have a doubt on the return type, you can try `std::cout << sizeof(.) << "\n"` – Damien Jun 27 '19 at 10:01
  • 1
    There is no place for `l` in that format expression. I seem to have already alluded to that. Also `sizeof` is an operator, not a function: it doesn't need the parentheses. – user207421 Jun 27 '19 at 10:03
  • 1
    It seems that `sizeof()` return `unsigned int`s on avr-g++, not `unsigned long`s. So, if you remove `l` you will see correct results. – sklott Jun 27 '19 at 10:42
  • 4
    Well, you can always use an ugly cast: `printf("Size of float: %d bytes\n", (int)sizeof(float));`, but why aren't you using `std::cout`? – Bob__ Jun 27 '19 at 12:43
  • @user207421 and sklott, you were right. Removing the 'l' did the trick and now the data type sizes get printed correctly. Thank you for the help! – borizzzzz Jun 27 '19 at 20:11

3 Answers3

2

On the atmega2560, an integer is 2 bytes. So each of your calls to printf is passing the format string, followed by a 2-byte integer.

But the format string specifies %ld, which is a 4-byte long integer; so printf is expecting a 4-byte value on the stack. It is therefore reading bytes from the stack that weren't pushed there by the calling function; these bytes could have any value whatsoever.

If you had printed the values in hexadecimal with %lX, you would have seen the expected size in the low-order two bytes. For example, 41680900 is 0x027C0004, and you can see the size of a float in that 0004. The 027C is effectively random noise.

To fix this, use %d instead of %ld. This will work on your Ubuntu host (with 4-byte integers) and your atmega2560 (with 2-byte integers).

Perhaps even better is to use %z, as explained in chux's answer, in case the size of a sizeof is not the same as the size of an int.

TonyK
  • 16,761
  • 4
  • 37
  • 72
0

sizeof returns a size_t - some unsigned type. When using *printf(), use the specified matching specifier of "zu"`.

// printf("Size of float: %ld bytes\n",sizeof(floatType));
// () not needed with an object
printf("Size of float: %zu bytes\n", sizeof floatType);

If code is using an old version casting to the widest available type is an alternative:

// Assume unsigned long is widest
printf("Size of float: %lu bytes\n", (unsigned long) sizeof floatType);

Of course with small result expected, code could use unsigned

printf("Size of float: %u bytes\n", (unsigned) sizeof floatType);

Yet since this is C++, how about the following? @Bob__

cout << "Size of float: " << sizeof floatType << " bytes" << endl;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • AVR-LibC does not implement print modifier `%z`. `cout` is not available; there is no file system, and GCC's libstdc++v3 is disabled for target avr because basic stuff (list file system etc.) is missing, and libstdc++ would not build. – emacs drives me nuts Oct 28 '22 at 12:46
  • @emacsdrivesmenuts In that select case, use a `(unsigned long)` cast and `%lu`. Print to a string and when `printf()` not available – chux - Reinstate Monica Nov 01 '22 at 15:48
  • `printf` is available. I just was confused because you proposed features that are not available / implemented for that target. Apart from that, `int` and `void*` are 16-bit types, so `%u` and `(unsigned)` will be enough. – emacs drives me nuts Nov 01 '22 at 16:01
  • @emacsdrivesmenuts Certainly cast to `(unsigned)` is available, as suggested above. Using `"%u"` with `int` or `void *` is _undefined behavior_. It may work as hoped with your target compiler today, but not certainly with future compilers even if the types remain 16-bit. Best to code to the standard. Future maintainers of your code will appreciate spec adherence. – chux - Reinstate Monica Nov 01 '22 at 20:05
  • I just wanted to say that 16-bit integral operands is enough. Apart from that, as we are using GCC we can simply `echo "" | avr-gcc -xc - -E -dM | grep SIZEOF` which, amongst others, prints `#define __SIZEOF_FLOAT__ 4`, hence sizeof(float) is 4. – emacs drives me nuts Nov 01 '22 at 21:08
  • @emacsdrivesmenuts I just wanted to say that deliberately coding _undefined behavior_ is not a good approach such as `printf("Size of char: %ld byte\n",sizeof(charType));` or `printf("Size of char: %u byte\n",sizeof(charType));`. – chux - Reinstate Monica Nov 01 '22 at 21:37
  • I didn't propose UB. You can cast the values. I wrote an answer so you can understand how casts can be used. – emacs drives me nuts Nov 02 '22 at 11:02
0

which is completely unrealistic. What could be the reason for this?

The reason is that avr-gcc / avr-g++ implements size_t as a 2-byte type, see Type Layout in the avr-gcc ABI, but you are using print modifier %ld which expects long int, which is a 4-byte type in avr-g++.

What will work on the host and on AVR

  • You can cast the size_t operand so it matches the printf operand, for example use %u with (unsigned) sizeof(float), or use %lu with (unsigned long) sizeof(float).

  • As you are using GCC, there are built-in macros that are defined to the size of the basic types so they can be used in pre-processor directives like #if. In order to display them, you can run1

    > echo "" | avr-g++ -xc++ - -E -dM | grep SIZEOF
    

    which will print something like

    #define __SIZEOF_INT__ 2
    #define __SIZEOF_POINTER__ 2
    #define __SIZEOF_FLOAT__ 4
    
    

    what will be missing is __SIZEOF_CHAR__ which is always 1 per language standard.

What will not work with avr-gcc

Using print modifier for size_t like %zu will not work2 because AVR-LibC does not implement z. Reason behind this decision is to keep code size footprint minimal.

What won't work either is using streams like in std::cout << sizeof(float) ... because there is no libstdc++ in GNU tools for AVR.


1Explanation: -E advises the compiler to just pre-process the source file, and -dM prints the values of all defined macros. The source file is - which means to read from standard input, which is an empty file as of echo "" |. Option -xc++ specifies the language to be C++, which is required because there is no file extension for the input file.

2What's also not implemented is 64-bit types for example. And to support float, you'll need an extended version of vfprintf that supports it. This is usually accessed by means of -Wl,-u,vfprintf -lprintf_flt, because the default versions of printf (and scanf for that matter) don't support float.

emacs drives me nuts
  • 2,785
  • 13
  • 23