Author Topic: Mind over bugs: C pointer edition  (Read 10210 times)

0 Members and 1 Guest are viewing this topic.

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Mind over bugs: C pointer edition
« on: June 29, 2021, 01:40:56 pm »
C pointers are confusing and bug-prone even for experienced programmers.  Because they are also very powerful, we don't want to get rid of them in C.  (We do switch to other languages to get rid of pointers and their associated baggage, though.  I'm only saying that one cannot get rid of them in C.)

Compilers are not much help in detecting C pointer bugs, either.  The kinds of bugs that many C programmers see at a fraction of a second glance, seem and are perfectly acceptable code to C compilers.

So, what is one to do to avoid proverbially impaling oneself on C pointers?

We change how we think, how we use pointers, so that we develop within ourselves that which the compilers cannot do for us.
Because this is squishy human stuff, what I will suggest below, will not work for everyone, and will be different to what others suggest; but I sincerely hope others will tell what they do, if they do avoid pointer bugs in their C work product, because that is just more tools in the box to choose from.
But do be critical, and make sure you don't listen to those who feel that it is perfectly okay for C code to crash if you call it with different parameters than they do, if the clock is past five in the evening, or because Sagittarius is in Aquarius or some other mystical reason.

This would work so much better as a Dave-style EEVBlog video.  (I'm particularly reminded of the capacitance multiplier one, #1116.)  Unfortunately, I have the on-screen personality of a slightly annoyed gerbil dealing with a frozen potato –– so possibly entertaining in the ooh, you stupid little critter sense, but that's it ––, so I cannot do it myself.  The best I can do, is note that since these bugs keep cropping up endlessly in this forum, and I may have a tool both learners and advanced C mongers can use to avoid creating those bugs, in the hopes of it being useful and spurring other ideas for others to post in this thread, is to write it here.
« Last Edit: June 29, 2021, 01:44:06 pm by Nominal Animal »
 
The following users thanked this post: DiTBho

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #1 on: June 29, 2021, 01:50:22 pm »
Use macros, that you have tested, and dont forget to use it.
*even now i still have a crash, i am personally thinking it is 50/50 chance the compilers fault.
You can not trust the best compiler with big linecount in combination with pointers.
Once i get past, lets say 10.000 lines, bugs appear automaticly that dont exist.
I think thats why they all make .dll files for windows.
« Last Edit: June 29, 2021, 01:52:11 pm by Jan Audio »
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #2 on: June 29, 2021, 03:50:56 pm »
THING_COUNT - 1

*sorry, coulndt resist.
« Last Edit: June 29, 2021, 03:52:36 pm by Jan Audio »
 

Online gf

  • Super Contributor
  • ***
  • Posts: 1178
  • Country: de
Re: Mind over bugs: C pointer edition
« Reply #3 on: June 29, 2021, 04:04:40 pm »
THING_COUNT is correct  :-+  Not THING_COUNT - 1
[ It is supposed to emulate the past-the-end iterator of C++ containers, which points behind the last element. ]

It may be confufing, though, for C guys which don't program in C++ as well.
« Last Edit: June 29, 2021, 04:07:30 pm by gf »
 
The following users thanked this post: newbrain

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #4 on: June 29, 2021, 04:07:05 pm »
Not always maybe, i have no clue about bank-switching, just to be safe you better :

Something * const p_end = &things[THING_COUNT - 1];

void algorithm( Something * const p_begin, Something * const p_end ) {
  for( Something * p_it = p_begin; p_it <= p_end ; p_it++ ) {
 

Online DiTBho

  • Super Contributor
  • ***
  • Posts: 3915
  • Country: gb
Re: Mind over bugs: C pointer edition
« Reply #5 on: June 29, 2021, 04:19:41 pm »
So, what is one to do to avoid proverbially impaling oneself on C pointers?

Personally I try to avoid every "pointer-arithmetic" because (for me) it's too prone to fail  :-//
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Online gf

  • Super Contributor
  • ***
  • Posts: 1178
  • Country: de
Re: Mind over bugs: C pointer edition
« Reply #6 on: June 29, 2021, 04:34:49 pm »
Not always maybe, i have no clue about bank-switching, just to be safe you better :

C++ containers and iterators follow a different philosophy. The end() iterator of a container is by definition always an invalid one, not pointing to an existing element of the container.
Therefore it is also exluded in the loop for( Something * p_it = p_begin; p_it != p_end ; ++p_it )

As I already said, this may be confusing for non-C++ programmers.
Since the topic of this thread are C pointers, I wonder whether C++ examples should be rather avoided?
OTOH, comparison with other languages can also give some insights.
So at the end I'm not sure whether such a comparison is rather confusing or nevertheless helpful for C guys.
 
The following users thanked this post: Jan Audio

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #7 on: June 29, 2021, 04:47:00 pm »
... I sincerely hope others will tell what they do, if they do avoid pointer bugs in their C work product, because that is just more tools in the box to choose from.

"Don't be afraid of death. Be afraid of unlived life." [unknown source]

You cannot teach a baby to walk by making him afraid of falls. Rather you motivate him to walk. He must fall many times before he can walk, and he will. There's no way you can prevent this. This is a part of the learning. What you can do, however, is to teach him not to cry when he falls, but rather laugh, stand up, and walk again. Eventually, the baby will learn how to walk. By any means, this doesn't prevent him from falling. When he grows up he will walk and he will fall. But when he falls this won't feel like a tragedy because he knows how to stand up and how to walk further.

Similarly, you cannot learn programming by being afraid of bugs. The whole generation of programmers have grown up being afraid of bugs. A bug is a tragedy to them. Therefore they cannot program, and they will never be able to program. You must not be afraid of bugs. You will encounter bugs no matter what. It's what you do when you encounter a bug that counts.
« Last Edit: June 29, 2021, 04:52:39 pm by NorthGuy »
 
The following users thanked this post: Siwastaja, DiTBho

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #8 on: June 29, 2021, 04:51:48 pm »
What is a pointer in C?

No, we're not interested in how the standards define them; we are only interested in getting a maximally useful and workable concept here.  For the reasons outlined in the initial post, and because during the last two decades or so my own C work product contains significantly fewer pointer-related bugs than average open source code, and the only difference I can tell between my approach and others is the concepts used, I must assume that is what makes the difference.

The most useful definition I have seen, is a three-way split: function pointers, void pointers, object pointers.

  • Function pointers are very powerful, but not bug-prone for whatever reason.  So, the standards' definitions for these seem to work fine.
    An important detail, however, is that systems like Linux do not actually use the C standard rules for function pointers, but a much stricter subset as defined by POSIX. 
    In particular, POSIX specifies compatibility between function pointers and non-function pointers, especially void pointers.  This makes things like dlsym() work without silly casting shenanigans required by a strict reading of the C standard.

    (You'll see similar stuff regarding unions and type punning.  Some insist that things like struct sockaddr as used by POSIX bind() to bind a socket to an address family specific address is not compliant to the C standard, and cannot work correctly according to a strict reading of the C standard.  Be that as it may, practice trumps theory and standards every time, and the massive amount of POSIX C that works very well indeed (with very few bugs related to struct sockaddr, I might add) indicates that that which needs "fixing" is not struct sockaddr, but the C standards instead.)

    (Note that in embedded environments where a curious mix of freestanding C and C++ is used –– Arduino, for example ––, even though POSIX is nowhere near, the compilers –– GCC and LLVM/Clang in particular –– always provide the POSIX-compatible pointer behaviour instead of the strictly-by-the-standard you-may-not-do-that-even-if-it-would-work-well-in-practice silliness insisted by Language Lawyers.  So, if you like, instead of "POSIX" you can substitute "GCC and LLVM/Clang" at least.)

  • Void pointers are the idiot siblings of all pointers.  Using the stricter POSIX definitions, void pointers –– void * –– really boil down to the address they point to, plus optional qualifiers for accessing that address (const, "I promise I won't try to modify"; volatile, "undetectable other things may modify this at any point, so make no assumptions about its value, compiler", in particular); and both object pointers and function pointers are compatible with void pointers.

    In my experience, the best approach to void pointers is to consider them the most basic import/export form for function and object pointers.
    You do not use them to access anything; you use them only when you need to convey the address to be accessed without any further assumptions about it (except those aforementioned qualifiers that involve how and not what).

    Simply put, you use void * only when you cannot reasonably use an object pointer ot a function pointer, and treat it as the transport format.  (This has serious implications to what a programmer must consider when doing this; I'll discuss those a bit further down.)

  • Object pointers are those that point to data.

    An object pointer is not just an address one can use.  If that is all it were, it would be a void pointer instead.

    Object pointers have non-void type, and that type specifies the size of the memory region referred to, indirectly (due to alignment restrictions) assumptions about the pointer itself, plus the type qualifiers like const and volatile related to the manner the region may be accessed.

    In visualization terms, only void pointers are reasonably described as arrows or spears or javelins; object pointers are more like buckets or boxes, with label stickers describing what the buckets can hold (think of "this bucket can hold acids" or "only for foodstuffs"), and handling stickers ("fragile") corresponding to const/volatile access qualifiers.  Function pointers can be thought of as little sticky notes describing where to go, with optional notes as to how to present oneself (types of parameters passed) and what to accept bringing back.
One can say that almost all pointer bugs occur when object pointers point to something other than intended.  To fix this, we find a way to think of and use object pointers, so that we more easily detect the cases where mis-pointing may occur.

The first step in this is to realize that the error does not occur when you use ("dereference", is the term) such a pointer, even though this is exactly where your debugger and other tools will direct your focus at.

The true error occurred when the pointer value was first constructed.

(Well, almost.  The exception to the case is use-after-free bugs, since there the pointer was absolutely fine, just left forgotten when the region of memory it points to was freed, quite unexpectedly from this pointers' point of view.  Leeroy Jenkins of pointers, shall we say?  Except I lean towards thinking that the error did occur when this pointer was constructed even in this case, by not having a mechanism to mark this pointer invalid if/when the memory region is freed, or by ensuring these pointers are not accessible anymore when the memory region is freed.  If you look at C garbage collectors like Boehm GC, they do exactly this; and Boehm GC happens to have papers showing that it does this deterministically.  So it is doable, and not as hard as one might think.)

Typical bugs when using pointers

There are several classes of bugs related to pointers, but the most common ones are off by one, out of bounds, and use after free.
  • Off by one

    Perhaps the most common off-by-one bug is forgetting that strings in C are just sequences of non-nul char/unsigned char/signed char, terminated by a nul, '\0', and that nul terminator must be accounted for in the size needed to store that string.

    Another common case is when backtracking, say removing trailing whitespace from a string, and forgetting to check if you are already at the very first char, and backtracking past the beginning.

  • Out of bounds

    This is the nastiest class of pointer bugs in my opinion, because they are so hard to track down.  Indeed, the core reason why proprietary kernel modules are marked "tainted" by the Linux kernel is not because of some ideological zealotry, it is because such modules can accidentally dereference pointers and scribble over any data structures at all (because Linux has a "monolithic" kernel architecture, one where kernel modules and drivers work within a single domain without boundaries; as opposed to "microkernel" architectures, where modules and drivers are protected from each other, paying a tiny performance cost, but moreso tying human developers to inter-domain information passing techniques –– and the Linux kernel developers like to be free to experiment, update, even replace stuff; that's why they won't limit themselves to a fixed binary interfaces within the kernel itself, for example).  You see, even when you after hours of work discover that the machine or process crashed bcause "ah ha! This memory was garbled!", you have ZERO indication of exactly what code did the scribbling-over!  You found the murder scene, but zero evidence of the murderer.

    The simplest verifiable example that is obviously dangerous that you cannot get your compiler to warn about was posted in another thread here by ataradov.  Tweaking it a tiny bit to more closely resemble the typical bug I see in real life, it can be written as

        extern int bar(volatile char *p);
       
        int foo(int i)
        {
            volatile char buffer[20];
            return bar(buffer + i);
        }

    Above, buffer is volatile only to stop the compiler from making assumptions about it.  The bug occurs, because we construct a pointer buffer + i while knowing that i could be any value at all representable by an int, with no guarantees that it is between 0 and 19 inclusive, that the code as written implicitly assumes.

    I can only assure you that if you have written sufficiently low-bug and/or debugged sufficient amount of C code, your eyes detect the possibility/likelihood of that code having a bug within a fraction of a second.  It is just that common, you see; it doesn't even involve conscious thought.  To me, it is analogous to seeing a pot hole in the road.

    If you do find a C compiler that supports a compilation/warning flag that makes it complain or warn about that code, let me know: I haven't found one for GCC or Clang (till 2021-06-29 at least).

  • Use after free

    Unlike the above two bugs, this is kinda-sorta not the pointers own fault.  It occurs, when the pointer points to memory that has been freed (or otherwise made inaccessible, say a memory map since unmapped), and is used ("dereferenced") afterwards.

    The programming technique called poisoning is useful in helping to detect these bugs.  It simply means that before the memory is freed, the contents are set to easily detected, preferably invalid, values – poison.  That way, an use-after-free bug that does not lead to an immediate crash of the process, can be detected by the accesses yielding "poisoned" values. (Remember, most C library implementations do not make freed memory inaccessible, only marks it internally available for subsequent allocations. Only much larger continous allocations on architectures providing virtual memory tend to use memory mapping techniques, so that freeing such an allocation causes a subsequent access to raise a segment violation – in Linux/Unix/BSDs, the SIGSEGV signal.  So, poisoning is a simple, cheap, practical indicator tool – but not perfect: if the memory is re-used by subsequent allocations, it is likely the poison has been replaced with non-poison data, making the underlying bug much harder to detect.  Since the allocation pattern often varies between debug and non-debug builds, these often belong to the Heisenbug class: those that vanish when you try to debug or instrument them.)

    The practice of NULLing pointers when they are freed is derided by some as "wasteful" or "unnecessary", but it does turn a sub-class of use-after-free bugs into NULL pointer dereferences, and the latter are easily detected by most C compilers at runtime; and since the point where the dereference occurs tells us which pointer was used, we have immediately discovered the murder weapon, and are much further in the debugging process than we would otherwise be.  In practice, this looks like
        free(ptr);
        ptr = NULL;

    Yes, the overall cost is clearing the pointer value, and in compiled machine code on architectures like x86, AMD64, and ARM64, the cost is effectively zero.

    This does not help with the large subset of use-after-free bugs where the pointer used is a cached copy of an earlier value, however.

    NULLing pointers is not just a bug-combating technique, however.  In POSIX C, for example, a function reading and processing an input file line by line can be written as

        #define  _POSIX_C_SOURCE  200809L
        #include <stdlib.h>
        #include <stdio.h>
        #include <ctype.h>
        #include <errno.h>
       
        #define  SEE_ERRNO  -1
        #define  I_ATE_IT   -2
       
        int for_each_line(FILE *src,
                          int (*handler)(long  linenum,
                                         void *base, size_t size,
                                         char *line, size_t linelen,
                                         void *context),
                          long *linenum_at,
                          void *context)
        {
            if (!src) {
                errno = EBADF;
                return SEE_ERRNO;
            } else
            if (ferror(src)) {
                errno = EIO;
                return SEE_ERRNO;
            }
       
            long     linenum = 0;
            char    *line = NULL;
            size_t   size = 0;
       
            /* If the caller is interested in the linenum, or has provided us with an initial number
               (lines consumed before the first one), use that. */
            if (linenum_at)
                linenum = *linenum_at;
       
            while (1) {
                /* Read a new line, without line length limitations (other than avaiable memory). */
                ssize_t  len = getline(&line, &size, src);
       
                /* len == 0 should not happen, but if it did, it would indicate end of input.
                   len < -1 should not happen, but if it did, it would be a C library bug.
                   We differentiate between all these cases by examining feof(src) and ferror(src).
                */
                if (len < 1)
                    break;
       
                /* The logic here is that we increment linenum whenever we read a new line.
                   So, if linenum_at == NULL or *linenum_at == 0, the first line read will be
                   linenum == 1. This is just one possible convention; you do you. */
                linenum++;
       
                /* If the caller is interested, then keep them up to date.
                   Volatile just tells the compiler to stop trying to be clever about it. */
                if (linenum_at)
                    *(volatile long *)linenum_at = linenum;
       
                /* Both GCC and Clang – the C compilers I prefer to use – generate slightly
                   better code for pointers as opposed to pointer+offset expressions.
                   So, taking care to avoid off-by-one errors, we use pointers from here on end. */
                char  *ptr = line;
                char  *end = line + len;
       
                /* Trim off trailing whitespace, including newline characters.
                   Note the care taken to avoid off-by-one errors.
                   Also, the cast *is* necessary for isspace(), see man 2 isspace for details.
                */
                while (end > ptr && isspace((unsigned char)(end[-1])))
                    *(--end) = '\0';
       
                /* Trim off leading whitespace, if any. */
                while (ptr < end && isspace((unsigned char)(*ptr)))
                    ptr++;
       
                /* Do not bother passing empty lines to the handler function;
                   and if there is no handler function specified, we're done with this line. */
                if (ptr >= end || !handler)
                    continue;
       
                /* If the first non-whitespace character of the line is a # or ;,
                   we treat the entire line as a comment, and won't pass it to handler. */
                if (*ptr == '#' || *ptr == ';')
                    continue;
       
                /* Let handler handle the line now. */
                int  result = handler(linenum, line, size, ptr, (size_t)(end - ptr), context);
                if (result == SEE_ERRNO) {
                    /* Error; abort.  Return errno to the caller. */
                    const int  saved_errno = errno;
                    free(line);
                    errno = saved_errno;
                    return SEE_ERRNO;
                } else
                if (result == I_ATE_IT) {
                    /* The handler took responsibility for the dynamically allocated buffer. */
                    line = NULL;
                    size = 0;
                } else
                if (result) {
                    /* Error; pass return value to caller. */
                    free(line);
                    return result;
                }
            }
       
            /* The line buffer is no longer needed.
               Also, free(NULL) is safe and does nothing, so no need to check it first. */
            free(line);
            /* We could add
                   line=NULL;
                   size=0;
               here, but since the rest of the function does not look at them at all,
               I shall omit it here.  Just to show that rules of thumb are just that. */
       
            /* Did we just get an I/O error instead of just an end of input? */
            if (ferror(src) || !feof(src)) {
                errno = EIO;
                return SEE_ERRNO;
            }
       
            /* No errors, everything fine.  We return a nice zero. */
            return 0;
        }

    If the handler function returns I_ATE_IT, it means that it decided to reuse the entire dynamically allocated region containing the line, starting at base, having size size.  (context is just a custom parameter not often needed; it is there only so that if the original caller has some context it wishes to pass to or share with the handler, it can do so without using global or thread-local variables.)

    If we had passed the handler only ptr, it could not do that: you cannot pass ownership/responsibility of a dynamically allocated region of memory using a pointer that points to somewhere inside that region, because we don't have a way to determine the region (or even its base address) from the pointer.  That's why we pass the base and size of the dynamically allocated region to the handler, too.

    Because getline() allocates new dynamic memory buffer whenever the pointer is NULL and size zero, we only need to NULLify the pointer and set the size to zero to keep going.  While this case may look so very contrived if one thinks of it only as an example of why NULLing a pointer inside a function is sometimes necessary for correct operation, take a step back and look how useful, simple, but yet powerful the function is.  It tracks line numbers, gives the handler the ability to grab ownership of the buffer if it wants, handles comment lines and removes leading and trailing whitespace.

    I wrote the above function using the concepts I talked earlier in this post, so to evaluate those concepts, look at the code.  And try to find a scenario where it could bug out (except when given a bad FILE state to begin with, or by having handler() contain a bug).
How to avoid pointer bugs?

The key, I claim, is to realize that the bug does not occur when you use ("dereference") a pointer, but when you construct the pointer in the first place.
(Ignore technicalities and language lawyerism for a second.  The context of that statement is the human understanding of how pointers behave, and how to think in ways that help you create fewer bugs than you would if you relied on pure language lawyerism and only on technically correct, impeccable definitions and statements.  Minds are squishy, and need to be treated on their own terms.)

That initializing/setting/constructing expression is what you need to examine.  You cannot do "security" checks later on, because the information on what the pointer should be, is already lost.  Security cannot be bolted on: it is either integral to the process, or it does not exist.

Checks like if (!src) in the above function do technically check if src is NULL or not, but they are not bounds checks: they are just sanity checks, intended as an extra check to catch the most stupid pathological (as in "sick") cases.  (A bounds check is one that is aware of the extent of the target to be accessed, and verifies that the access is within those bounds.  A sanity check is a check against never-useful/workable/valid inputs; they only catch the insane cases that cannot ever work.)

I myself use sanity checks mostly because they make testing easier.  (Okay, and because I like the belt-and-suspenders approach; but chalk that last one to being willing to pay the tiny extra cost just to reduce the likelihood of getting bit by a bug.  You could say I have a bit of the programming bug equivalent of arachnophobia, I guess.  It does not mean my house has fewer arachnids than anyone else, it's just that I'm willing to go a bit further in trying to avoid encountering any than what most consider "normal" or "reasonable".  Actually, I do have a bit of arachnophobia, but I actually like spiders, because where I live there are no spiders harmful to humans in any way, and they keep mosquitoes and gnats and other blood-sucking pests I hate in check.  So, I like spiders being close by, I just have an unfortunate atavistic reaction to seeing them.  With programming bugs, I may be overcompensating and overblowing the importance of trying to fix bugs and create less of them, and try to make ones code detect and deal with unexpected input or errors, instead of letting them be expressed as unwanted/unexpected/unexplainable behaviour – bugs.)

Remember, because pointer bugs are caused at the moment of their construction and not when the pointers are used/dereferenced, such sanity checks mean that if I do find a bug, the sanity check has already ruled out whole classes of causes (due to preceding code) by that single, cheap, sanity check.  Furthermore, things like accidentally passing a never-valid value (say, a NULL pointer), always caught by the sanity check, suddenly transform from catching a bug into reporting to the caller that they used invalid/unacceptable parameters.  No bugs necessarily involved, as it turns say a NULL pointer from a bug, into explicitly ignored or rejected input value.  Think of the free(NULL) case before you make your mind about that.

The extension of this basic idea is to be suspicious of any expression constructing a pointer value.

The cases where pointers are used/dereferenced, are fait accompli: a done deal, a fact of life; and no matter how deeply we would examine that part, we'd gain zero additional information whether that access is safe and non-buggy or not.

Of particular interest is whenever we convert a void pointer to an object pointer or a function pointer.  (This is something you do and consider if you start treating void pointers as an import/export method for pointers you cannot express with better fidelity, as I mentioned much earlier.)
There is not much we can do, programming-wise, at that point to check the pointer; the only thing I can think of is to make sure it is sufficiently aligned per the hardware and ABI alignment rules, but because C does not really have existing tools to express such checks in a portable manner (like say a built-in is_unaligned(type-qualified pointer) operator or function), it is not useful to try and think of how to achieve that (beyond perhaps a simple binary AND mask on the least significant bits of the address, with the mask being a compile-time constant).

Instead, we must turn to our fellow developers, and start discussing what non-program-expressed guarantees the caller (or whoever provides us with the void pointer in the first place) can provide us with and whether they could instead pass us more information.

See how that logic meshes with the for_each_line() function implementation above, especially what we provide to the callback handler() function on every call?

A typical knee-jerk reaction from C programmers to that, is fear/dislike of passing that much more data, and consider it a waste of resources.  However, for example on AMD64 on Linux (using AMD64 SysV Application Binary Interface, i.e. hardware calling convention), all six arguments to the handler function are passed in registers (rdi, rsi, rdx, rcx, r8, and r9, to be specific, with the return value in eax (the 32 least significant bits of the rax register; also used to hold the function pointer value in most patterns generated by clang/llvm and gcc), and the added register pressure does not affect the efficiency of the function in any measurable scale, since the bottleneck here is always the getline() standard library function.

In other words, if you felt any dislike at adding two seemingly superfluous parameters to a callback function, just so that the callback could *safely* grab complete ownership and responsibility for some dynamically allocated memory, you need to start fighting against your feelings or intuition, because it is demonstrably wrong here.

And herein lies the entire Mind Over Bugs trick: we don't look for outside assistance to help combat these bugs.  We use our own minds to do so, by retraining ourselves to think in terms where such bugs become visible – even glaring! –, and thus easier to deal with, and hopefully much, much rarer as a result.  Assuming we do care about bugs in the first place; many programmers don't, because they don't affect their pay slip either way, and bugs are dull.

That does not mean we become immune, though.  I am fully aware that there may be a pointer-related bug even in the above for_each_line() example function above, I am just confident that its likelihood is low, based on past experience and statistics.  But, because I am not certain, I used lots of comments explaining my reasons for the key expressions, so that if anything gives me pause (or pause to any of my colleagues, collaborator, or cow-orkers, including Nominal Animal of the Future, who tends to see things a bit differently than Nominal Animal of Today, who sees things differently than Nominal Animal of Yesteryear did), I can start the examination by comparing whether the code matches the assumptions and reasoning explained in the comments.

Again, Mind Over Bugs, this time from the other side.  We use comments to describe our reasoning, and this gives two separate tools.  Note that such comments are orthogonal to the code: you cannot reliably infer them from the code, and they definitely do not describe what the code does; they only describe developer intent and reasoning.

One tool such comments provide is that we can now compare the reasoning to known rules and behaviour – for example, a comment above says free(NULL) is safe, and the code does rely on this; so we can take a look at man 3 free or a C standard, and check.  (The C standard does explicitly say that for free(ptr), "if ptr is a null pointer, then no action occurs".)

The second is that now we can compare the code and the comments, to see if they actually match.  Even the best C programmer on the planet has brainfarts – because they are human, and every single human with measurable brain function occasionally has those; perhaps more gently called "thinkos" or thinking equivalents of typos.  You don't call a professor an idiot just because one in one hundred instances of "which" in their output is mistyped as "witch".  They could be, but that's not a valid reason to make the categorization.  Similarly with thinkos, because proper software design is complex work, and occasionally a human brain just stumbles over a detail.  Often, those details are the smallest ones, so comfortable and well known that one is doubly ashamed of the error.  One of my own stumbling blocks is memset(), and the order of its fill value and size parameters.  Some of my colleagues think less of me because I always have a terminal open, and I check –– I even have an alias for man -s 2,3,7,5 function –– instead of being a True Professional Who Knows the Words of Power and Wisdom and Power and Truthiness and Never Reads Manuals That Are For Lesser Beings Anyway, and wing it.

Combine this with expressions that construct pointers.  If you have a comment that says that the caller is responsible for ensuring a parameter is within a specific interval, the human-readable description of that function better have that assumption/responsibility very visible, or most callers just won't know about the responsibility, and bugs will ensue.

Even if the entire codebase is written single-handedly by yourself, the person that wrote a piece of code two weeks, two months, or two years ago, does not have the same context and understanding they have right now.  This is because humans only stay in the same context and understanding, if they are dead.  Dead people do not often write code.  The reason is not actually that they're dead, it is because constructive creation is a process that affects the creator almost as much.
In less grandiose terms, when you create something new, you learn, and that changes how you do things.
Even in the minimum "I write this only once and will never look at it again" case, when most believe the comments are not needed/useful/required, the comments are really almost the only way we can detect when we learned something we previously did not that caused us to generate buggy code.

A case in point is the rubber duck debugging method, where you grab a rubber duck, any other inanimate object, or your favourite pet animal that likes to hear you talk but understands basically none of it, and describe the problem you are having.  Because of how the human mind does, this act of expressing the problem in a different manner, a spoken language, affects how your mind processes the problem; and surprisingly often, about midway in your description, the parts start fitting together and you realize the solution.
So even in the case where the code is for you yourself only and is only written once and never ever read again, those comments are useful because they can provide the same functionality for your Mind that the rubber duck target does.

In a very real sense, all of this can be boiled down to the idea that your mind is just another tool, and the concepts and words it uses as its own sub-tools determine how those tools are used, we-the-minds must redefine our concepts and use words that help us solve problems and accomplish tasks.  Free speech aside, relying on standards and other authorities to give us the concepts and we'll just use those, is to limit oneself to the preset toolset of those authorities.  It is the intellectual equivalent of tying ones hands behind their backs.
« Last Edit: June 29, 2021, 04:58:18 pm by Nominal Animal »
 
The following users thanked this post: T3sl4co1l, Siwastaja, newbrain, DiTBho

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #9 on: June 29, 2021, 06:00:52 pm »
I recommend the following reference: https://cwe.mitre.org/
(Which goes way beyond C and pointers, but there's a lot of stuff regarding those.)
 

Offline Feynman

  • Regular Contributor
  • *
  • Posts: 192
  • Country: ch
Re: Mind over bugs: C pointer edition
« Reply #10 on: June 29, 2021, 06:56:52 pm »
I develop a lot of safety critical firmware in C and bugs due to pointers are VERY rare. Most of the time it is just a matter of applying coding standards (e. g. MISRA) that define rules for pointer arithmetic AND enforcing them via static code analysis tools and/or reviews, of course. Additionally you can do things like letting your static analysis tool assume that every pointer passed to a function is potentially NULL and complain if there is no execution path for ptr==NULL, for example.
Switching to C++ is not a bad idea as well since it provides additional compile time checks and containers like std::array that provide boundary safe access.
 
The following users thanked this post: newbrain

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #11 on: June 29, 2021, 07:18:48 pm »
I develop a lot of safety critical firmware in C and bugs due to pointers are VERY rare. Most of the time it is just a matter of applying coding standards (e. g. MISRA) that define rules for pointer arithmetic AND enforcing them via static code analysis tools and/or reviews, of course. Additionally you can do things like letting your static analysis tool assume that every pointer passed to a function is potentially NULL and complain if there is no execution path for ptr==NULL, for example.

That would be my experience as well. IMHO, the site I mentioned above is still worth a read; if nothing else, to actually understand *why* the coding rules you have to stick to are what they are.

Bugs due to incorrect use of pointers are of course much more common in non-critical software. Often due to the fact strict coding rules are not enforced in this case, or very rarely so.

Sticking to reasonable coding rules, plus proper static analysis and testing makes things a lot better.

AFAIK, even if that goes beyond the scope of this thread, according to a number of studies already, the *main* source of software bugs, at least software developed in professional settings with a decent process, is incorrect or lacking specifications, rather than programming errors. And that's particularly visible with safety-critical software. Not saying that programmng errors are not a problem, but those should be avoided and/or caught up thanks to reasonable experience and a proper development process. It's much, much harder to ensure specifications are correct and exhaustive.


 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #12 on: June 29, 2021, 09:07:37 pm »
Sticking to reasonable coding rules, plus proper static analysis and testing makes things a lot better.

When you make a decision about something, you can either

- follow rules (or commands)

or

- think

Therefore, if you follow rules, you don't think.

Thinking is necessary. Rules explicitly suppress thinking. This is why bureaucracies stifle progress.

When you develop software (or hardware alike) you should think about various possible scenarios - what happens if the user presses the wrong button, what happens if a component fails etc. You design your software in a way which provides reasonable outcomes in all possible scenarios. If you don't think about this and a situation happens which you haven't foreseen, your software will malfunction. This is the most common source of bugs, and there's no rules which can substitute thinking in such situation.

Another thing you need to do - make everything as simple as possible - data structures, transactions, interactions. If you build something overly complex you may lose control over it and then you cannot handle bugs any more - fixing bugs produces new bugs making the situation worse and worse. This is the second most common source of bugs. And this cannot be helped with rules neither. Worse yet, rules may force you towards more complex solutions and consequently more bugs - for example if you can produce a simple and elegant solution with function pointers, but function pointers are forbidden.

The third, you need to understand everything in your system. If you don't really understand how underlying hardware works, your code may work in some cases, but in others it fails. You simply cannot fix such bugs unless you go back to basics. This happens very often where people use various libraries which creates an illusion that they don't need to understand what these libraries do. This often works, but occasionally it'll bite. You cannot fix this with any rules.

Finally, there are rare events. Like interrupt happening in exactly the wrong moment. It may be ok in the development and testing, but if you give your product to many customers the event will not be so rare anymore. You need to take courage and investigate every seemingly random glitch - there may be a problem behind it. Rule based programming won't fix this neither.

If someone always gets pointer errors in C and don't know how to find/fix them, he is simply incompetent and needs more learning/education/experience etc. Rules will not help an incompetent person to do the job. He'll find a way to screw up even if all the rules are followed.

So, my advise is - forget the rules, think.
« Last Edit: June 29, 2021, 09:12:02 pm by NorthGuy »
 
The following users thanked this post: spostma

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21686
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Mind over bugs: C pointer edition
« Reply #13 on: June 29, 2021, 09:30:46 pm »
When you make a decision about something, you can either

- follow rules (or commands)

or

- think

Therefore, if you follow rules, you don't think.

Thinking is necessary. Rules explicitly suppress thinking. This is why bureaucracies stifle progress.

This seems misguided, or perhaps not well considered?

If the rules were so strict that thinking weren't necessary, it wouldn't be a Turing-complete language anymore.  Problems expressible in it, might still require some problem solving to craft, but in general the set of programs that can be written in such an environment is impractically small.

The claim is true of the example; often, the point of bureaucracy is to prevent meaningful action, to serve a broader purpose (e.g. maintaining the budget, power, freedom, etc. of the large and privileged organization).  The programming equivalent is one of those toy sandbox environments, where you can pick and choose certain function blocks, but only those, and only connected in the ways prescribed.  You can't write arbitrary code on such a system.

Rather, I would suggest looking at it this way.  Engineering, at its most basic, is the art of satisfying constraints.  There are many fields to engineer within, and there are many ways to engineer a design within each of those fields.  Sometimes the constraints are broad, general, or nebulous (budget or timeline restrictions); sometimes they are specific (a reactor which must be resistant to X, Y and Z chemicals, withstand process temperature and pressure, etc.).  Sometimes they are counterproductive, but the results are nonetheless impressive, if not necessarily practical (not sure what products would exemplify this, but personal projects like discrete CPUs are very much in this vein, eschewing any integrated circuits, or anything more integrated than gates and registers, say).

And it's still very much an art form.  Many artists speak warmly of restrictions.  Doing a painting with certain materials or methods, or removing a color from the palette, or in a certain style, etc.  Other artists may be more free-form in principle, but inevitably take on skills and habits through repetition (the restrictions might well be self-imposed and subconscious).

In exactly the same way, programmers write code with identifiable styles, whether they mean to or not.

So, the purpose of these rules, is to restrict the solution domain to a subset of the complete language, which is easier to be correct within.  That may be some imposition to the developer, requiring extra boilerplate, or roundabout solutions where a simpler but less reliable or verifiable method might do (e.g. employing dynamic memory or recursive functions, neither of which is required for a lot of problems), but it's not about making whole solutions impossible.

...At least, as far as I know.  Hey, I've never read any of those code standards, don't take my word for it.

Tim
« Last Edit: June 29, 2021, 09:39:08 pm by T3sl4co1l »
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #14 on: June 29, 2021, 09:47:38 pm »
I agree with T3sl4co1l here. His post is more elaborate than what I would have done myself, so I won't add much. Just saying @NorthGuy : your post was way too extreme. I would never advocate sticking to rules over thinking. Both are not mutually exclusive. Actually, when they are, that means one of the two has been taken to a dysfunctional extreme.


 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #15 on: June 29, 2021, 10:09:53 pm »
Both are not mutually exclusive. Actually, when they are, that means one of the two has been taken to a dysfunctional extreme.

But they always are. Thinking leads you towards making your own decisions. Rules are decisions made for you.

For example, you cannot have a rule which forbids the use of function pointers and at the same time think whether to use a function pointer or not.

Rules are useful where it is necessary to suppress thinking. Say, you cannot decide whether to drive on the right side of the road or on the left one, this must be mandated by local rules.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #16 on: June 29, 2021, 10:31:14 pm »
So, the purpose of these rules, is to restrict the solution domain to a subset of the complete language, which is easier to be correct within.  That may be some imposition to the developer, requiring extra boilerplate, or roundabout solutions where a simpler but less reliable or verifiable method might do (e.g. employing dynamic memory or recursive functions, neither of which is required for a lot of problems), but it's not about making whole solutions impossible.

Sure. It is possible to program within rules. It is debatable whether the rules are capable of decreasing the number of bugs. It is debatable whether the benefit of the rules is worth the efforts exerted to follow and enforce them.

I have some rules which I follow too. Say, when I type, always indent everything by 2 characters. If it's not intended by 2 characters, it is not written by me. But I understand that this doesn't make my code any better and I cannot find any rational explanation for this.
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #17 on: June 30, 2021, 01:41:48 am »
But they always are. Thinking leads you towards making your own decisions. Rules are decisions made for you.
If that were exactly true, how would you explain chess?

The complexity in the amount of creativity/choices/options is roughly of the same order for certain size C programs, so the comparison is apt.

(In my opinion, that all has to do with the concept of choice or option.  I do not know the exact term for that, sorry.  Rules that restrict choices do behave like you describe, but rules do not necessarily restrict choices in any meaningful way.  In chess, the number of possible games is so large that the rules that dictate how each single piece may move, does not reduce the choice among all possible games in a manner that would matter to a human (or to even a large computer system, really).  Even in the choice-restricting end of the spectrum, Conway's Game of Life has just four rules with the only choice being the initial configuration (which then absolutely dictates the evolution of the system), but has been proven to be Turing-complete – a classification which we can interpret as "able to compute any finite computable problem you choose".)
« Last Edit: June 30, 2021, 01:49:28 am by Nominal Animal »
 

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21686
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Mind over bugs: C pointer edition
« Reply #18 on: June 30, 2021, 01:50:07 am »
Another microcosm of the same principle: the utter success of digital logic.

Sure, we could craft analog computers from transistors in arbitrary connections; they might not be as stable or consistent, but the sheer richness of continuum variables and nested exponential functions would surely be extraordinarily powerful.

But good luck designing them.  Such a circuit is impossible to reason about.  How many ways can transistors be connected?  It's factorial in the number of elements!  Let alone how to get stable operation vs. temperature, voltage, consistent manufacture, etc.

So we add rules to them.  Digital logic is a subset of analog, where we can use fewer rules to describe the elements.  The resulting elements are far easier to reason about, and much more stable and consistent.  We can use simple logic simulations, and guard-banding min/max design rules, to verify circuit operation.  Instead of being challenged by mere hundreds of transistors, we can bring billions to bear!

Although I wonder how much of that remains true today; I have a suspicion that FinFETs aren't nearly as good as their larger predecessors, and as a result the design rules and constraints are far more complex than they used to be.  But in parallel, the development tools have also advanced, and can supply enough intelligence, and compute power, to solve those problems, even on such a vast scale.

Ironically, it may well come to be, that that process of increasing design complexity continues, and we get rules and constraints so broad that they're basically describing individual transistors themselves.  Then we'll have structures, and solutions therein, basically like hardware neural nets: a sea of transistors, connected seemingly randomly, that nonetheless seems to give quite good results (of course, extensively testing its function space remains just as impossible as it was decades ago already; bugs won't ever be going away).  Indeed we have such hardware already, though I don't know if it's implemented as digital cores, or if they're working with analog networks (or relevant elements like memristor arrays).


So, from this example -- I'm not sure how one can see the development of digital logic, and not appreciate that there can be some rules, which when applied, greatly facilitate development for various and sundry purposes.  It doesn't take any stretch to imagine that, in other fields of study, there might be other rules that can be applied, to similar benefit.  I'm not sure why one would submit a non sequitur as a counterexample!

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #19 on: June 30, 2021, 02:27:45 am »
If that were exactly true, how would you explain chess?

Chess is a game. It is abstract and defined by rules. If you break rules, this is not a chess game anymore and it doesn't make any sense to play it. But if a player could move the chess pieces as he wish, but his opponent had to follow chess rules, do you think his opponent would have any chance against him?

Our life is not a chess game. It is not restricted by rules. The reality we live in is restricted by laws of physics. You cannot change them. You must use them as they are. But if you could defy the laws of physics and change them for your project as you wish, don't you think you would be the best engineer ever?

Bureaucratic rules are not the same as laws of physics. They're not imposed on us by reality we live in. They're pushed on us by bureaucrats, for various reasons which have nothing to do with our engineering goals, but rather serve the bureaucrat's purpose helping them to escape responsibility, make their workers deplorable etc. If you must follow the programming rules by the decree of your employer, there's no choice for you (except if you want to to quit). But the same thing apply as with the laws of physics or with rules of chess - if you can defy these rules you will be able to do much better job.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #20 on: June 30, 2021, 03:26:12 am »
So we add rules to them.  Digital logic is a subset of analog, where we can use fewer rules to describe the elements.  The resulting elements are far easier to reason about, and much more stable and consistent.  We can use simple logic simulations, and guard-banding min/max design rules, to verify circuit operation.  Instead of being challenged by mere hundreds of transistors, we can bring billions to bear!

These are not rules, these are engineering decisions. Nobody forces car manufacturers to use round wheels. Wheels are round because square ones would work much worse. Nobody forces us to use digital logic. It is just much more tolerant to noise because there's only two levels.

We're talking about programming rules which force people to write programs in certain way while there's no technical reason behind the restriction. The theory behind this is that the rules help people avoid bugs which makes software less buggy, but there's no empirical evidence that this helps. Modern software is not remarkably efficient and bug free. I'm convinced that it is possible to write better software - less bloated, works faster, is faster to develop and easier to maintain. I'm sure many people do this. But they're rare and far between.
 

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21686
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Mind over bugs: C pointer edition
« Reply #21 on: June 30, 2021, 03:36:58 am »
You seem to disregard the possibility that there could ever be a rule which is beneficial.  Can you give examples of rules, used in software design, that you find restrictive or indeed useless?  And can you provide evidence and arguments to defend them as such?

I'd be really interested to see such arguments; but if it's just a knee-jerk "I can do anything I want, no one can tell me what/not to do!", that's pretty weak.

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 

Offline Feynman

  • Regular Contributor
  • *
  • Posts: 192
  • Country: ch
Re: Mind over bugs: C pointer edition
« Reply #22 on: June 30, 2021, 06:14:21 am »
Sticking to reasonable coding rules, plus proper static analysis and testing makes things a lot better.
So, my advise is - forget the rules, think.
Well, I couldn't disagree more :)

For our example (C pointers) the tools for more or less avoiding bugs altogether are out there and well proven. And whether you like it or not these tools are rules and their enforcement with things like static code analysis. Thinking about problems that have already been solved is just a waste of mental resources most of the time. In a professional environment there is no shortage of problems your mental resources are better invested in. And no one says that you have to follow rules 100% of the time without thinking. That's what deviation processes are for. As a matter of fact nothing taught me more about the C language than the rationale and enforcement of rules, e. g. thinking about a lint warning ("Why is there a warning?", "Does the warning make sense here?", ...).

Car traffic without rules and everyone just "thinking" would be a total mess, even if only the smartest people where participating. The worst code I'm seeing in real life is often a result of people trying to be smart, respectively.

So, if it's about live or death (not exclusively), you better do not forget the rules ;)
« Last Edit: June 30, 2021, 08:14:49 am by Feynman »
 
The following users thanked this post: newbrain

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: Mind over bugs: C pointer edition
« Reply #23 on: June 30, 2021, 08:00:33 am »
In-house programming rule sets often/usually/sometimes (pick your favorite) do have a few items that indeed do not make a lot of technical sense, or are overgeneralizations.

Even the C standard which is the "ultimate" rule book for C, has some rules which developers have decided to ignore as real-world needs win over theory.

Bad rule sets are regularly overridden by those experienced enough programmers who indeed "think". Rule sets are still needed to guide less experienced programmers who understand well enough to be clearly beneficial (and not damaging) to the project. Analogy would be a bank teller who definitely is beneficial for the business and does their job well but still is not allowed to go to the big vault with all the cash in it. It's a good rule because it only limits something that is not required in fulfilling the actual task, but exposes a risk of damage if done. Then such rule is easy enough to enforce.

A very typical example of a bad rule is forbidding goto in C, despite the fact that goto is useful, clear and documenting (due to the label) in breaking out of 2D nested loops or error handling; OTOH, working around the rules usually generates code of higher bug risk, for example generating complex loop conditions or temporary "do break break" variables. I have also seen rule sets where the ?: operator is disallowed, despite the fact very few use it "for fun" without a good reason already. The ?: operator has potential to reduce code copy-paste which is always a massive maintenance risk.

But I do think most of the rules in most rule sets do more good than harm (pretty handwavy statement, though).

Finally, if you are in the transcend position of Understanding Everything, then you can easily see which rules are not preventing you from thinking - these will be the Good Rules, and which are - these will be the Bad Rules. A good rule set then won't limit you the slightest, although if you are already Perfect^tm, it's just unnecessary - but not harmful.
« Last Edit: June 30, 2021, 08:04:21 am by Siwastaja »
 
The following users thanked this post: newbrain

Online DiTBho

  • Super Contributor
  • ***
  • Posts: 3915
  • Country: gb
Re: Mind over bugs: C pointer edition
« Reply #24 on: June 30, 2021, 10:06:52 am »
"Goto" is allowed, but only in critical code, where it makes sense.
Abuses are prone to produce spaghetti code, terrible for the ICE.
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Feynman

  • Regular Contributor
  • *
  • Posts: 192
  • Country: ch
Re: Mind over bugs: C pointer edition
« Reply #25 on: June 30, 2021, 11:12:06 am »
"Goto" is allowed, but only in critical code, where it makes sense.
Abuses are prone to produce spaghetti code, terrible for the ICE.
Correct. MISRA for example has an "advisory" rule that forbids the use of goto. But MISRA also acknowledges the fact that code without goto could be less transparent than the goto it replaces, i. e. if the code required additional flags to get rid of goto. And MISRA therefore defines rules for using goto in a safe way, e. g. only jumping "down" and to a point in the same function.

The harmfulness of goto is pretty much known since 1968 and every serious set of rules should generally discourage a programmer from using it.
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #26 on: June 30, 2021, 02:12:39 pm »
Sorry havent looked to my macros since i made them :

Code: [Select]

//------------------------------------------------------------------------------
// Linked List Macros ( uses 2 dummys ( first & last ) )
// note : in the object constructor : pList is not valid
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// LIST_VARIABLES   adds variables to list class
//------------------------------------------------------------------------------
#define LIST_VARIABLES( objectname ) \
friend class objectname; \
objectname*first,*last,*current;

//------------------------------------------------------------------------------
// OBJECT_VARIABLES   adds variables to object class
//------------------------------------------------------------------------------
#define OBJECT_VARIABLES( objectname , listname ) \
friend listname; \
objectname*next,*prev; \
listname*pList;

//------------------------------------------------------------------------------
// LIST_CONSTRUCT   use this in the constructor
//------------------------------------------------------------------------------
#define LIST_CONSTRUCT first = last = current = NULL;

//------------------------------------------------------------------------------
// LIST_DESTRUCT   use this in the destructor
//------------------------------------------------------------------------------
#define LIST_DESTRUCT( objectname ) \
current = first; \
\
while( current ) \
     { \
     objectname*destroy = current; \
     current = current->next; \
     delete destroy; \
     }

//------------------------------------------------------------------------------
// LIST_INIT   creates 2 dummys for fast linking and initialize
//------------------------------------------------------------------------------
#define LIST_INIT( objectname ) \
/* create dummys */\
if( !( first = new objectname ) || !( last = new objectname ) )return false; \
\
/* link dummys */\
first->next = last; \
last->prev = first; \
\
/* terminate for deletion */\
last->next = NULL;

//------------------------------------------------------------------------------
// LIST_REMOVE ( removes all objects exept the 2 dummys )
//------------------------------------------------------------------------------
#define LIST_REMOVE( objectname ) \
current = first->next; \
\
while( current != last ) \
     { \
     objectname*destroy = current; \
     current = current->next; \
     delete destroy; \
     } \
\
/* link dummys */\
first->next = last; \
last->prev = first;

//------------------------------------------------------------------------------
// LIST_ADDFRONT   adds new object in front of list
//------------------------------------------------------------------------------
#define LIST_ADDFRONT( objectname ) \
objectname*temp = new objectname; \
temp->pList = this; \
\
temp->prev = first; \
temp->next = first->next; \
first->next->prev = temp; \
\
first->next = temp;

//------------------------------------------------------------------------------
// LIST_ADDBACK   adds new object in back of list
//------------------------------------------------------------------------------
#define LIST_ADDBACK( objectname ) \
objectname*temp = new objectname; \
temp->pList = this; \
\
temp->next = last; \
temp->prev = last->prev; \
last->prev->next = temp; \
\
last->prev = temp;


//------------------------------------------------------------------------------
// LIST_FUNCTION   uses functions from whole list exept the 2 dummys
//------------------------------------------------------------------------------
#define LIST_FUNCTION( func ) current = first->next; while( current != last )current->func();

//------------------------------------------------------------------------------
// OBJECT_DELETE   delete object and link prev with next !use as last thing!
//------------------------------------------------------------------------------
#define OBJECT_DELETE \
next->prev = prev; \
prev->next = pList->current = next; \
delete this;

//------------------------------------------------------------------------------
// LIST_EMPTY   check if list is empty
//------------------------------------------------------------------------------
#define LIST_EMPTY first->next == last


They just work, for C++.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #27 on: June 30, 2021, 02:38:08 pm »
You seem to disregard the possibility that there could ever be a rule which is beneficial.  Can you give examples of rules, used in software design, that you find restrictive or indeed useless?  And can you provide evidence and arguments to defend them as such?

I'll give you one example, which hit me personally.

When I was young I wanted to take a job as a programmer and I went to a job interview. They asked me whether I use "goto" while coding. And I said yes. They didn't hire me, told me that I didn't know modern (back then) programming theory and gave me an academic paper which explained that using "goto" is pure evil and leads to unmanageable spaghetti code. If it was now, it would probably also said that if I use "goto" I also kill all the chances for the compiler to optimize the code, but this wasn't the case back then.

So, I made a resolve to improve my coding and decided to learn how to program without goto. I made a rule for myself never to use "goto". It was difficult at first, but after several months I got much better at this and I was very proud of my code which didn't have any "goto"s. It took me few years to realize that it doesn't bloody matter. I wasted countless hours doing this, and all I gained is a bad habit (which is difficult to get rid off, even now).

Lost time alone wouldn't be that bad. But the thinking about "goto"s shifted my attention from important things. Instead of thinking about what I program, finding better data structures and algorithms, I was wasting my efforts on eliminating "goto". I'm not proud of what I did. Now I understand that I would be better off using my own brain.

... "I can do anything I want, no one can tell me what/not to do!".

May be you worded this a little bit too extreme, but that's what I advocate. Programmers are perfectly capable of deciding how to program.
 
The following users thanked this post: Siwastaja, Nominal Animal

Online gf

  • Super Contributor
  • ***
  • Posts: 1178
  • Country: de
Re: Mind over bugs: C pointer edition
« Reply #28 on: June 30, 2021, 03:13:15 pm »
Sorry havent looked to my macros since i made them :

Code: [Select]

//------------------------------------------------------------------------------
// Linked List Macros ( uses 2 dummys ( first & last ) )
// note : in the object constructor : pList is not valid
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// LIST_VARIABLES   adds variables to list class
//------------------------------------------------------------------------------
#define LIST_VARIABLES( objectname ) \
friend class objectname; \
objectname*first,*last,*current;

//------------------------------------------------------------------------------
// OBJECT_VARIABLES   adds variables to object class
//------------------------------------------------------------------------------
#define OBJECT_VARIABLES( objectname , listname ) \
friend listname; \
objectname*next,*prev; \
listname*pList;

//------------------------------------------------------------------------------
// LIST_CONSTRUCT   use this in the constructor
//------------------------------------------------------------------------------
#define LIST_CONSTRUCT first = last = current = NULL;

//------------------------------------------------------------------------------
// LIST_DESTRUCT   use this in the destructor
//------------------------------------------------------------------------------
#define LIST_DESTRUCT( objectname ) \
current = first; \
\
while( current ) \
     { \
     objectname*destroy = current; \
     current = current->next; \
     delete destroy; \
     }

//------------------------------------------------------------------------------
// LIST_INIT   creates 2 dummys for fast linking and initialize
//------------------------------------------------------------------------------
#define LIST_INIT( objectname ) \
/* create dummys */\
if( !( first = new objectname ) || !( last = new objectname ) )return false; \
\
/* link dummys */\
first->next = last; \
last->prev = first; \
\
/* terminate for deletion */\
last->next = NULL;

//------------------------------------------------------------------------------
// LIST_REMOVE ( removes all objects exept the 2 dummys )
//------------------------------------------------------------------------------
#define LIST_REMOVE( objectname ) \
current = first->next; \
\
while( current != last ) \
     { \
     objectname*destroy = current; \
     current = current->next; \
     delete destroy; \
     } \
\
/* link dummys */\
first->next = last; \
last->prev = first;

//------------------------------------------------------------------------------
// LIST_ADDFRONT   adds new object in front of list
//------------------------------------------------------------------------------
#define LIST_ADDFRONT( objectname ) \
objectname*temp = new objectname; \
temp->pList = this; \
\
temp->prev = first; \
temp->next = first->next; \
first->next->prev = temp; \
\
first->next = temp;

//------------------------------------------------------------------------------
// LIST_ADDBACK   adds new object in back of list
//------------------------------------------------------------------------------
#define LIST_ADDBACK( objectname ) \
objectname*temp = new objectname; \
temp->pList = this; \
\
temp->next = last; \
temp->prev = last->prev; \
last->prev->next = temp; \
\
last->prev = temp;


//------------------------------------------------------------------------------
// LIST_FUNCTION   uses functions from whole list exept the 2 dummys
//------------------------------------------------------------------------------
#define LIST_FUNCTION( func ) current = first->next; while( current != last )current->func();

//------------------------------------------------------------------------------
// OBJECT_DELETE   delete object and link prev with next !use as last thing!
//------------------------------------------------------------------------------
#define OBJECT_DELETE \
next->prev = prev; \
prev->next = pList->current = next; \
delete this;

//------------------------------------------------------------------------------
// LIST_EMPTY   check if list is empty
//------------------------------------------------------------------------------
#define LIST_EMPTY first->next == last


They just work, for C++.

Hmm. Why should I do this in C++ if std::list is available anyway? (or boost::intrusive::list, if I need an intrusive variant of a list)
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: Mind over bugs: C pointer edition
« Reply #29 on: June 30, 2021, 03:24:47 pm »
"Goto" is allowed, but only in critical code, where it makes sense.
Abuses are prone to produce spaghetti code, terrible for the ICE.

But isn't this obviously true for practically any construct in the language?
« Last Edit: June 30, 2021, 03:29:55 pm by Siwastaja »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #30 on: June 30, 2021, 03:26:21 pm »
For our example (C pointers) the tools for more or less avoiding bugs altogether are out there and well proven. And whether you like it or not these tools are rules and their enforcement with things like static code analysis.

This is very simple actually. If you access a variable through a pointer which points outside of valid memory, the program will crash. That's all there is to it. I don't think any amount of rules or any amount of static analysis can catch all such cases. You just analyze the crash, find the cause, and fix it.

There are other bugs where the pointer points to a valid memory, but not where it should. Bad access may corrupt something. These are slightly more difficult to fix because the cause and the effect are separated in time.
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #31 on: June 30, 2021, 03:28:04 pm »
Hmm. Why should I do this in C++ if std::list is available anyway? (or boost::intrusive::list, if I need an intrusive variant of a list)

Because it is the same or better, you can adjust, my file is much bigger then these basics i just posted.
I dont use other persons programming.

I dont know what is intrusive, i dont want to add any include files and especially no .dll files.
I want source-code only, if they give the code, then i still adjust it to my own.
Mostly they dont give anything.
« Last Edit: June 30, 2021, 03:32:15 pm by Jan Audio »
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: Mind over bugs: C pointer edition
« Reply #32 on: June 30, 2021, 03:30:14 pm »
NorthGuy, thanks for the goto story. It kinda confirms and clears the idea in my head; the problem with such arbitrary rules is that they are not just technical misunderstandings by a rule writer, no, they are identity politics. You are supposed to show "professionalism" by following the trends, just like now you are not supposed to call master devices master devices anymore, and can fail getting a job if they ask you a trick question about the default git branch name and you answer "master". This is pure evil shit.

The question regarding rule sets really is, to which extent it's identity politics, and to which extent sane rules with technical purposes. And that depends a lot, there are good rules for good reasons, too.
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #33 on: June 30, 2021, 03:54:30 pm »
There are other bugs where the pointer points to a valid memory, but not where it should. Bad access may corrupt something. These are slightly more difficult to fix because the cause and the effect are separated in time.

Then bugs show up everywhere, where there is no bug, tell me about it.
The fun part is that all systems show different bugs, you will be able to trace it sooner if you testing on 3 different systems ( PCs ).
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #34 on: June 30, 2021, 04:23:44 pm »
If that were exactly true, how would you explain chess?

Chess is a game. It is abstract and defined by rules.
No dice.  Everything is a game; that's why John Nash became famous.  See game theory. In a very real, mathematically provable sense, all interactions between rational beings can be modeled as games (with "win" or "lose" similarly arbitrarily selectable); even the definition of game in game theory is "interactions between rational beings".  There is no "play" or "pretend" implied or assumed here.

Choice is the key.  See combinatorial explosion.

Simply put, if you have Conway's Game of Life with four rules, an m by n world, the information it contains is just 2mn (plus the information needed to store the rules, which I believe is either 256 or 512 bits for any possible rule set involving only the nearest neighbor cells), no matter how long the world would evolve.

Consider a simple game on a m by n board, where each player gets to move their piece one step in any of the four cardinal directions.  Ignoring the minute difference whether we allow or deny them occupying the same position, the board has 22mn possible states.  However, because of having the ability to choose, the sequence of position over K turns is much larger.  Even if you add a hundred rules, the size of the phase space of all possible sequences – the amount of information required to describe such a game – is infinitely larger.  If there is no stop conditon, it is infinite.  The only reason chess has a limited phase space, is because it has rules/stop conditions regarding cyclicity (impasse, I think? I'm too stupid to be much good at chess myself).



Consider what I wrote above about C pointers.  They basically have no rules the C compiler enforces.  If you want a pointer of qualified type TYPE to point to address ADDRESS given as either a pointer or a numerical address (compatible to intptr_t or uintptr_t type), then you do (TYPE *)(ADDRESS).

I have claimed based on limited personal observation that the majority of bugs related to C pointers stem from not seeing anything wrong or suspect in such expressions.

So, we derive rules, based on observations of such expressions.  Mine can perhaps be summarised as
  • Make sure the target address is valid, that it points to what I think it does.
  • Make sure the target address is sufficiently aligned for the hardware requirements.
  • Make sure the entire address range implied by the pointer is valid.  A four (say, uint32_t) or eight-byte (say, uint64_t) access even at a sufficiently aligned address in a byte buffer may not be valid, if the content in the buffer does not cover the entire accessed element.
  • Record your assumptions, and if reasoning was required, the basis of that reasoning in a comment.  It serves as an orthogonal support for the maintenance of the code (describing important features not described by the code itself), as well as a tool for organizing ones thoughts (rubber duck): it saves total effort.
  • Record the basis of expressions that are the result of effective simplification from an originally complicated form.  The originally complicated form may express assumptions and logic that are not expressed in the effective simplified form.

    As a mathematical example, consider the sum of n consecutive integers starting at k.  This simplifies to k n + (n2 - n)/2.  This simplified form does not imply the sum; therefore if the sum is the mathematical/logical thing needed to express or evaluate, just having the simplified form loses significant amount of information needed for a developer to see whether the code implements the logic the author intended it to or not.  Including the original reason (sum of n consecutive integers starting at k) as a comment, lets further developers verify the implementation, and possibly even replace this local optimization with a higher-level algorithmic one (one that eliminates the need for any calculation of this sort, typically).

I understand if your beef is with the definition of what is a "rule", "law", "rule of thumb", et cetera; but fact is, reality trumps theory and words every single time.

Words are only tools, not primary things – think of Magritte's The Treachery of Images (Ceci n'est pas une pipe) – and it is at the very core of what I am suggesting here; and the "rules" we are talking about here are not the kind of rules that restrict choice, these are the sort of rules that help people estimate the effects of any given choice.

Hell, I even tried to allude to this directly in my long example code snippet, in the fact that even it was in a long-ass paragraph describing why one would deliberately set a pointer NULL after freeing or handing it off to another entity taking responsibility for it, I deliberately omitted it and even added a comment why, near the very end!

I'm familiar with this, because every single rational physicist who sees "laws of physics" knows they're not, in any sense of the "law"; except in that one rarely used or understood sense which means "if you choose to behave this way – or apply this model to phenomena you see – you should succeed and prosper".  Or whatever the correct idiom/saying in English is.  Me no English good today.
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #35 on: June 30, 2021, 05:06:36 pm »
They asked me whether I use "goto" while coding. And I said yes. They didn't hire me, told me that I didn't know modern (back then) programming theory and gave me an academic paper which explained that using "goto" is pure evil and leads to unmanageable spaghetti code.
Yes; this too is why I so often need to put "knowledge" and "know" in quotes.

To one, rock solid "knowledge", is just an understandable but provably incorrect belief to another.  Having it recorded in a respected scientific journal means much less than what one would think, because up to a quarter of peer-reviewed articles in most respected journals –– much, MUCH more in general –– end up being retracted or amended.

I am not advocating rules of thumb, I am only advocating models one can apply when estimating the results of choices; and the reason this thread exists, is that I hope others would both build on if possible, and tear down those models if necessary, based on their experience and observations.

In short: you, and for example I myself, are not in any disagreement here, just focusing on different aspects of the matter.  Me, on the tools and models available, and roughing them out; you, more on the philosophical aspects and consequences of regarding models as rules, and on codifying specific models as fixed rules with regard to programming.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #36 on: June 30, 2021, 06:32:41 pm »
The "goto" thing mostly shows that you must understand why you do things and why you'd better not do some others. If you don't know why you stick to some rule, that's a recipe for a lot of frustration, at best, and even counterproductive results, at worst.

"goto" as is is a very poor construct. It was fought against to promote a clean structured programming approach with proper language constructs. This is still valid today, although obviously programming languages have come a long way.

These days, using "goto" in C as a way to handle errors/get out of nested loops in a cleaner way than having to use convoluted structured constructs with a bunch of flags is of course sometimes justified. But it does come from the fact C lacks basic constructs that would make this easy and clean, so you have to resort to "goto".

Although there isn't a  lot of difference (apart from syntax sugar) if you use "goto" sparingly and correctly in C, having, for instance, tagged blocks, and keyword(s) to get out of a tagged block would be much cleaner, more expressive and possibly less bug-prone. If, to this, you add the possibility of declaring "defered" actions, then it gets even better. Of course, some languages implement this kind of features. Apart from looking cleaner, one benefit of tagged blocks is that it gives static analysis the ability to analyze flow control much more precisely.

I'm not at all bashing C here, I'm still a proponent of it, but I know what its shortcomings are, and in particular here, using the "the rule of not using goto is stupid" argument when the only really relevant use of "goto" comes from shortcomings of the language itself has to be considered for what it is.
« Last Edit: June 30, 2021, 06:34:49 pm by SiliconWizard »
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #37 on: June 30, 2021, 09:15:07 pm »
"goto" as is is a very poor construct.
The labels-as-values extension (with unary && operator providing the compile-time constant address of a label, and goto *ptr; to jump to any label thus acquired) as implemented by GCC and LLVM/Clang, covers additional shortcomings of C, and can be actually useful in rare cases.

Specifically, when you implement multiple separate finite state machines in the same scope, as if they were coroutines.  Then, the label to jump to to execute the next state in a particular state machine is kept in a variable.

In most cases, you can just keep the local data in a separate structure, either in a global variable, or by passing the structure to each function handling a state for a particular state machine, so one could argue that it is not strictly needed feature to implement these state machines; but after playing with it, and looking at such code (from a maintainability perspective), I like having the option.



For the newbies who aren't sure what this goto stuff is about:

In my own example snippets I've suggested to others, for the single common error cleanup case I prefer

        do {
            if (step1fails)
                break;
            if (step2fails)
                break;
            if (step3fails)
                break;
            return success;
        } while (0);
        cleanup();
        return failure;

but for staged/structured error cases, goto seems easier to maintain:

        if (step1fails)
            goto fail1;
        if (step2fails)
            goto fail2;
        if (step3fails)
            goto fail3;
        return success;
    fail3:
        undo3();
    fail2:
        undo2();
    fail1:
        undo1();
        return failure;

but I am not opposed to say

        if (step1succeeds) {
            if (step2succeeds) {
                if (step3succeeds) {
                    return success;
                }
                undo3();
            }
            undo2();
        }
        undo1();
        return failure;

either; I kinda like it, but only when there are no silly limitations on line lengths (I often start breaking long lines only after 160 chars or so), as the indentation pushes stuff quite far right, and it is no good if it means expressions then need to be split to multiple lines because of style guides.  However, the following,

        if (step1) {
            undo1();
            return failure;
        }
        if (step2) {
            undo2();
            undo1();
            return failure;
        }
        if (step3) {
            undo3();
            undo2();
            undo1();
            return failure;
        }
        return success;

seems the nicest and cleanest of them all, but deceptively so: the code duplication in the failure branches (which tend to be the least tested ones) means that any changes must be correctly reflected across all error branches; and this makes it annoyingly easy to fix a bug or add a detail to one, but not all, error branches.  And if the undo steps have any sort of complexity at all, when finally somebody notices they are no longer in sync, they have to go delve in source code management history to see what was modified last and why, just to determine which of the error branches currently have the bug-free version, and should be duplicated to the other branches.  Nasty, when you immediately see a bug (based on differences in the error branches), but there are more than one difference in the error branches: there is no correct one, so one has to grab a cup of favourite beverage, and start analysing the code to see what ones cow-orkers actually tried to accomplish when writing this code – and end up rewriting the entire mess just so oneself does not have to fix it again and again in the future too.  Ask me how I know.

If there is an existing codebase, I use its conventions.  If I have to choose, I pick the one that seems most easy to maintain for that particular case.
If this makes someone reject me as a developer, I don't think I'd like working with them anyway.

And if an employer gives me any lip about fixing bugs affecting production and making the codebase easier to maintain instead of concentrating on new features, I'm outta there.  Fine work for others; I'm not suited for that job.  I, too, am a tool.
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #38 on: June 30, 2021, 09:34:25 pm »
The following pattern is useful when you need to allocate multiple different chunks of dynamic memory in a function:
Code: [Select]
int my_func(size_t doubles, size_t ints, size_t chars)
{
    double *darray = malloc(doubles * sizeof (double));
    int    *iarray = malloc(ints * sizeof (int));
    char   *carray = malloc(chars);
    if (!darray || !iarray || !carray) {
        free(carray);
        free(iarray);
        free(darray);
        return NOT_ENOUGH_MEMORY;
    }

    // Do stuff ...

    free(carray);
    free(iarray);
    free(darray);
    return SUCCESS;
}
Because free(NULL) is safe and does nothing, we can do all the allocations we want, and then just check if any of them failed.  If any of them failed (is NULL), we free all of them, and return the not-enough-memory error.  Yes, we do call free(NULL) on the failed allocations, but it is minimal overhead, and safe; thus worth it for the simpler, easier to maintain code.

So, although logically we have three steps and possibly seven different possible error cases, we don't need to tie ourselves into the complexity.  Here, we'd win nothing by handling each allocation failure case separately, we'd just make the code more complex.  By handling any allocation failures this way, we keep things simple and robust.

In case you wonder, there is no sizeof (char) in there, because in C sizeof (char) == 1 .  And the proper unsigned integer type for in-memory sizes of types and structures and arrays is size_t.  Depending on the architecture, it is the same type as unsigned int, unsigned long, or unsigned long long, but it is better to think of it as a separate type.  Its conversion specification for print and scan family of functions is %zu (in C99 and later; don't use a C compiler that does not support it).

Also, there is no reason why the free operations should be in reverse order of the allocations; it does not matter for correctness.
I do it, because I like the mirror symmetry; it helps me perceive the code somehow.  If you prefer the same order of frees as allocations, do feel confident to do so; just keep observing yourself to see what helps you write better code, and then do that.  Sometimes a bit of discomfort/unfamiliarity helps one see, sometimes not; find out for yourself.

Someone might point out that doing the frees in the opposite order of the allocations might help the underlying implementation locate the memory chunk faster somehow, but honestly, standard library memory allocation functions are fast, and have to be darned fast to not be annoyingly slow, and use data structures and algorithms that don't really care either way.  It just won't affect their execution speed enough to matter even to speed freaks.  If one is mixing frees and allocations, that's different: you want to do the frees as early as possible, all before any of the allocations if possible, to make room for the future allocations.

Finally, the above function interface has a set of implied assumptions: it assumes none of the three parameters are zero.

You see, malloc(0) may return either NULL, or a non-NULL pointer that cannot be dereferenced but can/must be freed.  (Testing it will only tell you what a particular version of a particular compiler on a particular hardware does; the results are not reliable, because malloc(0) is explicitly allowed to return either one.)
So, as it is currently written, the function may or may not report an allocation error if one or more of the sizes is zero!  Ouch!
There are many ways to handle the zero case as well: you can make the allocation check (and optionally the allocation, or leave it as is) conditional on the size being nonzero [changing the if clause to if ((!darray && doubles > 0) || (!iarray && ints > 0) || (!carray && chars > 0)) {], or you can allocate an extra element for each, and so on.  I did not want to tie anyone to a particular approach, so I left it out.
« Last Edit: June 30, 2021, 09:45:17 pm by Nominal Animal »
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #39 on: June 30, 2021, 09:55:53 pm »
Aren't rules there because we share our code (willingly or not!) with other people, and it is sensible to have a common language. Not everyone is a programming god able to instantly see how some weirdo construct implements some ultra-clever Heath Robinson contraption. You may be able to read and understand the spaghetti code you wrote, but someone else might struggle. Thus the reason, for instance, goto is frowned upon is not because you'll tie yourself in knots with it, but someone else will struggle to follow the convoluted path jumped around inside a 4-page function.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #40 on: June 30, 2021, 10:09:30 pm »
"goto" as is is a very poor construct.
The labels-as-values extension (with unary && operator providing the compile-time constant address of a label, and goto *ptr; to jump to any label thus acquired) as implemented by GCC and LLVM/Clang, covers additional shortcomings of C, and can be actually useful in rare cases.

I have no doubt they can be very useful, but they certainly do not address the shortcomings I was referring too. I see those more as band-aids.

I for one would never touch those with a stick. First because they are non-standard, and I refrain from using any non-standard language feature, except when they are strictly unavoidable. Second, because frankly, as nice again as 'goto *ptr' can be, it's pretty horrendous to me on several levels. Some of the reasons I mentioned already. Makes static analysis pretty much impossible, and potentially leads to security issues (but for this point I would need to know how exactly it's compiled, so I'm not sure). Makes code hard to read as well. Just my humble opinion there though of course.

My personal take on goto in C is restricting its use to:
- Breaking out of nested loops (at least when any other means would be clunkier);
- Jumping to some common code section, for instance for error handling inside a function.
« Last Edit: June 30, 2021, 10:11:38 pm by SiliconWizard »
 

Online T3sl4co1l

  • Super Contributor
  • ***
  • Posts: 21686
  • Country: us
  • Expert, Analog Electronics, PCB Layout, EMC
    • Seven Transistor Labs
Re: Mind over bugs: C pointer edition
« Reply #41 on: June 30, 2021, 10:14:29 pm »
        if (step1) {
            undo1();
            return failure;
        }
        if (step2) {
            undo2();
            undo1();
            return failure;
        }
        if (step3) {
            undo3();
            undo2();
            undo1();
            return failure;
        }
        return success;

seems the nicest and cleanest of them all, but deceptively so: the code duplication in the failure branches (which tend to be the least tested ones) means that any changes must be correctly reflected across all error branches; and this makes it annoyingly easy to fix a bug or add a detail to one, but not all, error branches.

It would be nice to have nested functions, so that the undo1() etc. can be de-duplicated while sharing scope; or lambdas or something.  But that's not C.  (C++ however...)

Or you can always pass a laundry list of variables.  Such a PITA though, and more stuff to go wrong (name/order confusion?).

It would be amusing to probe the prior function's stack frame and extract these data.  Too bad that's only going to work in debug, as the compiler otherwise knows not to save variables to memory unless there is a specific need (taking its address, register pressure..).  Or I mean, with rewriting the host function to force its variables into memory.  Very much an advanced trying-too-hard-to-be-clever / desperation / worse abuse than goto trick... :-DD

Tim
Seven Transistor Labs, LLC
Electronic design, from concept to prototype.
Bringing a project to life?  Send me a message!
 
The following users thanked this post: Nominal Animal

Online DiTBho

  • Super Contributor
  • ***
  • Posts: 3915
  • Country: gb
Re: Mind over bugs: C pointer edition
« Reply #42 on: June 30, 2021, 10:25:04 pm »
- Jumping to some common code section, for instance for error handling inside a function.

I meant this, and only this :D
The opposite of courage is not cowardice, it is conformity. Even a dead fish can go with the flow
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #43 on: June 30, 2021, 10:59:08 pm »
Aren't rules there because we share our code (willingly or not!) with other people, and it is sensible to have a common language.
Good point/question.   I personally do not know.  I keep all that stuff under the Maintainability heading, and they're very important to me; I just don't know if I'd call them rules, sensible advice, or information gained through past experience, or something else.

I do know that worrying about maintainability is very hard when you are starting out as a programmer; it is hard enough to get stuff to work, and trying to make it "nice" or "maintainable" on top is a lot of hard work without immediate rewards.

So, if a beginner were to ask for my advice or opinion, I'd tell them to focus first on writing good comments – orthogonal to the code, meaning try hard to not just say what the code does; try to describe why instead, for a start.  Then, when you start collaborating with friends, talk shop about what sort of code is easiest and most fun to work with (and I mean ignoring what the code does), and what one can do to make it more worthwhile for others to look at, contribute, and work with ones code.  You'll find your own voice and style, but only if you don't get fixated on a specific one "as the Correct or Best one", and try different ones.  When you start regularly getting your programs working the way you want, start working them over with the idea of keeping them around for longer; with all details like build prerequisites and quirks and so on included in the source folder, perhaps a proper LICENSE file and SPDX license identifiers in source files; comments intended to let someone else take over the maintenance.  Heck, you could do some fun projects, maybe terminal-mode network games using curses/ncurses with some friends, and decide that on a certain date, you'd switch projects, just to learn how to take over, maintain, and collaborate with others.

Me, I always talk to the old hands, no matter what the profession.  I'm eager to hear what they've learned, how they've become good at what they do, and what they have to say about doing the job efficiently and satisfactorily and have fun doing it.  And what they want to avoid, and why.  Not all of it will apply to myself, but it might; there is hard-fought experience and information in there, so why not look for it a bit.

I've mentioned before, but one of my own issues, even after three decades of programming as a profession, using half a dozen to a dozen different programming languages, is that I didn't develop my commenting skills early on, and still have some bad habits left.  I have to spend more effort than I would if I had learned to do it correctly early on.  And commenting is not "optional", it is a crucial part of the development process for me, so that does hurt the quality of my work product.  I just didn't really learn this is so until relatively late.

"goto" as is is a very poor construct.
The labels-as-values extension (with unary && operator providing the compile-time constant address of a label, and goto *ptr; to jump to any label thus acquired) as implemented by GCC and LLVM/Clang, covers additional shortcomings of C, and can be actually useful in rare cases.
I have no doubt they can be very useful, but they certainly do not address the shortcomings I was referring too. I see those more as band-aids.
I fully agree; I intended to list them as "also, these are related but cover a different set of shortcomings, or at least provide an option for some specific rare scenarios, but are similarly not a tool you should often reach for".  Bad wording on my part.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #44 on: June 30, 2021, 11:12:34 pm »
        if (step1) {
            undo1();
            return failure;
        }
        if (step2) {
            undo2();
            undo1();
            return failure;
        }
        if (step3) {
            undo3();
            undo2();
            undo1();
            return failure;
        }
        return success;

I have used this pattern quite a bit. It gets clunky pretty fast, but it works. It's somewhat annoying to maintain too.

One way of making this less clunky is to do exactly as you suggested with malloc/free.

Implement things so that your undo*() functions can be called even when the corresponding step hasn't been executed yet. Just requires proper initializations and proper checks.
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #45 on: June 30, 2021, 11:34:45 pm »
and...

Quote
Similarly, you cannot learn programming by being afraid of bugs.

I agree with this, if only because learning from a mistake carries far more weight than learning by rote.

However,

Quote
When you make a decision about something, you can either

- follow rules (or commands)

or

- think

Therefore, if you follow rules, you don't think.

This is wrong. I see a rule and the first thing I want to know is why it's a rule. What is it fixing, and often just knowing that there is a rule for something makes me aware of, and learn about, something I hadn't previously considered. What happens about it then would depend on the specific situation, but generally a rule is prior experience pointing a finger.

The final line there might pass as workable bon mot but perhaps it merely reflects one person's narrow view.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #46 on: July 01, 2021, 01:17:19 am »
Quote
Therefore, if you follow rules, you don't think.

This is wrong. I see a rule and the first thing I want to know is why it's a rule. What is it fixing, and often just knowing that there is a rule for something makes me aware of, and learn about, something I hadn't previously considered. What happens about it then would depend on the specific situation, but generally a rule is prior experience pointing a finger.

The final line there might pass as workable bon mot but perhaps it merely reflects one person's narrow view.

Perhaps I didn't word it well enough. If your employer forces a rule on you, this certainly doesn't preclude you from thinking about the subject and evaluating the merits of the rule. However, what you think doesn't count - you must obey the rule no matter what you think.
« Last Edit: July 01, 2021, 01:19:01 am by NorthGuy »
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #47 on: July 01, 2021, 01:32:37 am »
I see a rule and the first thing I want to know is why it's a rule.
Human after my own heart!  Me too.

I sometimes refer to it fondly as my monkey aspect, because it is sometimes seen as the tendency to rattle ones cage and fling dung around, as monkeys are wont to do.
In truth, experiments show that monkeys tend to abide by any rule that is associated with negative feedback; they only go monkeying around when frustrated or hurt.



As to fear of bugs: I don't avoid bugs because they scare me; they do not.  They always happen, just like taxes.  I only want to avoid bugs because they annoy the heck out of me.  So, I trade time and effort for not getting annoyed too often.

An analog: dog turds.  I don't fear them.  I don't mind picking up after my own, or a friends' dog; it's just a part of being a dog's (temporary) person, gotta do it.  I've gotten hit by it in the face when walking past a lawnmower, and while annoying and icky, it does not scare me.  Getting hit in the eye with a piece of glass or a rock flung by a lawnmower blade, now that is scary.  Poop is not, it's just annoying, and wastes my time as I have to go and wash.  Or at least hose off my shoes.  Although if it is just on the bottom, I just do that wipe-on-grass thing.

Same with bugs.

I do "fix" some bugs by recording the cause (unacceptable parameter conditions) in a comment in a suitable place, so callers know to avoid the bug, without a single line of functional code.  I do still count that as "fixing".  A common one is something like documenting a Xorshift64* Pseudo-Random Number Generator (my favourites!) with a note that the initial seed has to be nonzero, and if it is, the state will never be zero either; so zero state is always invalid.  I don't bother checking for it, although I do make sure random-seeder (the one that initializes one with a new, random, seed state) never generates one, if intended for a Xorshift PRNG.

Now, having my data silently garbled, that gets me angry.  My data is valuable to me, and "tools" that garble my data, get fixed and/or swapped for better ones.

Right now, I'm seriously considering switching from gcc to clang, exactly because I'm finding myself too often annoyed with what GCC gives me back, and I've started to systematically compare the output of the two.  And for ARM, GCC bugzilla looks like seeing a lawnmower come towards you, with a patch of something glinting in the grass between you and the lawnmower.  I don't even fear getting bit by GCC bugs, because that too occurs and I just deal with it, but turning my back and seeing if the next patch of grass is maybe a bit less glint-y and more suitable for plopping down for a picnic lunch, seems like a prudent option to consider here.

(To be clear, thus far I've used older versions of GCC to keep to versions more stable and less in flux, but now it looks like feature development is greatly exceeding the rate at which bugs are fixed, and bugs are no longer fixed because they occur even in the older versions that the developers have no interest in anymore; and newer versions are too buggy to rely on for any kind of serious work.  I don't want to participate in the bughunt, because I don't care much about the new features they're adding; I'd just be helping people I think are wasting my time, waste even more of my time.)
« Last Edit: July 01, 2021, 01:34:34 am by Nominal Animal »
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #48 on: July 01, 2021, 01:06:57 pm »
I am almost considering ASM if i read this.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #49 on: July 01, 2021, 04:20:34 pm »
As to fear of bugs: I don't avoid bugs because they scare me; they do not.  They always happen, just like taxes.  I only want to avoid bugs because they annoy the heck out of me.  So, I trade time and effort for not getting annoyed too often.

Look at that this way. Once you have written software, it will have bugs.  Here, there ... You don't know where they are and therefore you cannot fix it. Your software is buggy and crappy. Then, you encounter a bug. Either you find it yourself, or QA engineer filed a report, or user filed a support ticket. This is good luck for you. This gives you an opportunity to work on it and fix it. This will make your software better, and your software will eventually becomes clean and reliable. If you didn't find the bug, the software would remain crappy.

How can you possibly look at this very positive event as if someone is throwing dog shit at you?

If you cannot tell where the bugs are after you write the software, you certainly cannot tell where they are before you write the software. Of 100 lines you write, may be one or two will have bugs, may be even none. If you look at every line as if it already contained a bug (which it most likely doesn't), and try to figure what the bug can possibly be, and try to fix it even before your write that line, you make your job needlessly hard. If it's not broken, don't fix it.

« Last Edit: July 01, 2021, 04:22:42 pm by NorthGuy »
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #50 on: July 01, 2021, 04:37:50 pm »
If you look at every line as if it already contained a bug (which it most likely doesn't), and try to figure what the bug can possibly be, and try to fix it even before your write that line, you make your job needlessly hard. If it's not broken, don't fix it.

There is always room for improvement, as you keep learning.
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #51 on: July 01, 2021, 04:55:56 pm »
Quote
If it's not broken, don't fix it.

How about when it is broken but it just hasn't triggered a noticeable issue yet? Surely you would want to have it as bug-free as possible rather than waiting for some Bad Thing to occur?

Further, simple finding (or, more likely, being told) a bug is not a good move. There is a fair chance that you are unable to fix the bug without serious rework, and potentially it is not fixable at all. Don't forget that the earlier a bug is caught the cheaper it is to fix.

But also when someone reports the bug to you, your programming reputation will have taken a hit and will take some effort to recover (if it ever does).

No, being told there is a bug is never a positive event. Fixing it can be, but the existence of it is definitely a negative situation.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #52 on: July 01, 2021, 05:42:41 pm »
No, being told there is a bug is never a positive event.

I'm not sure I agree with this. Becoming aware of a bug that was there unnoticed IS a positive event.

Fixing it can be, but the existence of it is definitely a negative situation.

Ah, this is a different thing now. Maybe you didn't express it quite right above. The existence of a bug, is, of course, something that is not positive.

But, becoming aware of one IS a positive event. It gives you the opportunity to fix it, or, if you can't fix it, to at least KNOW about it, and find workarounds for your users. Or, if you can't find any workaround, at least tell them how and when it could manifest itself so they are prepared.

Ignorance, when it comes to bugs, is rarely bliss.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #53 on: July 01, 2021, 05:49:09 pm »
Quote
If it's not broken, don't fix it.

How about when it is broken but it just hasn't triggered a noticeable issue yet? Surely you would want to have it as bug-free as possible rather than waiting for some Bad Thing to occur?

Sure, but you cannot fix it until you know it exists. That's why you do testing and hire QA people and lure beta-testers. You want to find the bug.

Further, simple finding (or, more likely, being told) a bug is not a good move. There is a fair chance that you are unable to fix the bug without serious rework, and potentially it is not fixable at all.

Finding a bug is a necessary condition for fixing it, but not always sufficient.

Don't forget that the earlier a bug is caught the cheaper it is to fix.

Yes. That's why you should to be happy when a bug is found. If not found, it would persist, causing more harm and more cost in the future.

But also when someone reports the bug to you, your programming reputation will have taken a hit and will take some effort to recover (if it ever does).

Non-sense. Everybody makes mistakes. And the more afraid you are of making a mistake, the more mistakes you make. Show me a programmer who claims his code is always bug free and I'll show you a liar.

No, being told there is a bug is never a positive event. Fixing it can be, but the existence of it is definitely a negative situation.

How so? The bug existed before. If it wasn't found, it would persist, possibly causing harm. The only difference is that you wouldn't know about it. Now, when it's found, you can stop the harm you were causing. Would you really prefer to remain delusional?
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #54 on: July 01, 2021, 06:51:45 pm »
Quote
Quote from: dunkemhigh on Today at 17:55:56

    No, being told there is a bug is never a positive event. Fixing it can be, but the existence of it is definitely a negative situation.


How so? The bug existed before. If it wasn't found, it would persist, possibly causing harm. The only difference is that you wouldn't know about it. Now, when it's found, you can stop the harm you were causing. Would you really prefer to remain delusional?

Perhaps, as someone said of me above, you aren't expressing yourself well.

I don't get the point of discussing bugs during development in this context because they will always be there, and they will typically be fixed. It's after that, when someone else finds the bugs that I assumed we were talking about here. Not during development but when whatever product is being used. Clearly, that's a sub-optimal time. And, yes, getting a reputation for buggy code is really a bad thing, and people remember that even if you clean up your act and don't release buggy code for a long time.

Perhaps you mean 'find' as in QA. I would query whether that's an innocuous time too. Yes, you want to find bugs, which is why you test, but that's when you're writing and creating the stuff, not after it's supposed to be working.

Personally, I get a bigger kick out of writing bug-free code than I do having to fix my mistakes afterwards. OTOH, I get a kick from resolving an issue, which might be due to someone elses bug. But mine just means I was shit at what I was trying to do.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: Mind over bugs: C pointer edition
« Reply #55 on: July 01, 2021, 07:09:36 pm »
Except for some safety-critical (say avionics or pacemaker), or extremely small and trivial software, bugs are also always in the end product. Customers are totally used to it. Customers are also totally used to bugs not being taken seriously and ever fixed. See Altium Designer for example.

I don't think this is good state of affairs. But it's the reality of software in year 2021. And has pretty much always been, especially on desktop computing.

So no, if you have a bug or two every now and them, react to them seriously and relatively swiftly, and fix the issues, you won't get a bad reputation. Quite the opposite, everybody's happy because you do so much better than basically anybody else. Admit mistakes and serve your customers, and most customers are ready to pay for it.

Publish a bug-ridden piece of shit, never fix anything, and coat the experience by basically saying "fuck you" to your customers, and you are approximately doing average. Like Microsoft Windows. Or Altium Designer.

Nowadays I'm really happy if the software can be used for its intended purpose (with workarounds for bugs) at all. Like Altium, which, after all, can somehow magically produce a set of manufacturing files which turn out to be a working PCB, in a process which is still orders of magnitude easier than hand-writing the gerbers from scratch, despite crashing 47 times with a "Please wait..." window, or copy-paste crashing it if your Windows installation lacks a default printer (how easy it was to figure out that workaround: Altium crashes randomly -> add a printer!).

This also relates to the fact most problems are at a higher level than over-indexing an array. Whoever got this great idea that,
(1) Copy-pasting involves probing for the installed system printers,
(2) Such operation can be assumed successful without checking,
(3) You use the result in some way that results in a crash if the result was invalid,
(4) Yet you don't do anything actual with the result, i.e., you do not print,

introduces this bug without actually writing a single line of code yet.
« Last Edit: July 01, 2021, 07:22:54 pm by Siwastaja »
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #56 on: July 01, 2021, 07:29:57 pm »
Quote
Customers are also totally used to bugs not being taken seriously and ever fixed. See Altium Designer for example.

And see the comments dissing Altium.

I use some software which is actually pretty good, but it has bugs. I notify the developer and he often fixes them reasonably quickly. But whenever something is not right in the software, even if it's actually my fault, my first reaction is "Oh no, another bug". If there were an alternative I would probably switch to it.

So, yes, everyone realises that bugs exist, but that doesn't meant they like them or ignore them. They are a fact of life, but so is getting cold and wet when it rains, and no-one in their right mind looks forward to that, do they?
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #57 on: July 01, 2021, 10:43:17 pm »
You can have bugs at any level, but in the context of C pointers I think we're discussing programming bugs. That is, the behaviour expected of the code by the programmer doesn't occur.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #58 on: July 02, 2021, 12:15:05 am »
Personally, I get a bigger kick out of writing bug-free code than I do having to fix my mistakes afterwards.

I also write bug-free code. Always. But sometimes there are bugs in it ;)

These bugs are there not because I used some particularly harmful language construction. Nor because I wrote something in a way which is particularly bug-prone. They are not a sign that my programming methods are bad and have to be changed. These are just simple human mistakes - something misspelled, misjudged, or misunderstood. You cannot prevent such mistake from happening. You can only fix them afterwards.

Next time you play golf, stand on the first tee and resolve that you will make birdie on the first hole. Then repeat this for the second hole and for the rest. Not going to happen. Same with bug-free code.

 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #59 on: July 02, 2021, 12:22:26 am »
As to fear of bugs: I don't avoid writing bugs because they scare me; they do not.  They always happen, just like taxes.  I only want to avoid bugs because they annoy the heck out of me.  So, I trade time and effort for not getting annoyed too often.
Look at that this way. Once you have written software, it will have bugs.  Here, there ... You don't know where they are and therefore you cannot fix it. Your software is buggy and crappy. Then, you encounter a bug. Either you find it yourself, or QA engineer filed a report, or user filed a support ticket. This is good luck for you. This gives you an opportunity to work on it and fix it. This will make your software better, and your software will eventually becomes clean and reliable. If you didn't find the bug, the software would remain crappy.

How can you possibly look at this very positive event as if someone is throwing dog shit at you?
I realized I left out a crucial word there: writing.

The way I usually encounter a bug is when they silently garble my data (which I consider similar to having a nice big streak of poo on your back),  or a process produces unexpected and useless results.  There is usually some pain involved – not just "what now?", but the "hmm, I cannot pinpoint any logical errors in this chain".

Whenever I get a bug report, or a colleague pointing out a typo, thinko, or even a severe misunderstanding (and that last category requires verifiable proof, not just someone quoting the standard as if it was the holy bible; me being a dirty pagan after all), I get ecstatic.

A good bug report, one with a verifiable test case, and showing why a specific behaviour is desired, is in my book worth a very nice Thank You, perhaps a cuppa or a beer depending on the environment; and I am definitely in the habit of telling anyone who has produced a good one for me to fix a bug with, that if they don't get a response quick-ish and they have more of these, to ping me.

I do often go bug-hunting in all sorts of projects.  (In my mind, "avoiding bugs" includes "make bug-hunting raids now and then"; it just doesn't translate well to the English I type out.  Apologies.)  If a friend or acquintance has a problem with a project I'm unfamiliar with, but which uses tools I'm comfortable with (as in, do not need to set up completely new development environments, very specific versions of nonstandard libraries, and such), but can either browse the sources online or pull a git, I *often* help.
(There are members here who've contacted me via PM that I've done exactly this for/with/to. English!  In exchange, I only ask them to pass it forwards.  Since I haven't been active at StackExchange, direct emails from there have dropped down to a trickle, less than one a week.)

I guess my attitude is much better described as "does not ignore known bugs, likes to squash them, and keep their population down".

If you cannot tell where the bugs are after you write the software, you certainly cannot tell where they are before you write the software. Of 100 lines you write, may be one or two will have bugs, may be even none. If you look at every line as if it already contained a bug (which it most likely doesn't), and try to figure what the bug can possibly be, and try to fix it even before your write that line, you make your job needlessly hard. If it's not broken, don't fix it.
Yes, and no.  If I have no reason to suspect a code does not behave as it is supposed to, I don't worry about it.

The suspiciousness when reading code basically boils down to looking at the code critically and asking oneself, Does this code do what it is supposed to do? (split into two sub-questions, Did the original programmer choose a suitable algorithm or approach? and Does the code do what the original programmer intended?) and Are there any expressions here I should be taking a more careful look at?

It is not the same thing as thinking there might be bugs.  It's more like a structural engineer walking through a worksite, and looking at the girders and walls to see if anything seems fishy; not just say cracks in the concrete, but whether the concrete has eroded in places already indicating the mix was incorrect in the first place.

(A typical such "erosion", in my opinion, is a program that takes user parameters, but never checks them (even whether they were parsed correctly or not), and just assumes that they should be, since we haven't crashed yet.  It itself is not necessarily an error, but it indicates that a closer look may be warranted.  Even I use sscanf() to parse user input sometimes, even though it doesn't properly check for all possible errors (numerical overflows in particular); it's just .. hmm... an indication of the sort of neighborhood you are in.  If it is an industrial hall used to store vehicles, it may not need *walls* at all, wallpaper and hardwood floors even less.)

This, too, is a skill that can be cultivated, and does not need any special "aptitude" for it.  I do believe most programmers concentrate too much on *emitting* code, and not enough on *examining* already existing code.  With the sheer amount of existing open source code in public source code repositories, one really only needs some sort of a yardstick to pick some "good ones" and some "bad ones", and start reading, comparing, and trying to understand; it only takes time and effort.

I think the biggest software system defects aren't implementation bugs but requirements / specification / documentation bugs.
Yes, I think I have to agree.  I do feel they occur in a slightly different context, though: instead of silently garbling data, they just refuse to work in an useful manner, or forbid actions or expressions that the user needs to perform their tasks.  (Lots of compiler features in this class!  Yet, I still use C and C++.)

One of the issues I do have, is that people assume terms have a specific meaning without checking.  Worse, if they insist, without trying to find any agreement on the uses.  (And, I do claim, it is the uses that define the meaning, not any particular claims of their meanings, no matter how "authoritative".  Consider Albert Einstein and the cosmological constant.)

If you stick to the compiler or standard definitions, your understanding will be bracketed, limited and defined, by that standard; and there really isn't any reason to believe any committees get the definitions right.  A human mind has to be nimble, and think about the unstated assumptions even they themselves are making, be ready to shift context whenever such a shift is warranted, and just deal with it all.

In physics, outside classical mechanics, even the term mass is problematic, because one cannot be sure if it refers to the invariant or rest mass, or relativistic mass as the analog or extension of classical mass with Lorenz factor "correction".  Electrons do not "orbit", they have "orbitals"; but in many languages the two map to the same words, with much confusion.  In mathematics, you need to tell what each of your variables represent, and if for example you use \$\vec{v}\$ for a generic vector, but \$\hat{v}\$ for an unit vector (like I often do).  In linear algebra, when dealing with matrices and vectors, if you use the \$\vec{v} = (x, y, z)\$ notation for vectors in the text (instead of \$\vec{v} = \left[ \begin{matrix} x & y & z \end{matrix} \right]\$ or \$\vec{v} = \left[ \begin{matrix} x & y & z \end{matrix} \right]^T\$ , you also need to remember to tell whether your vectors are row or column vectors, because one of \$\mathbf{M}\vec{v}\$ and \$\vec{v}\mathbf{M}\$ yields a vector and the other a matrix, and whether the vectors are row or column vectors, determines which.

I seriously wish that more people start realizing that the counterquestion, "What do you mean by Z, exactly?" in science and engineering is completely different to the social accusation it tends to nowadays be in "polite society".  In science and engineering, it is an expression of interest in the subject and mutual discourse, and a request for clarification without any negative connotations.  It must be, because any other interpretation robs us of one of the core tools; that all-important one that makes peer review so valued and respected.  I myself have had to learn to make sure my face expresses both interest and deep thought so in face-to-face the question isn't misunderstood, which is kinda silly and unnecessary waste of the meager efforts my thinking fat is capable of stretching to.

These are just simple human mistakes - something misspelled, misjudged, or misunderstood. You cannot prevent such mistake from happening. You can only fix them afterwards.
I'm not sure I even think of them as proper bugs anymore; they're so common and easy to fix.  I myself like to sleep over an initial implementation, then review it the next day, because those always occur – like taxes! – and I consider it just part of my development workflow, not "bug hunting" per se.

I even do the same to my posts.  I do preview, but after posting, I read it through once.  Then, I do something else, and if I see any responses, I first re-read my own to see if anything needs fixing.  Then I read the responses, and if I see a discrepancy, I re-read my own again to see if it is easily read in a different manner I intended.  English is a lot of work for me, but I am a fast reader.  (And I don't just glance; the speed does not affect my understanding, I don't understand a bit more even if I read slower.  When I read carefully, I consider the ways each paragraph can be understood; that takes considerably more time.)
« Last Edit: July 02, 2021, 12:26:31 am by Nominal Animal »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #60 on: July 02, 2021, 04:12:22 am »
These are just simple human mistakes - something misspelled, misjudged, or misunderstood. You cannot prevent such mistake from happening. You can only fix them afterwards.
I'm not sure I even think of them as proper bugs anymore; they're so common and easy to fix.

But all bugs are like this. They're your mistakes of various kinds. While you can catch some of them in code reviews, most are best visible when you run your code and observe the effects.
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #61 on: July 02, 2021, 09:53:16 am »
Quote
I realized I left out a crucial word there: writing.

I am finding it very difficult to interpret what y'all are on about. I thought we were discussing bugs that we, as the programmer, had introduced. So it is hard to reconcile why you would "get ecstatic" when someone finds you've fucked up, confused them and they've spend some effort generating a report which they wouldn't've had to do if you'd done your job right in the first place.

But then you say "If a friend or acquintance has a problem with a project..." so in the above did you mean some code you didn't write but got a report about? If so, that's a completely different thing and the expected response would be arse about face in comparison.

So, just wtf are we talking about here? Alternatively, can we please either stick on talking about our own generated bugs or other peoples bugs and not mix the buggers up? Otherwise it's like discussing buying stuff vs selling stuff without letting on which it is.
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: Mind over bugs: C pointer edition
« Reply #62 on: July 02, 2021, 02:57:55 pm »
So it is hard to reconcile why you would "get ecstatic" when someone finds you've fucked up, confused them and they've spend some effort generating a report which they wouldn't've had to do if you'd done your job right in the first place.

But "doing your job right" can't mean perfectionism. Show me a developer writing completely bug-free code and I show you a liar, as somebody somewhere said.

If you react "OMG, I fucked up!" to every bug revealed, you are overreacting, and you won't mentally last long. Or maybe you just don't code.

I understand very well the feeling of being happy that somebody has participated and made your life easier by doing a good bug report. The result is good for everyone; the bug will be fixed sooner than with a crappy bug report.

Sure, they are doing "your job", in a sense. But seeing they voluntarily made that bug report without you asking for it possibly is related for the ecstatic feeling, no...?

It may have taken 2 hours of their time. With a crappy bug report written in two minutes, you could waste 2000 hours trying to reproduce it and still not find the bug.

That's why as a customer I want to be able to invest my time writing a good error report and want it to be taken seriously. It's not only limited to bugs, really. It's also when I report to LIDL that the tuna cans had their labels mixed up I spend some time properly documenting the case.
« Last Edit: July 02, 2021, 03:03:16 pm by Siwastaja »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #63 on: July 02, 2021, 03:31:50 pm »
... someone finds you've fucked up ...

Consider two scenarios:

1. You fuck up
2. Software with the bug ships out
3. Some of the customers encounter the bug
4. Your software gets reputation of buggy (just like Altium in Siwastaja's post)
5. Sales drop
6. The company goes bankrupt
7. You get fired
8. You get on with your life and you never know that you have destroyed the company

or

1. You fuck up
2. Software with the bug ships out
3. Some of the customers encounter the bug
4. One of the customers reports a bug
5. You promptly fix the bug
6. The customers install the update and most of them don't even know that the bug existed
7. The company's revenue increases
8. You get promoted

Which one do you think is better? The differences start from item #4, where you find out that you've fucked up.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #64 on: July 02, 2021, 03:43:54 pm »
That's why as a customer I want to be able to invest my time writing a good error report and want it to be taken seriously.

Me too. But unfortunately the majority of companies don't accept bug reports. At best you get a support person whose job is to help customers to use the product. She doesn't know how to file a bug report even if she wanted to. This discourages customers and they stop submitting bug reports. This is one of the reasons why software is getting so buggy.
 
The following users thanked this post: Siwastaja

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #65 on: July 02, 2021, 03:51:17 pm »
These are just simple human mistakes - something misspelled, misjudged, or misunderstood. You cannot prevent such mistake from happening. You can only fix them afterwards.
I'm not sure I even think of them as proper bugs anymore; they're so common and easy to fix.

But all bugs are like this. They're your mistakes of various kinds. While you can catch some of them in code reviews, most are best visible when you run your code and observe the effects.

Yep. That's my experience too.
Of course, some "obvious" bugs can be caught in code reviews. That's particularly true for simple bugs, often those introduced by less-experienced developers. Some tools like static analysis can help as well. But they are always limited in what they can catch in the real world.

For any at least moderately complex design, testing is really all you can do to find the most bugs. And I mean, heavy testing under real conditions.

 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #66 on: July 02, 2021, 03:56:10 pm »
That's why as a customer I want to be able to invest my time writing a good error report and want it to be taken seriously.

Me too. But unfortunately the majority of companies don't accept bug reports. At best you get a support person whose job is to help customers to use the product. She doesn't know how to file a bug report even if she wanted to. This discourages customers and they stop submitting bug reports. This is one of the reasons why software is getting so buggy.

Agreed. Many companies just don't give their users the possibility of submitting bug reports in any useful way. They'll tend to ignore bugs, as long as sales don't reflect the problem. Then if sales suddenly drop, they switch to panic mode. Which never really helps product quality either.

Of course that's not true for companies selling safety-critical stuff.
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #67 on: July 02, 2021, 03:57:21 pm »
Quote
Consider two scenarios:

Yep, considered them. They are made-up rubbish because the differentiator is step 3. You have no influence on getting to step 4, so from step 3 on it's completely arbitrary and shows nothing except difference user reactions.

Consider, instead, a scenario where you don't get to step 3 at all. Isn't that better?

Sure, we all make mistakes and no-one is perfect. But that's not a reason not to try to get it right, is it? Shrugging at step 3 and thinking "Shit happens, the punter will cope" doesn't seem to me to be an appropriate viewpoint.

And don't forget that the majority of your end users won't say a dicky. But they'll notice, oh yes.

Besides which, I take pride in a job well done. It ain't well done if it's faulty. Consider your viewpoint as a programmer transferred to, say, a cabinet maker. There is no way I would be ecstatic that someone points out one leg of a table is half a millimeter too short, or the support of a bridge is too thin, or the cut is a fraction too far that way, the sliding fit is notchy, etc.
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #68 on: July 02, 2021, 04:45:57 pm »
These are just simple human mistakes - something misspelled, misjudged, or misunderstood. You cannot prevent such mistake from happening. You can only fix them afterwards.
I'm not sure I even think of them as proper bugs anymore; they're so common and easy to fix.

But all bugs are like this. They're your mistakes of various kinds. While you can catch some of them in code reviews, most are best visible when you run your code and observe the effects.
Only the simple ones; they are so common and natural that I don't consider them "bugs" per se, just part of the expected, normal work flow.

When helping others with their code, I obviously don't know what is simple to them; it varies from person to person.  So I try to help make all sorts of "bugs" simple to fix and avoid, by using human concepts that aid in such cleanup work (and also in avoiding making any unnecessary mess in the first place, i.e. "buggy" code, or code with lots of unstated assumptions and undefined behaviour the users of the code do not know about).  Sometimes people are offended because they think that by doing that I believe I am belittling them, but I just don't know how else to find out what others are familiar with.

Asking "Do you know Z" does not work in practice, because people often dislike showing unawareness or "lack of knowledge" (it isn't, it is just having a different experience that hasn't encountered Z yet), and either say they do when they've never used it in anger, or misunderstand what is meant by Z... describing the things at the operations or algorithmic level just seems to yield better results in practice.  (But that isn't problem-free either: I recently angered brucehoult here by describing some virtual memory stuff, intending to keep everybody on par with the topic, and definitely not as a suggestion he might not know because I for sure knew he knows that stuff inside-out and was just really interested to uncover his differing opinion because I knew he knows that stuff.  But discussing it at the hoighty-toighty jargon level has just never worked well for me: I always associate it with blatant self-aggrandizement.)

I think we are pretty much everybody here in agreement that whatever you call the errors or issues, to err is human.  It is natural, and even the smartest person, or the best programmer on this planet, make them.  Frequently.  If their work output contains fewer of them than average, it only means their workflow is better equipped to deal with them, not that they make less of them in the first place.  (I've mentioned some of my own approaches, exactly because if I just wing it, I get even the parameter order of memset() often wrong.)

We do not "fix" such errors for ideological reasons –– say, because we think errors are somehow shameful or bad or reflect on the person ––, but because we find they cause unwanted behaviour or have a high enough risk of unexpected/unwanted behaviour.  (For example, consider write-only, unmaintainable spaghetti code: as long as it works as-is, and we have no indications that there is a problem with it, we live with it.  I guess this is the point many wish to emphasize, by saying "if it ain't broken, don't fix it".)

On the emotional level, I guess I do those bug hunts for the same reasons others adopt say a stretch of a highway or a road, and clean the trash out.  (Wait, some people insist on setting up placards telling about their good deeds; I don't mean that, and I dislike that.  Maybe a small note in the corner of a rest stop information board, AKA acknowledgements section.)  Or, when hiking, one makes sure a remote cabin has a bit more emergency resources when you leave compared to when you came, not leaving it without firewood, and so on.



You (and others above) do have a good point, though: I might get results I like better, if I myself change how I think about "bugs" versus documentation and specification et cetera.  It might even make my life easier wrt. language lawyers, and reduce unnecessary friction.

I shall investigate and experiment on this; thank you. :-+
« Last Edit: July 02, 2021, 08:21:26 pm by Nominal Animal »
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #69 on: July 02, 2021, 05:22:48 pm »
Besides which, I take pride in a job well done. It ain't well done if it's faulty.

So, you say you never make any bugs. May I ask how do you know that?
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #70 on: July 02, 2021, 06:06:11 pm »
Besides which, I take pride in a job well done. It ain't well done if it's faulty.

So, you say you never make any bugs. May I ask how do you know that?

Where did I say I never make mistakes? In fact, throughout this thread I've said I do make them. You're making shit up so you can knock it down, and that's not a good way to carry an argument.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #71 on: July 02, 2021, 06:47:53 pm »
Where did I say I never make mistakes? In fact, throughout this thread I've said I do make them. You're making shit up so you can knock it down, and that's not a good way to carry an argument.

What is a good way to make argument?

I argue that finding a bug is a positive event. Here:

... you encounter a bug. Either you find it yourself, or QA engineer filed a report, or user filed a support ticket. This is good luck for you. This gives you an opportunity to work on it and fix it.

You argue that discovering a bug is a bad thing. Here:

No, being told there is a bug is never a positive event.

When I try to show you why finding a bug is positive and want to hear your side of the story,  you answer that it's better not to have any bugs at all (which, besides being irrelevant, is a total bromide):

Consider, instead, a scenario where you don't get to step 3 at all. Isn't that better?

I take pride in a job well done. It ain't well done if it's faulty.

So, am I going to get any answer why is it bad to find a bug in your code?
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #72 on: July 02, 2021, 09:50:06 pm »
Quote
You argue that discovering a bug is a bad thing. Here:

Quote from: dunkemhigh on Yesterday at 17:55:56

    No, being told there is a bug is never a positive event.


Acutally, no. Being told there is a bug is a bad thing. Discovering a bug (thus preventing being told about it) is a good thing, but not in the sense that one gets a kick out of it. It's only good because it was fixed early before the code got out.

Quote
When I try to show you why finding a bug is positive and want to hear your side of the story,  you answer that it's better not to have any bugs at all

So you prefer to have bugs than not have bugs? Do you consciously add them just so you can feel ecstatic from fixing them?

That's an outrageous take on your comment, but it is exactly what you are doing to my comments.

And, yes, it is massively better not to have bugs. That isn't saying it won't have, or that one never makes mistakes (which is apparently what you perversely interpreted it to mean). But I'll humour you: why is bug-free code not better than buggy code? Seriously, I cannot think of a single valid reason.

Quote
So, am I going to get any answer why is it bad to find a bug in your code?

WTF? I've stated several times why. Oh, OK! I just twigged.... given the way you twist stuff, ignore stuff, you have to be trolling. Well done, you took me for a ride there.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: Mind over bugs: C pointer edition
« Reply #73 on: July 03, 2021, 01:33:38 am »
Acutally, no. Being told there is a bug is a bad thing. Discovering a bug (thus preventing being told about it) is a good thing, but not in the sense that one gets a kick out of it.

I didn't say anything about kicks. I am a business person and I don't care about emotions. I said it was positive. The person who found a bug in your software and told you about it is helping you. He gives you the information that you wouldn't otherwise have. And you can use this information to improve your software. This is why I think it is positive.

Unfortunately, people often get discouraged by the companies who don't want to hear about their bugs. Customers prefer coping with bugs rather than reporting them to the company. This is not a good situation for the business.

You still didn't say why it's bad when you're told you had a bug.

So you prefer to have bugs than not have bugs? Do you consciously add them just so you can feel ecstatic from fixing them?

That's an outrageous take on your comment, but it is exactly what you are doing to my comments.

Except these are not my comments. I've never said anything about feeling ecstatic.

And, yes, it is massively better not to have bugs. That isn't saying it won't have, or that one never makes mistakes (which is apparently what you perversely interpreted it to mean). But I'll humour you: why is bug-free code not better than buggy code? Seriously, I cannot think of a single valid reason.

I can. If your software has less bugs, people will buy more.

Every time you're told there's a bug, you fix it. This is one less bug in your software. This way the software gets better and better.
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #74 on: July 03, 2021, 02:55:44 am »
Do you consciously add them just so you can feel ecstatic from fixing them?
If that's re my comment, I definitely do not.  Chemically, the dopamine kick comes from improving something, and is more than negated by any conscious (and usually even any unconscious and purely accidental) act of damage.

There are many unhealthy human psychopathologies involving breaking things just so you can fix them, but I am very happy to report I personally do not suffer from any of those.

I also seriously dislike insinuations like that.  How would you feel if someone commented to you that perhaps you are a parent only because you have a case of Munchhausen syndrome by proxy?  No, that shit goes too far.  I suggest you edit out your insinuations before you start something you really don't want to finish.
 

Online PlainName

  • Super Contributor
  • ***
  • Posts: 6844
  • Country: va
Re: Mind over bugs: C pointer edition
« Reply #75 on: July 03, 2021, 10:39:57 am »
Quote
Quote
    But I'll humour you: why is bug-free code not better than buggy code? Seriously, I cannot think of a single valid reason.

I can. If your software has less bugs, people will buy more.

That explains everything! You either cannot read or cannot understand what you're seeing. You post was littered with similar failures of comprehension, as were numerous previous comments.
 

Offline Feynman

  • Regular Contributor
  • *
  • Posts: 192
  • Country: ch
Re: Mind over bugs: C pointer edition
« Reply #76 on: July 03, 2021, 11:39:59 am »
Well, I bowed out of the discussion when he was explaining how it took him "several months" to learn programming without gotos :D
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #77 on: July 03, 2021, 01:43:52 pm »
If you release bugs you havent tested enough.
If its work for money i can understand.
I better not work for money.
 

Offline Jan Audio

  • Frequent Contributor
  • **
  • Posts: 820
  • Country: nl
Re: Mind over bugs: C pointer edition
« Reply #78 on: July 03, 2021, 01:47:45 pm »
Take a look to where the world is going,
they expect you to take the garbage they give, so they can fix it later over internet or USB.
Else they dont make any money if people buy the other brand, that do has a buggy release.
Do you ever hear about bugs in CASIO ?
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: Mind over bugs: C pointer edition
« Reply #79 on: July 03, 2021, 04:41:15 pm »
Do you consciously add them just so you can feel ecstatic from fixing them?
If that's re my comment, I definitely do not.

Actually, if anyone in this thread understood what dunkemhigh said here from anyone else posting here, I'd be very surprised. It looks like a pretty twisted way of interpreting things.

I'm sure the arguing here mostly comes from mutual misunderstandings, but seriously dunkemhigh, you probably should tone it down. You seem particularly aggressive here for no good reason, jumping on people when you think they have misinterpreted you, while doing the exact same thing to them.

The thread has obviously largely drifted towards off-topicism, but there still were some interesting points discussed. No need to ruin it with aggressiveness and pointless arguing.


 
The following users thanked this post: Siwastaja, Nominal Animal

Offline emece67

  • Frequent Contributor
  • **
  • !
  • Posts: 614
  • Country: 00
Re: Mind over bugs: C pointer edition
« Reply #80 on: July 03, 2021, 11:13:45 pm »
.
« Last Edit: August 19, 2022, 04:30:19 pm by emece67 »
 

Offline Nominal AnimalTopic starter

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: Mind over bugs: C pointer edition
« Reply #81 on: July 04, 2021, 07:17:42 pm »
So, what's my point? It is, can smart pointers ala C++ be of any help here.
Very true.  I like the way you put it, because it highlights that C++ is not the same language as C (exactly because it tries hard to avoid the shortcomings in C and has a very different set of abstractions).

I myself write a lot of systems-level code: utility tools, low-level libraries, service daemons, and such stuff.
There I use C, especially for libraries, as it is the lowest common denominator, and does not introduce any additional dependencies to other applications and libraries.

I do not use C++ there, because it introduces a runtime dependency to a particular version of the C++ standard library.  (Right now on this machine, I only have 5.0.7 and 6.0.25 installed, so the likelihood of wanting to use something that depends on one while my C++ code uses the other, creating version conflicts, is low.  But I've bitten by this before, when it was common a new version of the C++ runtime (dynamic libraries) appeared every couple of months on my machines.)
Note that in Linux, even pure-C++ dynamically linked binaries have a run time dependency on the standard C dynamic library.  (Actually, *all* dynamically linked binaries on my system, even rustc, are linked against the standard C dynamic library, libc.so.6 in this case.  Just checked via ldd.)

I could use C++ for GUIs (and do for Qt on raw framebuffer), but I prefer to use Python3 + C for the low-level stuff instead; just a personal preference.

On the other end, for microcontrollers and embedded environments (including Arduino), I use a weird combination of freestanding C and a subset C++, with compiler extensions sprinkled on top.  There, using a selected subset of C++ features, with a coding style/paradigm not really being "C" or "C++" but of this particular environments own, just works best for me.

So, choosing the toolset, including the programming language and associated libraries, is very, VERY important.

Even the set of libraries you use at the application level affects your approach.  Consider Boost and Boehm GC for example.  Or, if you do any math work in C, you've probably at some time met BLAS and LAPACK, which still carry their own flavour from their FORTRAN roots (only slightly hidden when shimmed by higher-level libraries like the Intel Math Kernel Library or AMD Optimizing CPU Libraries).  Working with GNU Scientific Library (GSL) in C, and then having to go back to BLAS/LAPACK land, can be painful.  For Fourier transforms, the concept of "wisdom" implemented by FFTW3 affects your applications down to run time and user help stuff.  (There being many different ways to implement the computation of a particular transform, "wisdom" is the label used to denote that given a specific transform of a specific size a certain approach is computationally efficient, sometimes optimal.  "Wisdom" is stored in per-user configuration files.  Lack of "wisdom" makes your program slow, but gathering or generating "wisdom" is usually much much slower; but it only needs to be gathered once per dataset size and transform.  A proper FFTW3-using program lets the user choose whether to do a particular operation in a wisdom-gathering manner, rely on existence of wisdom on the problem at hand, or to pick a balance in between so that even with complete lack of wisdom a reasonably efficient approach is used without spending any effort to optimize repeated similar transforms of the same size.  See?  Changes the entire "feel" of the program, just by choosing to use a particular library.)
 

Offline bd139

  • Super Contributor
  • ***
  • Posts: 23024
  • Country: gb
Re: Mind over bugs: C pointer edition
« Reply #82 on: July 04, 2021, 07:26:42 pm »
Take a look to where the world is going,
they expect you to take the garbage they give, so they can fix it later over internet or USB.
Else they dont make any money if people buy the other brand, that do has a buggy release.
Do you ever hear about bugs in CASIO ?

Casio stuff is chock full of bugs. There’s a good one in the 991 ES PLUS. If you use a repetitive function then STO it in A then it stores the previous value not the current one  |O

I haven’t found a single Casio calculator that doesn’t have something wrong with it. Even had a Casio keyboard once that I could crash.
 
The following users thanked this post: Jan Audio


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf