2

I found this unexpected behaviour when writing code for Arduino, and I was able to replicate the same thing in MSVC15:

struct IDCount
{
    uint16_t count;
    char ID[20];
};
void test06b()
{
    IDCount item;
    char str[] = "0411010103, 8";
    char str1[20];

    // writing to struct member char array
    int res1 = sscanf(str, "%[^,], %d", item.ID, &item.count);
    printf("[%d] id: [%s]\tcount: [%d]\n", res1, item.ID, &item.count);

    // writing to a char array
    int res2 = sscanf(str, "%[^,], %d", str1, &item.count);
    printf("[%d] id: [%s]\tcount: [%d]\n", res2, str1, &item.count);
}

Results:

[2] id: []    count: [8]
[2] id: [0411010103]  count: [8]    

I spent quite a bit of time checking on the format specifier, before I narrowed down to the issue with the struct char array. Why isn't the char array in the struct not working? Any ideas?

Thanks in advance.

Update Changing from uint16_t to int works.

bot1131357
  • 877
  • 8
  • 25
  • You are using %d instead of %hu https://stackoverflow.com/questions/8699812/what-is-the-format-specifier-for-unsigned-short-int – Victor Padureanu Oct 30 '18 at 12:27
  • @VictorPadureanu `%hu` is for `unsigned short` which is not necessarily the same as `uint16_t`. – ach Oct 30 '18 at 13:01
  • @ach are right, I just tested on MSVS15 and %hu was ok in that situation. Thank you for pointing that out. – Victor Padureanu Oct 30 '18 at 17:01
  • @VictorPadureanu C/C++ are so frail languages, with so numerous ways to get into the domain of undefined behaviour, that simply testing on one compiler is not enough. Practically speaking, on virtually all modern platforms where `uint16_t` exists, it is defined as a synonym of `unsigned short`. However in theory, `unsigned short` is not required to have exactly 16 bits and may have more. In this case, `uint16_t` and `unsigned short` would be different types. That's why macros such as `SCNu16` are provided in the standard library. – ach Oct 31 '18 at 10:13

1 Answers1

0

You use a wrong format specifier to read item.count. For an uint16_t you have to use "%"SCNd16 or "%"SCNu16 instead of "%d".

With int on most modern platforms being 32 bit long, the data written into item.count spills over into item.ID. On a little-endian architecture, with a value fitting into a uint16_t, the spilling over bytes will be zeroes, effectively terminating the string in your buffer at position 0.

You could try to supply a negative value for item.count in your source string and see first two bytes of item.ID overwritten with 0xFFs.

ach
  • 2,314
  • 1
  • 13
  • 23
  • Thanks, I was so convinced it was a format specifier error that I overlooked memory corruption due to spill over from adjacent memory writes. . – bot1131357 Oct 30 '18 at 23:36