
Some things every C programmer should know about C (2002) - kick
https://web.archive.org/web/20030812081713/http://klausler.com/cnotes.txt
======
zabzonk
> Bitfields may appear only as struct/union members, so there are no pointers
> to bitfields, arrays of bitfields

You can certainly have a pointer to a struct member, so this isn't the reason
why you can't have pointers to bitfields. The reason is that bits are not
addressable, fullstop.

~~~
mytailorisrich
Yes, that's straightforward at least when coming to C from assembly (as it
used to be the case once upon a time...) In practice, though, bitfields are
seldom used, so everything related to them is largely academic.

~~~
wahern
Bitfields aren't _commonly_ used, especially in public interfaces. But usage
is far more prevalent than for typical "largely academic" features. For
example,

    
    
      $ grep -rE ':[[:digit:]][[:digit:]]*;' /usr/include/ | wc -l
         702
      $ uname -a
      OpenBSD orville.25thandClement.com 6.6 GENERIC.MP#3 amd64
    
      $ grep -rE ':[[:digit:]][[:digit:]]*;' /usr/include/ | wc -l
      276
      $ uname -a
      Linux alpine-3-10 4.9.65-1-hardened #2-Alpine SMP Mon Nov 27 15:36:10 GMT 2017 x86_64 Linux
    
      $ grep -rE ':[[:digit:]][[:digit:]]*;' /usr/include/ | wc -l
      532
      $ uname -a
      Linux splunk0 5.0.0-36-generic #39-Ubuntu SMP Tue Nov 12 09:46:06 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

~~~
dtornabene
they're also more commonly used in security tooling

------
netule
This reminds me somewhat of Deep C (2011), a great read for C/C++ programmers:
[https://www.slideshare.net/olvemaudal/deep-c](https://www.slideshare.net/olvemaudal/deep-c)

------
williamDafoe
One mistake in the first native C compiler was that all struct arms were in
the same namespace, so

    
    
      struct a {
        struct *a next;
      };
    
      struct b {
        struct *b next;
      };
    

was illegal because the second declaration of "next" is a redeclaration.

~~~
asveikau
This is true. It also explains why a lot of old-time Unix struct member names
(including a lot that made it into POSIX or are otherwise in use today) have
redundant-seeming prefixes. eg. _struct stat_ has _st_mode_ and not simply
_mode_. _struct sockaddr_in_ has _sin_addr_ and not simply _addr_.

~~~
wahern
That's still common today for subsequent interfaces as it makes it possible to
write macros for preserving API compatibility. For example, to upgrade (struct
stat).st_mtime to sub-second granularity you could do:

    
    
      #define st_mtime st_mtimensec.tv_sec
    

POSIX preserves many prefixes for this reason. C99 brought anonymous unions (a
Plan 9 invention), which makes many of these API tricks possible without using
macros.

~~~
wahern
Correction: C11 brought anonymous unions, which perhaps explains why they're
not used in standard headers. Standard headers on most unix systems do make
use of C99 features these days.

~~~
asveikau
Thanks for looking that up, I didn't know the timeframe on that feature.

I've known for a long time that Microsoft headers make use of anonymous
unions, which makes it a rare case where they have implemented a recent ISO C
feature ahead of time. I seem to recall GCC in the same timeframe would accept
this with a warning that it's nonstandard. [I guess not anymore.]

~~~
wahern
In addition to C11's anonymous untagged members,

    
    
      struct foo { union { int i; double d; }; }
    

Visual Studio also supports anonymous tagged members,

    
    
      union bar { int i; double d; };
      struct foo { union bar; };
    

Is the latter more common in Microsoft land? I've always thought the latter
were more useful and wished they were standardized. Anonymous tagged fields
make it possible to reuse plain struct and union definitions, permitting a
simple form of inheritance; whereas with untagged fields you have to rely on
macros for sharing definitions.

GCC ([https://gcc.gnu.org/onlinedocs/gcc/Unnamed-
Fields.html](https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html)) and
clang (confirmed macOS clang-1001.0.46.4) support anonymous tagged members
with -fms-extensions, but I can't bring myself to make use of it as I still
try to keep many of my projects working with Sun Studio and otherwise try to
avoid non-standard features.

~~~
asveikau
> Is the latter more common in Microsoft land?

I don't know if I have an authoritative sample on that. I saw it exactly once
when I worked at MS and I thought it was weird. But I guess it aids in the
"psuedo-inheritance" game that people play with structs and their first member
being a "base class". Like GObject or gtk+ do. Or struct sockaddr, which
duplicates the first few fields.

------
nayuki

      > There is an implicit "x != 0" in: if (x), while (x), ... etc.
      > An explicit "x != 0" in these contexts serves no semantic purpose.
      > And "x == 0" in these contexts might be better written as "!x".
    

I disagree with this advice because the idea is not portable across languages.

For example, if we let the variable x hold the integer value 0, then:

    
    
      Java    'if (x)' is a compile-time type error.
      Python  'if x:'  is falsy, similar to C.
      Ruby    'if x'   is truthy!

~~~
raverbashing
Yeah... Ruby is in the wrong there. Not even js does that

Insisting if takes a bool like java does is perfectly acceptable. But just
taking a 0 value and making it "true" makes sense only in Bash?

~~~
lmkg
It depends on what the set of falsey values is for that language.

E.g. in Common Lisp, the only falsey value is NIL (the empty list). This is
easy to understand, easy to remember, and straightforward in practice because
idiomatic Lisp does a lot of list operations. It wouldn't make sense for 0 to
be truthy, because that's a legit value, and NIL represents the absence of a
value.

I don't know what values are falsey in Ruby, so I can't comment on it.

------
klingonopera
> _Given two operands to a binary operator, find the first type in this list
> that matches one of the operands, then convert the other operand to that
> type._

Does anyone know for sure whether or not the Arduino compiler deviates from
this standard, or ever had, in previous versions?

Does multiplication qualify as a binary operator?

IIRC, I think I was compound multiplying the product of an int and a char into
a long (see below), and I had to explicitly cast the char as int to get the
right results, much to my surprise, as I had expected the char to get promoted
to an int. But I could also have made some mistakes, it's been awhile...

    
    
      longType *= intType * (int)charType;
    

EDIT: Found my old code snippet, and it kinda invalidates my original
question, what I was actually doing was

    
    
      longType *= charType * charType * charType
    

which leads the right side to evaluate first without promotion, thus
modulating the end result by 256, which was not intended. I then cast all of
them as long, but probably (certainly!...?) only one would've been enough.

EDIT2: Or at least, two of them have to be casted, else it might evaluate two
chars first, modulating the result, before actually promoting to long to
multiply with another long. (Or not, because it promotes to the most
significant data type on the right side? Ugh... this is why I prefer to be
extra verbose.)

~~~
bxparks
Arduino boards (the ones based on AVR chips) use the GCC C++ compiler adapted
for the AVR. The current IDE version 1.8.10 uses avr-gcc-7.3.0
([https://github.com/arduino/Arduino/blob/master/hardware/pack...](https://github.com/arduino/Arduino/blob/master/hardware/package_index_bundled.json)).
The previous IDE version 1.8.9 used avr-gcc-5.4.0.

The compiler is configured to follow the -std=gnu++11 variant. Here are the
flags: [https://github.com/arduino/ArduinoCore-
avr/blob/master/platf...](https://github.com/arduino/ArduinoCore-
avr/blob/master/platform.txt)

If you use other Arduino-compatible boards (e.g. ARM chips, ESP8266 or ESP32),
they will use a different C++ compiler and different settings. See for
example:

* SAMD uses 'arm-none-eabi-g++' ([https://github.com/arduino/ArduinoCore-sam/blob/master/platf...](https://github.com/arduino/ArduinoCore-sam/blob/master/platform.txt))

* ESP8266 uses xtensa-lx106-elf-gcc ([https://github.com/arduino/esp8266/blob/master/platform.txt](https://github.com/arduino/esp8266/blob/master/platform.txt))

* ESP32 uses xtensa-esp32-elf-gcc ([https://github.com/espressif/arduino-esp32/blob/master/platf...](https://github.com/espressif/arduino-esp32/blob/master/platform.txt))

So Arduino programming is "just" C++ with additional libraries and an IDE.
Though I wrote "just" in quotes because they've done a lot of work to make
things easier and approachable for beginners, so I don't want to diminish
their work.

~~~
klingonopera
Thanks, looks like there's an _-mmcu_ flag that gets passed to the compiler as
well, telling it what chip the code is intended for.

I think the answers I'm looking for are buried in those specifications. Gotta
check them out, one of these days...

> _So Arduino programming is "just" C++ with additional libraries and an IDE._

Considering that flag, I think it's something more (or _less_ ) than just
plain C++...

------
jolmg
> Operator Precedence and Associativity

There's also the operator(7) manpage for quick reference.

------
azhenley
I'm surprised a bit by the "Translation Steps" part at the end.

I assumed much of the steps listed happened during parsing, such as processing
escape characters and converting newlines, but those are done beforehand.

Perhaps it is because C uses a preprocessor and some macros would not be
possible if all the steps were performed while parsing?

------
mainnode
Remember kids: Bitfields are not thread-safe!

~~~
zabzonk
Nothing in C is thread-safe out of the box.

~~~
saagarjha
Certain operations are, such as static variable initialization (which I
believe is done during program startup in C).

~~~
zabzonk
Static variable initialisation that is not initialisation of such variables
local to functions is performed before the program starts running, so there
cannot be multiple threads.

I can't remember the case in C, but in C++ the initialisation of a function
local static variable is guaranteed to be thread-safe.

~~~
int_19h
It doesn't matter that much whether the variable is global or local, but
rather what the valid initializers are.

In C, all statics can only be initialized with compile-time constants. Thus,
they can all be initialized before anything in the program starts running
(indeed, there's no code initializing them in most implementations - they are
just bytes in the data segment).

In C++, initializers can be arbitrary expressions, which means that an
initializer for a global variable can spawn a thread, and that thread might
still be running when another global is being initialized with a different
expression that is not a compile-time constant. There are no thread safety
guarantees wrt those kinds of conflicts, for either globals or locals. But for
static locals with runtime initializers, the evaluation of initializer is
deferred until execution actually reaches the definition of that variable -
and thus C++ has a special provision for thread-safe synchronization if that
happens on two different threads concurrently.

