SizeOf seemingly not working - Page 1
Forum section: electronics / general

author: ricko_uk
author_rank: Super Contributor
timestamp: November 07, 2021, 01:43:22 am
content: Hi,I am trying to get the number of array elements (elementsInArray) in a couple of ways but it does not seem to work. It returns either 1 or 4 instead of 20800.What am I doing wrong?Code:typedef struct {const uint8_t *data;uint16_t width;uint16_t height;uint8_t dataSize;} tImage;static const uint8_t image_data_TestImage_1_640_590x260 = {0x0FF ........... };const tTestImage xTestImage_1_640_590x260 = { image_data_TestImage_1_640_590x260, 640, 260, 8 };uint32_t LoadTestImageIntoMainImageArray(tTestImage* pstructSourceImage, bool bIndexOfMainImageArray){uint32_t elementsInArray = sizeof(*pstructSourceImage->data)/sizeof(uint8_t);elementsInArray = sizeof(*pstructSourceImage->data)/sizeof(pstructSourceImage->data);_nop();return elementsInArray;}LoadTestImageIntoMainImageArray((tTestImage*)&xTestImage_1_640_590x260, 0);Thank you

author: retiredfeline
author_rank: Frequent Contributor
timestamp: November 07, 2021, 01:48:19 am
content: sizeof is a compile time operator.Ask yourself what are the types in the expression:*pstructSourceImage->data => uint8_t **pstructSourceImage->data => uint8_tSo for 32 bit pointers, the quotient will be 4.And generally sizeof will not work the way you want on function arguments.

author: ricko_uk
author_rank: Super Contributor
timestamp: November 07, 2021, 01:55:40 am
content: Thank you retiredfeline,I tried derenferencing the second pointer but it doesn't compile.

author: Ian.M
author_rank: Super Contributor
timestamp: November 07, 2021, 02:19:43 am
content: The sizeof operator only works on arrays when the original definition (not declaration*) is in scope (i.e. when applied to the original array name, in the unit that it was originally created in, below its definition). As a pointer to an array is actually a pointer to its first element, sizeof cannot return the true size of arrays passed as function arguments, as it only 'sees' the size of one element.* ... unless the extern array declaration also explicitly includes its dimension(s), which makes the use of sizeof irrelevant anyway, as there is little point if the size is already known!

author: ricko_uk
author_rank: Super Contributor
timestamp: November 07, 2021, 02:20:29 am
content: Thak you both,Why would it not work on function arguments? Can I not de-reference the *pstructSourceImage->data?How do I then get the correct answer?Thank you

author: brucehoult
author_rank: Super Contributor
timestamp: November 07, 2021, 02:22:02 am
content: Quote from: ricko_uk on November 07, 2021, 01:55:40 amNo. That's not how C works.sizeof(*pstructSourceImage->data) is 1 because data is a pointer to a char.sizeof(pstructSourceImage->data) is 4 because data is a pointer and you're on a 32 bit machine.If you want a C function to know how large an array is then you need to tell it explicitly.For example, add a dataLen field to tImage and put 20800 into it. Preferably by using a const or #define to only write "20800" in one place.Note that you *can* do...sizeof(image_data_TestImage_1_640_590x260)... and get 20800 since they are 1 byte elements.

author: brucehoult
author_rank: Super Contributor
timestamp: November 07, 2021, 02:23:21 am
content: Quote from: ricko_uk on November 07, 2021, 02:20:29 amOf course you can dereference it. You just have to know how many elements are there by keeping track of it yourself via other means.

author: ricko_uk
author_rank: Super Contributor
timestamp: November 07, 2021, 03:00:08 am
content: Thank you Bruce,yes I initially had it as sizeof(image_data_TestImage_1_640_590x260) but because I need the additional structure and am planning to have other changes that reference the structure I was trying to keep it more clean getting the size through the struct.I will then stick it to the original approach.Thank you

author: retiredfeline
author_rank: Frequent Contributor
timestamp: November 07, 2021, 03:06:21 am
content: Quote from: ricko_uk on November 07, 2021, 02:20:29 amHere's what I mean:Code:uint8_t foo;int func(uint8_t *arg){return sizeof arg;}...int i = func(foo);You will not get 100, but sizeof uint8_t *;

author: ricko_uk
author_rank: Super Contributor
timestamp: November 07, 2021, 03:14:47 am
content: Got it, thank you

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 07, 2021, 03:18:21 am
content: Interestingly, in the tImage struct you already have a 'dataSize' member, which we could think was meant to hold the size of the buffer pointed to by 'data' precisely? If so, just use it. But if it was meant for that, you should probably use a larger in than uint8_t . I'm guessing your images may be bigger than what it can hold.

author: ricko_uk
author_rank: Super Contributor
timestamp: November 07, 2021, 04:00:00 am
content: @SiliconWizzard,In most cases yes, but not always.In those cases where I could do that, I could have to do 640 x 260 / 8 because I do have that data in the struct. But that would take longer to calculate which, in some cases, for timing reasons, might not be ideal.Thank you

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 07, 2021, 06:55:51 pm
content: You probably missed something here. I guess.If the sizes can be evaluated at compile time, then there's always a way you can assign them to this member. sizeof() was not the right way here because you tried using it on a pointer, while expecting the semantics of using it on an array. But you can still find ways to do it, just not the way you tried here.

author: golden_labels
author_rank: Super Contributor
timestamp: November 07, 2021, 07:04:38 pm
content: ricko_ukHave you actually profiled that? Have you compared it against the alternative solution of checking, on each access, if the size element is usable? You are doing double indirect dereference with offset. It would be unexpected if calculating the size would have a noticeably bad impact on the code. And, after all, your current code already attempts to do that calculation anyway.The size field is weirdly small, though, considering that it describes something that can have rows and columns identified by 16-bit indices.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: November 10, 2021, 05:24:46 am
content: Quote from: ricko_uk on November 07, 2021, 01:43:22 amFirstly, what is that "SizeOf" you mentioned in the title?Secondly, the trick you are trying to use only works when you apply `sizeof` to an array. You are not applying it to an array. That is what you are doing wrong.

author: TheCalligrapher
author_rank: Regular Contributor
timestamp: November 10, 2021, 05:25:58 am
content: Quote from: retiredfeline on November 07, 2021, 01:48:19 amIn what programming language? In C++ `sizeof` is a compile-time operator. In C `sizeof` can easily be a run-time operator, if applied to a variably modified type.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 10, 2021, 09:31:46 am
content: Code:typedef struct{uint8_t dataSize:uint16_t bytes;uint16_t width;uint16_t height;const uint8_t *data;} image_t;static const uint8_t test_image_data = {0x0FF ........... };const image_t test_image = {.dataSize = 1, // In bytes, not bits!.bytes = sizeof(test_image_data),.width = 640,.height = 260,.data = test_image_data,};elementsInArray = sizeof(*pstructSourceImage->bytes)/sizeof(pstructSourceImage->dataSize);In any case, you're using uint8_t*, why is dataSize there?

author: golden_labels
author_rank: Super Contributor
timestamp: November 11, 2021, 12:19:50 am
content: Quote from: TheCalligrapher on November 10, 2021, 05:25:58 amExcept for VLAs, thesizeofoperator is never evaluating its operand and always yields a constant expression. Given the limited use of VLAs, in practice it is nearly always yielding a compile-time constant.

author: SiliconWizard
author_rank: Super Contributor
timestamp: November 11, 2021, 07:36:42 pm
content: Quote from: retiredfeline on November 07, 2021, 03:06:21 amYou make an interesting point, but unfortunately with a poor example.Since your 'arg' argument here is a pointer, function argument or not, it would just return the size of the pointer.A better example of why it doesn't work as you'd expect in function arguments:Code:uint8_t foo;int func(uint8_t arg){return sizeof arg;}...int i = func(foo);Yes you can absolutely use array types in function arguments. But it's merely a cosmetic addition here.Even declared as a proper array, sizeof on 'arg' will still return the size of a pointer rather than the size of the array.Note that GCC (and probably others) correctly warns you about it:Code:warning: 'sizeof' on array function parameter 'arg' will return size of 'int *' [-Wsizeof-array-argument]This is, IMO, the context in which it would look a lot less intuitive.As opposed to this, of course, C pointers do not hold any information on the size of the pointed to object, or anything else. Pointer arithmetic would not make any sense otherwise, or would be a complete mess. C pointers are very basic. Some languages implement some kind of "fat" pointers, which can "hold" a number of attributes, but that's not the case for C at all.