On Thu, Sep 5, 2024 at 9:02 PM Tomek CEDRO <to...@cedro.info> wrote:
> Hello world :-) > > I am working more in C recently than in for instance Python. Using > structures with pointers to structures or even pointers to functions > with pointer parameters. The problem is for instance some pointers > will not always be null but still invalid so the references cause > panic and reboot. > > Is there any way to handle especially the worst of exceptions in any > other way than kernel panic? > > For instance Python has this try-except block, C++ has this try-catch > block. This allows handling gracefully even worst exceptions (or only > selected ones). > > Is there anything like this in C / NuttX? :-) > > Any hints welcome :-) > Tomek > > -- > CeDeROM, SQ7MHZ, http://www.tomek.cedro.info > Hi Tomek, In C, if something isn't initialized, it could contain anything. Its value is "undefined." C doesn't have constructors and destructors like C++ that automatically set things up and tear them down. Every variable, struct, buffer, etc., should be assumed to contain undefined garbage until initialized explicitly. (Yeah yeah I know globals supposedly start at all 0s... blah blah, I don't trust that because it's often not true, especially in embedded!!!) Everything is garbage until initialized!! The trouble is, with pointers, that undefined value could be nonzero, so it looks like a valid pointer until you use it and then all hell breaks loose. Unfortunately, there is no way to test a pointer to see if it's garbage. You can only test for NULL (which means don't dereference it) or non-NULL (which means you should be okay to dereference it). But non-NULL could either be "okay!" or "garbage!" So you have to be very disciplined in the use of pointers to ensure they're not garbage. So, the pattern to use is: 1. Always initialize everything, including always initialize pointers to either NULL or to the object they'll point to. 2. When a pointed-to object goes out of scope or is freed, clear all pointers that point to it right away! When you do both of those things, you can check 'if (my_pointer == NULL)' ... before accessing it, and report an error or do some other reasonable action. This avoids crashes. :-) More about item #1: If it's a structure, a buffer, etc., I always start by memset()ing it to 0. If allocating a structure on the heap, there's a debate on whether to use malloc(), calloc(), zalloc(), etc. IMO the best to use is calloc() because it initializes the allocated memory to 0, so you don't have to do your own memset(). Also on some systems, calloc() may be implemented in a way that avoids needless memset() while still giving you a zeroed buffer. If your program does a tremendous amount of calloc()s, this can make some slight performance improvement over malloc() + memset(). More about item #2: Clearing pointers when done with them: e.g.: free(thing); thing = NULL; It is best to only ever have one pointer point to something because that's easier to keep track of, but sometimes this is not possible: Your application's data may be some kind of hierarchical or graph type thing with multiple structures pointing to each other in complicated ways. In this case, you really need to track down and clear all such pointers, and order of operations here is important! For example, maybe you have struct a, struct b, struct c, and they point to each other like this: a <-> b <-> c Meaning a has a pointer to b, b has a pointer to a, b has a pointer to c, c has a pointer to b. Now suppose you're going to free b. When you architect this program, you need to think about what happens if you free b. Does it mean you should also free a and c? Or does it mean a should point to c and c should point to a? Like this: a <-> c Or does it mean a and c are left alone but because b is gone, the links between these objects is gone: a c There's another thing to consider: what's keeping track of the existence of these objects? Does your program have global pointers to a, b, c? Or do you only keep a pointer to one of them, such as b, and reach the other ones through that? The answer to this question will help explain what should happen when you free b. Because if you only have the pointer to b in some global variable and you free b and destroy your ability to reach a and c, you'll leak memory. Admittedly this is a lot to think about, but this is more of a whole-program architecting of how you store and access information. It helps to reason through your data and draw it out on paper with arrows between objects. Each arrow is a pointer. Then start reasoning about what happens if you add another object? If you free an object? If you only take two things from this email, they should be: 1. Always initialize pointers to either point to a valid object or be NULL and 2. Always clear pointers back to NULL when the object is freed or goes out of scope And if you only take one thing from this email, it should be: 0. Never let a pointer contain "undefined" garbage!! (or point to some already freed thing) Hope this helps! Nathan