A quick primer on C3
This primer is intended as a guide to how the C syntax – and in some cases C semantics – is different in C3. It is intended to help you take a piece of C code and understand how it can be converted manually to C3.
Struct, enum and union declarations
Don’t add a ;
after enum, struct and union declarations, and note the slightly
different syntax for declaring a named struct inside of a struct.
Also, user defined types are used without a struct
, union
or enum
keyword, as
if the name was a C typedef.
Arrays
Array sizes are written next to the type and arrays do not decay to pointers, you need to do it manually:
You will probably prefer slices to pointers when passing data around:
Note that declaring an array of inferred size will look different in C3:
Arrays are trivially copyable:
See more here.
Undefined behaviour
C3 has less undefined behaviour, in particular integers are defined as using 2s complement and signed overflow is wrapping. See more here.
Functions
Functions are declared like C, but you need to put fn
in front:
See more about functions, like named and default arguments here.
Calling C functions
Declare a function (or variable) with extern
and it will be possible to
access it from C3:
Note that currently only the C standard library is automatically passed to the linker. In order to link with other libraries, you need to explicitly tell the compiler to link them.
If you want to use a different identifier inside of your C3 code compared to
the function or variable’s external name, use the @extern
attribute:
Identifiers
Name standards are enforced:
Variable declaration
Multiple declarations together with initialization isn’t allowed in C3:
In C3, variables are always zero initialized, unless you explicitly opt out using @noinit
:
Compound literals
Compound literals use C++ style brace initialization, not cast style like in C. For convenience, assigning to a struct will infer the type even if it’s not an initializer.
typedef and #define becomes ‘def’
typedef
is replaced by def
:
def
also allows you to do things that C uses #define
for:
Read more about def
here.
Basic types
Several C types that would be variable sized are fixed size, and others changed names:
Read more about types here.
Instead of #include: Modules and import
Declaring the module name is not mandatory, but if you leave it out the file name will be used as the module name. Imports are recursive.
Comments
The /* */
comments are nesting
Note that doc comments, starting with /**
has special rules for parsing it, and is
not considered a regular comment. See contracts for more information.
Type qualifiers
Qualifiers like const
and volatile
are removed, but const
before a constant
will make it treated as a compile time constant. The constant does not need to be typed.
volatile
is replaced by macros for volatile load and store.
Goto removed
goto
is removed, but there is labelled break
and continue
as well as defer
to handle the cases when it is commonly used in C.
Changes in switch
case
statements automatically break. Use nextcase
to fallthrough to the
next statement, but empty case statements have implicit fallthrough:
Note that we can jump to an arbitrary case using C3:
Bitfields are replace by explicit bitstructs
A bitstruct has an explicit container type, and each field has an exact bit range.
There exists a simplified form for a bitstruct containing only booleans, it is the same except the ranges are left out:
For more information see the section on bitstructs.
Other changes
The following things are enhancements to C, that does not have a direct counterpart in C.
- Expression blocks
- Defer
- Methods
- Optionals
- Semantic macros
- Generic modules
- Contracts
- Compile time evaluation
- Reflection
- Operator overloading
- Macro methods
- Static initialize and finalize functions
- Dynamic interfaces
For the full list of all new features see the feature list.
Finally, the FAQ answers many questions you might have as you start out.