CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Passing Dynamically Sized Array of Structs By Reference

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Backfire



Joined: 12 Oct 2020
Posts: 44

View user's profile Send private message

Passing Dynamically Sized Array of Structs By Reference
PostPosted: Sat Sep 18, 2021 6:16 am     Reply with quote

Hello all,
I've set myself a challenge, but seem to be getting myself tied in knots with it, but I'd really love to know how to accomplish my end goal.

I'm attempting to pass an array of structures by reference, when the array itself has been created dynamically using the calloc() function. I think I'm struggling most with the indirection operator, but my values aren't being populated as I'd expect, any trying other code syntax that I think seems logical results in a compilation error. Hence I know my understanding is flawed, and I'd really appreciate some guidance.

I have a struct, let's say defined as below for example:
Code:

typedef struct{
    int8 StructMemberA;
}StructTypeName;


Then in my main routine, I dynamically create an array of this structure:
Code:

StructTypeName * DevicesOn[];
DevicesOn = (StructTypeName *)calloc(NoOfDevices, sizeof(StructTypeName));


And then I attempt to pass this array by reference to a function (example call also shown above):
Code:

Populate(&AirGapBoards, 0x00, 0x10);

void Populate(StructTypeName * Devices, int8 SAddr, int8 EAddr)
{
   int8 i = 0;
   
   // Attempting access here is an issue...
   Devices[i].StructMemberA = 0xFF;
}


I know that dynamic memory allocation on a PIC is more challenging, and I actually could statically size this for my implementation, but then I wouldn't learn anything new!

I'm just not sure where I'm going wrong! Any help would be awesome, thanks for taking the time to read this.
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Sat Sep 18, 2021 11:34 am     Reply with quote

Instead of
Code:

StructTypeName * DevicesOn[];


try using
Code:

StructTypeName * DevicesOn;


The first one says that DevicesOn is an array of *pointers* to StructTypeName, which doesn't match your calloc call at all.

The second just sets up a pointer that can be a pointer to either a single StructTypeName or an array of them.
Backfire



Joined: 12 Oct 2020
Posts: 44

View user's profile Send private message

PostPosted: Sun Sep 19, 2021 6:51 am     Reply with quote

Thanks for your reply Jeremiah.

I've been researching a little more on this, and have some other quick questions I'd appreciate some guidance on:

Most examples I've seen in C also include the struct keyword in the function definition, as in:
Code:
void f1(struct StructTypeName * Devices)


Is this required by the CCS compiler to your knowledge?

And also, given that another function modifies the array I'm intending to allocate memory to from within main(), does my memory allocation or pointer need the static keyword to ensure scope resilience?

Thanks for your advice so far,
BackFire.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Sep 19, 2021 7:03 am     Reply with quote

No, and yes....

The 'point' is you are just passing a pointer. The function only has to know
that is is a pointer, _but_ where it is used, needs to know the size of the
actual element accessed.
You can perfectly well simply pass the pointer as a void *, or int *, and
then cast this to the required size when you actually use it. However
understand that if the pointer is passed as a char *, and you then increment
it, it will increment by just one byte, while if it is passed as a pointer to a
type, then it will increment by the size of that type.

Separately, passing any pointer, to an object of variable size, really always
should also pass the size limit to the function. Otherwise there is no way
of preventing the function trying to access beyond the size of the array.
Result disaster... Sad
Backfire



Joined: 12 Oct 2020
Posts: 44

View user's profile Send private message

PostPosted: Sun Sep 19, 2021 7:37 am     Reply with quote

Hi Ttelmah,
Thanks for your advice. I certainly will be passing the dynamically established size as a function parameter. So no worries there.

Should my pointer and/or array created with calloc also utilise the static keyword, to ensure data retention in scope when calling out of the main() routine?

And I suppose I should also check with you that struct member access is possible in an array of structs pointed to by a pointer?

Many thanks for your help, as always.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Sun Sep 19, 2021 12:10 pm     Reply with quote

One comment here, you talk about 'passing by reference', but you are
only passing a pointer, not passing by reference.
To pass by reference, you use & in the declaration rather than *.
You need to declare the type being passed as :

void Populate(StructTypeName &Devices[], int8 SAddr, int8 EAddr)

Then 'Devices' will actually be the array value from the calling code,
and can be used in the function just as in the calling code.
This is a C++ feature partially supported by CCS. Don't know if it
will work for this situation.

With your current code, you are passing a pointer 'by address'. When the
function is called the current pointer value is copied 'to' the function. So
any changes after this point have no affect, but (of course), the function
is then dependant on the memory still being available.
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Mon Sep 20, 2021 9:19 am     Reply with quote

Backfire wrote:
Thanks for your reply Jeremiah.

I've been researching a little more on this, and have some other quick questions I'd appreciate some guidance on;

Most examples I've seen in C also include the struct keyword in the function definition, as in;
Code:
void f1(struct StructTypeName * Devices)


Is this required by the CCS compiler to your knowledge?

Since you are using "typedef" to define your type you don't need the struct keyword. If you had used just a regular struct declaration with a tag name instead, you are then supposed to use struct with it, but I don't know offhand if CCS follows the ANSI reqs for that or not as I always use the typedef method.

Backfire wrote:

And also, given that another function modifies the array I'm intending to allocate memory to from within main(), does my memory allocation or pointer need the static keyword to ensure scope resilience?

Thanks for your advice so far,
BackFire.


Not sure what scope resilience is off the top of my head. For variables static does three things:
1. Ensures the variable lives for the lifetime of the program. Having the variable declared in main also accomplishes this effectively, even without static, though I don't know if you actually need this. It depends on how you are using the variable and you haven't supplied enough info to know if that is needed specifically.

2. For variables declared at library level (outside of any function), it ensures that only functions in the same compilation unit can reference it. Again, not enough info to know if this is required.

3. Ensures the variable is at least 0 initialized if you don't initialize it. I'd still initialize it as it is a good practice to initialize it.

For functions, only #2 applies
Backfire



Joined: 12 Oct 2020
Posts: 44

View user's profile Send private message

PostPosted: Tue Sep 21, 2021 6:27 am     Reply with quote

Thanks to both of you for all your information and guidance. Code is now working, which makes a happy Backfire!

One last, related, request for guidance:
@Ttelmah, you mentioned that functions working with dynamically created arrays and such, should always pass a size parameter, of the allocated data space.

My C knowledge is strong enough to know that addressing dynamically created memory areas is very(!) dangerous, hence your recommendation.

My question is:
Is it standard/best practice to pass this value as a function parameter?
OR:
If the function itself, when called, should make another call to the size determination function?

I'm essentially trying to determine which of the below, A or B, is more common (or considered best coding practice), or if it simply doesn't matter!
Code:

A:
// Is it standard practise to include the size parameter in functions operating on dynamic memory...
void func1(char *Arr, int8 SizeOfArr)
{
   int8 i = 0;
   for(i=0 ; i<SizeOfArr ; i++)
   {
      // Do some stuff
   }
}


B:
// func2 is NOT being passed a size, but does make its own call to determine size...
void func2(char *Arr)
{
   int8 i = 0;
   int8 SizeOfArr = GetArrSize();
   
   for(i=0 ; i<SizeOfArr ; i++)
   {
      // Do some stuff
   }
}

int8 GetArrSize()
{
   // Assume this returns correct size
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Tue Sep 21, 2021 10:30 am     Reply with quote

It is normal to pass the array size as a parameter to the function being called.
[url]
https://www.geeksforgeeks.org/how-arrays-are-passed-to-functions-in-cc/
[/url]
This only 'works', provided you add to your code, functionality to ensure
the size if not passed.
jeremiah



Joined: 20 Jul 2010
Posts: 1314

View user's profile Send private message

PostPosted: Tue Sep 21, 2021 1:22 pm     Reply with quote

Another option is to use a struct to hold both the dynamically allocated array pointer and the size:

Code:

typedef struct{
   char * array;
   unsigned int16 size;
} char_array_t;


You call calloc to assign to the "array" field and just add the correct value to the "size" field. You can then just pass in the struct to the function. If you pass the struct by pointer, then you get one more level of indirection, but that usually isn't a huge overhead. The struct is also small enough that you can probably just pass it by value.

Note that is only a stylistic change vs simply passing the length as a parameter. They are effectively the same methods. This just pairs the length with the array always.

Side note. I know passing by reference was mentioned earlier. Be aware that if you pass by reference (See Ttelmah's post on the syntax), that the compiler will force function inlining, which may blow up your code size if the function is large and called multiple times.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group