3

When I run the following code

#include <stdio.h>

typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;

const uint8_t Symbols[] = {
    0xCA,0x04,// size
    0x0B, // width
    0x0B, // height
    0x00, // first char
    0x04, // char count
    0x01, 0x02, 0x03, 0x04,// char widths
    // font data
    0x00, 0x00, 0x20, 0x00, 0xF0, 0x07, 0x08, 0x04, 0x84, 0x07, 0x84, 0x07, 0x84, 0x07, 0x0E, 0x04, 0xF0, 0x07, 0x20, 0x00, 0x00, 0x00, // 0
    0x10, 0x01, 0x08, 0x01, 0x04, 0x01, 0x04, 0x01, 0x08, 0x01, 0x10, 0x01, 0x10, 0x01, 0x08, 0x01, 0x04, 0x01, 0x04, 0x01, 0x00, 0x00, // 1
    0x18, 0x00, 0xFF, 0x07, 0x18, 0x00, 0x00, 0x00, 0x80, 0x01, 0xFF, 0x07, 0x80, 0x01, 0x00, 0x00, 0x0C, 0x00, 0xFF, 0x07, 0x0C, 0x00, // 2
    0x00, 0x02, 0x80, 0x05, 0x60, 0x04, 0x18, 0x04, 0x04, 0x04, 0x72, 0x05, 0x04, 0x04, 0x18, 0x04, 0x60, 0x04, 0x80, 0x05, 0x00, 0x02 // 3


};

typedef struct
{
    uint16_t size;   //2
    uint8_t width;   //1
    uint8_t height;  //1
    uint8_t first_char;  //1
    uint8_t char_count;  //1
    uint8_t *font_widths;
    uint8_t *font_data;
} _graphics_font;

_graphics_font* test;
uint8_t* font_st;
uint8_t temp;

int main(void)
{
    test = (_graphics_font*)&Symbols;
    font_st = (uint8_t*)&test->font_widths;
    temp = font_st[0]+font_st[1]+font_st[2]+font_st[3]; //1+2+3+4 = 10
    printf("temp=%d",temp);
  return 0;
}

in C, I'm expecting that the pointer font_st will point exactly after the 6-th Byte of the Symbols array. Therefore the result printed should be 10. Instead of that the pointer is allocated to the 9-th byte, missing every time 2 bytes and wrongly to my expectations is printing 7 as a result. Why and how could that be?

Important update is that the using instead the line:

temp = font_st[-2]+font_st[-1]+font_st[0]+font_st[1]; //1+2+3+4 = 10

works.

judoka_acl
  • 375
  • 2
  • 22
  • 1
    the member `font_widths` of `_graphics_font` struct is a pointer. Thus, try and change the line `font_st = (uint8_t*)&test->font_widths;` to `font_st = test->font_widths` – Xxxo Sep 14 '15 at 11:16
  • besides, `uint8_t *font_widths` will not be initialized correctly from the data in `Symbols`, because it is a *pointer* and not an actual array of bytes; same for `font_data`, of course. – JimmyB Sep 14 '15 at 11:19
  • This will assign to font_st address 0x01 0x02 0x03 0x04 and the result will be access memory violation under windows and total destruction under embedded. – judoka_acl Sep 14 '15 at 11:22
  • @HannoBinder this is a C program - everything can be represented as an array of bytes – judoka_acl Sep 14 '15 at 11:26
  • Wait, I think I see now how you're emulating an array through a pointer to a pointer, where the latter actually isn't a pointer. In that case, there's no sense in declaring `font_witdths` as a pointer in the first place; any type would do. But consider using `uint8_t[] font_witdhs` instead to avoid the confusion. – JimmyB Sep 14 '15 at 11:43
  • http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member – Lundin Sep 14 '15 at 11:45
  • Have you actually checked `sizeof(_graphic_font)`, because it's probably padded – Elias Van Ootegem Sep 14 '15 at 11:45
  • 1
    You should use proper (de)serialisation and not cast the array to a `struct` pointer. You are dealing with padding, alignment and endianess issues. Doing it right avoids may hours debugging, not to speak of portability issues. For your problem, however, you should use a normal `struct` with the data being a _flexible array member_. This avoids all trouble. – too honest for this site Sep 14 '15 at 11:49
  • Just checked it 16 bytes... It should be 14. Where are the missing 2 bytes? – judoka_acl Sep 14 '15 at 11:50
  • Why "should it be 14" bytes? It is very likely on 32 bit archs `struct`s to have always a multiple of 4 or even 8 octets. – too honest for this site Sep 14 '15 at 11:53
  • Likely is not helping particularly. I'm requesting a solid explanation general for all 32-bits and 64-bits platforms. – judoka_acl Sep 14 '15 at 11:58
  • Also even if it's 16 bits, that doesn't explain why the pointer is at 9-th bit not in the 7-th. – judoka_acl Sep 14 '15 at 12:02
  • This line`test = (_graphics_font*)&Symbols;` might invoke UB due to misalignment. – alk Sep 14 '15 at 12:25
  • "explanation general for all 32-bits and 64-bits platforms" -- That'll be hard to do, as padding &c. depends on the compiler used, the target architecture, and the optimizations activated at compile time. – JimmyB Sep 14 '15 at 12:25
  • @HannoBinder I've not said it's gonna be easy... – judoka_acl Sep 15 '15 at 12:02

4 Answers4

2

This behaviour is most likely caused by struct padding.

Try the following:

#include <stdio.h>

#pragma pack(push, 1)

.... (all other code)

#pragma pack(pop)
Community
  • 1
  • 1
alain
  • 11,939
  • 2
  • 31
  • 51
  • The link is about MS C compiler, I'm working with the gc. – judoka_acl Sep 14 '15 at 11:55
  • 1
    [GCC should support it too](https://gcc.gnu.org/onlinedocs/gcc/Structure-Packing-Pragmas.html), but you could also use `__attribute__((__packed__))` in the struct declaration. – alain Sep 14 '15 at 12:00
  • Didn't work. I still got 7 as result instead of 10. And the pointer is at 9-th bit instead of the 7-th. I'm afraid padding is not the problem. – judoka_acl Sep 14 '15 at 12:06
  • I'm sorry it worked out. I was using the temp = font_st[-2]+font_st[-1]+font_st[0]+font_st[1]; and the result was 7 which confused me to think it's the old result. How I know how to fix it. Just need to know why that has happened... – judoka_acl Sep 14 '15 at 12:11
  • Wow, that's really great! Thank you! – judoka_acl Sep 14 '15 at 12:19
1

As to the location of single elements in a struct, read about padding and packing, e.g. Structure padding and packing.

You could try it like this:

typedef struct glyph {
  uint8_t char_width;
  uint8_t[] glyph_data;
} glyph_t;

typedef struct font_t {

  uint16_t size;   //2
  uint8_t width;   //1

  ...

  glyph_t[] glyphs;

} font_t;

That way, you can avoid some issues you are facing due to the dynamic addresses of the data, i.e. those which depend on the value of char_count.

Of course, if the size of glyph_data is not a constant, you have to provide your own access logic, like

uint8_t* p_glyph = (uint8_t*)(font->glyph_data[0]);

for ( int i = 0; i < requiredCharIndex; i++ ) {
  p_glyph += total_size_of_glyph_in_bytes( (glyph_t*)p_glyph );
}

glyph_t* result = (glyph_t*)p_glyph;
Community
  • 1
  • 1
JimmyB
  • 12,101
  • 2
  • 28
  • 44
  • Let's concentrate on my question and the solution will come later. Why the pointer font_st is not at the 7-th byte, but in the 9-th? – judoka_acl Sep 14 '15 at 11:41
  • Check the padding and try declaring your struct as packed. – JimmyB Sep 14 '15 at 11:45
  • Try and print the address that font_st is pointing at, and then print the address of Symbols + 7 (where you are expecting it to be) and also print the address of Symbols + 9 (where you think it is). You will see that all three values will be different, its an illusion that font_st lies on the 7-th position given by the fact that font_st isn't pointing to any part of Symbols. – Fireeyes Sep 14 '15 at 11:46
  • Or in other words why temp = font_st[-2]+font_st[-1]+font_st[0]+font_st[1]; is working correctly? – judoka_acl Sep 14 '15 at 11:47
0
test = (_graphics_font*)Symbols;
font_st = (uint8_t*)test->font_widths;

I didn't check that, but at simple sight I would say that &s was wrong in that context. Symbols and test are already pointers, so you are referencing to the address to that pointers.

Abend
  • 589
  • 4
  • 14
  • 1
    Well, you should have, because you're assigning data to a pointer. Which means - the self-destruct sequence has been activated. The sequence may not be aborted. BOOOOMMMM! – judoka_acl Sep 14 '15 at 11:37
  • The address of an array in fact is the same as the address of the array's 1st element, to which an array decay, when being assigned to a pointer. – alk Sep 14 '15 at 12:22
  • Exactly, if you want to do that you should use &array[0] or simply array. Both ways are equivalent each other. – Abend Sep 14 '15 at 15:12
-3

You are not initializing correctly the two pointers in your struct.

test = (_graphics_font*)&Symbols;

On this line you are copying byte by byte from Symbols to test, which means that font_widths will point to the address made up by combining 0x01, 0x02, 0x03, 0x04, which contains garbage data.

Why 4 bytes? because every pointer in C has a size of 4 bytes regardless of the data it is pointing to.

Moreover, font_data will have the same behaviour.

Solution:

You have to use memcpy:

test->font_widths = (uint8_t*)memcpy(test->font_widths, Symbols + 6, 4*sizeof(uint8_t));

The same goes for font_data.

Fireeyes
  • 98
  • 3
  • First the data 0x01, 0x02, 0x03, 0x04 is not garbage, second I've never asked why 4 bytes. My question was why to the pointer font_st is assigned the 9-th byte of the array instead of the 7-th. – judoka_acl Sep 14 '15 at 11:33
  • font_widths is not pointing to the data 0x01, 0x02, 0x03, 0x04, its pointing to the address made up by those bytes., which is indeed garbage data. – Fireeyes Sep 14 '15 at 11:38
  • As I said, font_widths is a pointer, when you do the assignation in the first line of main, you are not populating the array that font_widths is pointing to, you just tell it to point to an invalid location. – Fireeyes Sep 14 '15 at 11:39
  • This happens because assignation happens byte/bit-wise, the first byte from test will be equal to the first byte from Symbol, so goes for the second, and the third, and so on. You cannot initialize an array just by assignation, you have to make font_widths point to the address where your data is, that's why you need to use memcpy. – Fireeyes Sep 14 '15 at 11:42
  • His main problem is related to struct padding, which this answer doesn't address. – Lundin Sep 14 '15 at 11:45