Post by Michael SOn Thu, 23 May 2024 22:10:22 +0200
Post by David BrownPost by Michael SOn Wed, 22 May 2024 18:55:36 +0200
Post by David BrownIn an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in
field of introducing new keywords (too liberal for my liking, many
new things do not really deserve keywords not prefixed by __) is so
conservative in introduction of program control constructs? I don't
remember any new program control introduced under Committee regime.
And I want at least one.
What program control construct would you like?
Ability to break from nested loops. Ability to"continue" outer loops
would be nice too, but less important.
I am not sure what syntax I want for this feature, never considered
myself a competent language designer.
I've heard people request this before. I can't say I've ever felt it
was something I'd have use for, but there's lots of things in C that I
never need.
There is a proposal for adding it to C:
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3195.htm>
Post by Michael SPost by David BrownPost by Michael SAnother area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long. If I am not mistaken, totally obsolete 'register'
class is still allowed.
"register" is still in C23. (Some compilers pay attention to it.
gcc with optimisation disabled puts local variables on the stack,
except for those marked "register" that get put in registers.) It
got dropped from C++ when "auto" was re-purposed in C++11, but with
the keyword "register" kept for future use. I would not have
objected to the same thing happening in C23.
Post by Michael SAnd I don't remember any additions.
_Thread_local was added in C11, with the alias thread_local in C23.
_Thread_local is a special-purpose thing, probably not applicable at
all for programming of small embedded systems, which nowadays is the
only type of programming in C that I do for money rather than as hobby.
I have never seen the point of it either. Why would anyone want a
variable that exists for /all/ threads in a program, but independently
per thread? The only use I can think of is for errno (which is, IMHO, a
horror unto itself) but since that is defined by the implementation, it
does not need to use _Thread_local. (Indeed, thread-local errno macros
existed long before C11.)
You and I (as small embedded systems programmers) are perhaps biased in
being allergic to the wasted bytes of ram a thread-local variable would
likely use!
Post by Michael SWith regard to constexpr, mentioned above by James Kuyper, my feeling
about it is that it belongs to metaprogramming so I would not consider
it a real storage class.
The term "storage-class specifier" is a bit of a misnomer, in that it is
more of a syntactic term than referring just to the storage duration or
placement of objects. "typedef" is also a storage-class specifier, for
example.
Good idea.
Post by Michael S1. global objects, declared in header files and included several times.
Where defined?
In C, they must be defined in exactly one translation unit.
Post by Michael SFor some linkers, mostly unixy linkers, in case of none-initialized
objects (implicitly initialized to zero) it somehow works.
The use of "int global_x;" in headers is undefined behaviour (AFAIK) in
C, and its support is a hangover from linker support for Fortran common
blocks. And it is the source of many odd errors for people who are not
careful enough in their coding. If your compiler supports this
misfeature (such as "gcc -fcommon"), and you accidentally declare two
uninitialised non-static variables with the same name in two files, you
have no detection or protection from the chaos that ensues. With
compilers that don't support this ("gcc -fno-common"), you get a
link-time error showing your problem. (gcc made "-fno-common" the
default in version 10. That's 9 major versions late, IMHO, but better
late than never.)
(To the extent that it "works", it is handled by putting the symbol name
and data space reservation in a "common" section. At link time, common
symbols with the same name are merged - whether that makes sense for the
code or not.)
Post by Michael SFor linkers used on embedded systems it requires additional effort.
I can't say I have ever seen it as an effort. Almost all my C "modules"
come in pairs - "file.h" and "file.c". All non-local variables (and all
functions) are either static and declared only in "file.c", or they are
externally linked and have an "extern" declaration in "file.h" and a
definition (with or without initialisation) in "file.c" (which #includes
"file.h"). It is a very simple and clean arrangement, easily checked by
gcc warnings, and there are never any undetected conflicts.
(And probably 90% or more of current small-systems embedded development
uses gcc and binutils linker.)
Post by Michael SI think, for initialized globals it takes additional effort even with
unixy linkers.
I wnat it to "just work" everywhere. I think that the best way to get
it without breaking existing semantics is a new storage class.
This is all very much a non-issue for well-structured code.
The only time it can matter is if you want to write "header-only"
modules. This is popular in C++, but not in C. In C++ it relies on the
linker merging the same symbols for inline declarations such as inline
functions, template functions, template class and function statics, and
- since C++17 - inline variables. So you can write "inline int
global_x;" or "inline int global_y = 123;" in a header, and it will be
created once and only once (if it is used somewhere). The
compiler/linker can't check for consistency of initialiser, unless you
are using link-time optimisation.
Post by Michael S2. Reversing defaults for visibility of objects and functions at file
scope.
#pragma export_by_default(off).
When this pragma is in effect, we need a way to make objects and
functions globally visible. I think that it's done best with new
storage class.
I would much prefer if file-level variables and functions were "static"
by default and required explicit exporting. But that ship sailed 50
years ago.
What you can do - what /I/ do - is be rigid in making sure all your
exported variables and functions are declared as "extern" in a header
that is also included by the defining C file. Use "gcc
-Werror=missing-declarations -Werror=missing-variable-declarations" to
enforce these rules. It is not quite as good as going back in time and
fixing C at the start, but it's close!