On Fri, Sep 6, 2024 at 4:02 AM Nathan Hartman <hartman.nat...@gmail.com> wrote:
> 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."

Thanks Nathan :-) That's the main concern here :-)

I was asking because maybe someone already invented some sort of
exception handling that I missed :D


> 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!!

Yup :-) I would like to avoid globals where possible :-)


> 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.

Yeah this is my problem right now :D


> 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. :-)

Yup this is my current approach, thanks for confirming this is okay, I
just need to keep myself disciplined :-) :-)

I had a part of external code recently where some stuff was not
initialized but referenced that caused crash. I just removed that
layer of code, created my own header files but with slightly different
internals, and coded my own functions, and it works fine without the
unnecessary bloat :-) :-)


> 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().

Thank you! I also prefer calloc() good to know its beneficial in many ways :-)


> 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.

Well I had to adopt external driver code that used some sort of messed
up glue layer like you mention above and that glue had pointers to my
functions. It was messed up and I could not get to what is wrong.. so
I removed the glue layer and presented my functions as the glue
layer.. and I get smaller elegant code with no mess :D

Nowadays I can see that sometimes its faster to write your own piece
than use existing solutions with problems.. surprisingly time summary
is usually better on "write your own stuff" :-P


> 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?

Yeah I try to keep things simple and self explanatory.. but sometimes
I have to work with some external code with different ideas.. the only
way I could track the problem was the JTAG debug but pins will be used
so I was thinking on other solution that could help on a final product
with this kind of situation where no Debug is possible. Log messages
were a bit too long to read and analyze and did not reveal the
problem.. and too many log messages also makes code bigger. I guess
the best choice is to keep code clean / simple (KISS) :-)


> 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

Thank you Nathan!! It helps a lot! :-) :-)

Have a good day :-)
Tomek

-- 
CeDeROM, SQ7MHZ, http://www.tomek.cedro.info

Reply via email to