Skip to content

Commit cf4a209

Browse files
committed
Fixed issues in C code examples and added modern standard idioms (C99 & C11).
The C examples used in that file are not purely C and contain several C++ ism. Furthermore, they do not use C99 idioms that are now very common. Some of the examples also contain errors. - typecasts of allocation functions are typical of C++ code but are errors in pure C. - names with leading underscore are reserved for the implementation and shall not be used. - One example freed an unitialized pointer -> crash guaranteed. - Where appropriate, modified or extended the C examples with constructs from C99 and C11. Mainly designated initializers.
1 parent c39aa29 commit cf4a209

File tree

1 file changed

+70
-39
lines changed

1 file changed

+70
-39
lines changed

ctod.dd

Lines changed: 70 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -208,14 +208,14 @@ $(H3 <a name="nans">Dealing with NANs in floating point compares</a>)
208208
$(H4 The C Way)
209209

210210
C doesn't define what happens if an operand to a compare
211-
is NAN, and few C compilers check for it (the Digital Mars
212-
C compiler is an exception, DM's compilers do check for NAN operands).
211+
is `NAN`, and few C compilers check for it (the Digital Mars
212+
C compiler is an exception, DM's compilers do check for `NAN` operands).
213213

214214
$(CCODE
215215
#include &lt;math.h&gt;
216216

217217
if (isnan(x) || isnan(y))
218-
result = FALSE;
218+
result = false;
219219
else
220220
result = (x &lt; y);
221221
)
@@ -233,9 +233,10 @@ result = (x < y); // false if x or y is nan
233233
$(H3 <a name="assert">Asserts are a necessary part of any good defensive coding strategy</a>)
234234

235235
$(H4 The C Way)
236-
$(P C doesn't directly support assert, but does support __FILE__
237-
and __LINE__ from which an assert macro can be built. In fact,
238-
there appears to be practically no other use for __FILE__ and __LINE__.)
236+
$(P C doesn't directly support assert in the language, but does define a
237+
macro in the standard library header assert.h. That macro writes a diagnostic
238+
message on stderr when the condition given as parameter is not true. The message
239+
will use __FILE__, __LINE__ and __func__ (C99) to localize the failing assertion.
239240

240241
$(CCODE
241242
#include &lt;assert.h&gt;
@@ -275,8 +276,8 @@ $(H3 <a name="arrayloop">Looping through an array</a>)
275276

276277
$(H4 The C Way)
277278
$(P
278-
The array length is defined separately, or a clumsy
279-
sizeof() expression is used to get the length.)
279+
The array length is defined separately, or a clumsy and error prone
280+
`sizeof()` expression is used to get the length.)
280281

281282
$(CCODE
282283
#define ARRAY_LENGTH 17
@@ -343,8 +344,7 @@ int array_length;
343344
int *array;
344345
int *newarray;
345346

346-
newarray = (int *)
347-
realloc(array, (array_length + 1) * sizeof(int));
347+
newarray = realloc(array, (array_length + 1) * sizeof(int));
348348
if (!newarray)
349349
error("out of memory");
350350
array = newarray;
@@ -380,9 +380,8 @@ char *s2;
380380
char *s;
381381

382382
// Concatenate s1 and s2, and put result in s
383-
free(s);
384-
s = (char *)malloc((s1 ? strlen(s1) : 0) +
385-
(s2 ? strlen(s2) : 0) + 1);
383+
s = malloc((s1 ? strlen(s1) : 0) +
384+
(s2 ? strlen(s2) : 0) + 1);
386385
if (!s)
387386
error("out of memory");
388387
if (s1)
@@ -394,10 +393,8 @@ if (s2)
394393

395394
// Append "hello" to s
396395
char hello[] = "hello";
397-
char *news;
398396
size_t lens = s ? strlen(s) : 0;
399-
news = (char *)
400-
realloc(s, (lens + sizeof(hello) + 1) * sizeof(char));
397+
char *news = realloc(s, lens + sizeof(hello) + 1);
401398
if (!news)
402399
error("out of memory");
403400
s = news;
@@ -612,7 +609,9 @@ $(CCODE
612609
void dostring(char *s)
613610
{
614611
enum Strings { Hello, Goodbye, Maybe, Max };
615-
static char *table[] = { "hello", "goodbye", "maybe" };
612+
static char *table[] = { [Hello] ="hello",
613+
[Goodbye]="goodbye",
614+
[Maybe] ="maybe" };
616615
int i;
617616

618617
for (i = 0; i &lt; Max; i++)
@@ -634,7 +633,9 @@ void dostring(char *s)
634633
structures, the enum, the table, and the switch cases. If there
635634
are a lot of values, the connection between the 3 may not be so
636635
obvious when doing maintenance, and so the situation is ripe for
637-
bugs.
636+
bugs. Designated initializers as were introduced with C99 allow
637+
to link correctly 2 of the 3 data structures, but at the cost of
638+
a lot of typing.
638639

639640
Additionally, if the number of values becomes large, a binary or
640641
hash lookup will yield a considerable performance increase over
@@ -718,23 +719,23 @@ Sometimes, it's nice to control the layout of a struct with nested structs and u
718719

719720
$(H4 The C Way)
720721

721-
C doesn't allow anonymous structs or unions, which means that dummy tag names
722-
and dummy members are necessary:
722+
Before C11 C didn't allow for anonymous structs or unions, which meant that
723+
dummy member names were necessary:
723724

724725
$(CCODE
725726
struct Foo
726727
{
727728
int i;
728-
union Bar
729+
union
729730
{
730-
struct Abc { int x; long y; } _abc;
731+
struct { int x; long y; } abc;
731732
char *p;
732-
} _bar;
733+
} bar;
733734
};
734735

735-
#define x _bar._abc.x
736-
#define y _bar._abc.y
737-
#define p _bar.p
736+
#define x bar.abc.x
737+
#define y bar.abc.y
738+
#define p bar.p
738739

739740
struct Foo f;
740741

@@ -839,10 +840,12 @@ $(H4 The C Way)
839840
$(CCODE
840841
union U { int a; long b; };
841842
union U x = { 5 }; // initialize member 'a' to 5
843+
union U y = { .b = 42l }; // initialize member 'b' to 42 (C99)
844+
842845
)
843846

844847
Adding union members or rearranging them can have disastrous consequences
845-
for any initializers.
848+
for any initializers. Designated initializers in C99 fix that issue.
846849

847850
$(H4 The D Way)
848851

@@ -865,13 +868,15 @@ $(H4 The C Way)
865868
$(CCODE
866869
struct S { int a; int b; };
867870
struct S x = { 5, 3 };
871+
struct S y = { .b=3, .a=5 }; /* C99 */
868872
)
869873

870874
This isn't much of a problem with small structs, but when there
871875
are numerous members, it becomes tedious to get the initializers
872876
carefully lined up with the field declarations. Then, if members are
873877
added or rearranged, all the initializations have to be found and
874-
modified appropriately. This is a minefield for bugs.
878+
modified appropriately. This is a minefield for bugs. Designated
879+
initializers in C99 fix that issue.
875880

876881
$(H4 The D Way)
877882

@@ -889,9 +894,11 @@ $(H3 <a name="arrayinit2">Array Initializations</a>)
889894

890895
$(H4 The C Way)
891896

892-
C initializes array by positional dependence:
897+
C initializes array by positional dependence. C99 fixes the issue:
893898
$(CCODE
894-
int a[3] = { 3,2,2 };
899+
int a[3] = { 3,2,1 };
900+
int a[3] = { [2]=1, [0]=3, [1]=2 }; /* C99 designated initializer */
901+
int a[3] = { [2]=1, [0]=3, 2 }; /* C99 designated initializer */
895902
)
896903
Nested arrays may or may not have the { }:
897904
$(CCODE
@@ -901,6 +908,7 @@ int b[3][2] = { 2,3, {6,5}, 3,4 };
901908
$(H4 The D Way)
902909

903910
D does it by positional dependence too, but an index can be used as well.
911+
The D syntax is lighter than C99 designated initializers.
904912
The following all produce the same result:
905913

906914
----------------------------
@@ -981,9 +989,18 @@ $(CCODE
981989
#include &lt;tchar.h&gt;
982990
tchar string[] = TEXT("hello");
983991
)
992+
Furthermore, in praxis `wchar_t` is not usable in portable code as its size
993+
is implementation dependent. On POSIX conforming machines it generally
994+
represents an UTF-32 codeunit, on Windows an UTF-16 codeunit. C11 introduced
995+
C++11 types char16_t and char32_t to overcome this issue.
996+
984997
$(H4 The D Way)
985998

986-
The type of a string is determined by semantic analysis, so there is no need to wrap strings in a macro call. Alternatively if type inference is used the string can have a $(B c), $(B w) or $(B d) suffix, representing UTF-8, UTF-16 and UTF-32 encoding, respectively. If no suffix is used the type is inferred to be a UTF-8 string:
999+
The type of a string is determined by semantic analysis, so there is no need
1000+
to wrap strings in a macro call. Alternatively if type inference is used the
1001+
string can have a `c`, `w` or `d` suffix, representing UTF-8,
1002+
UTF-16 and UTF-32 encoding, respectively. If no suffix is used the type is
1003+
inferred to be a UTF-8 string:
9871004
-----------------------------
9881005
string utf8 = "hello"; // UTF-8 string
9891006
wstring utf16 = "hello"; // UTF-16 string
@@ -1004,8 +1021,12 @@ $(H4 The C Way)
10041021
$(CCODE
10051022
enum COLORS { red, blue, green, max };
10061023
char *cstring[max] = {"red", "blue", "green" };
1024+
char *cstring[max] = {[red]="red", [blue]="blue", [green]="green" }; /* C99 */
10071025
)
1008-
This is fairly easy to get right because the number of entries is small. But suppose it gets to be fairly large. Then it can get difficult to maintain correctly when new entries are added.
1026+
This is fairly easy to get right because the number of entries is small.
1027+
But suppose it gets to be fairly large. Then it can get difficult to
1028+
maintain correctly when new entries are added. C99 added designated
1029+
initializers to solve that problem.
10091030

10101031
$(H4 The D Way)
10111032
-----------------------------
@@ -1071,7 +1092,7 @@ if (h != HANDLE_INIT)
10711092
$(CCODE
10721093
struct Handle__ HANDLE_INIT;
10731094

1074-
void init_handle() // call this function upon startup
1095+
void init_handle(void) // call this function upon startup
10751096
{
10761097
HANDLE_INIT.value = (void *)-1;
10771098
}
@@ -1087,9 +1108,9 @@ if (memcmp(&amp;h,&amp;HANDLE_INIT,sizeof(Handle)) != 0)
10871108

10881109
$(H4 The D Way)
10891110

1090-
D has powerful metaprogramming abilties which allow it to implement
1091-
$(D typedef) as a library feature. Simply import $(B std.typecons) and
1092-
use the $(B Typedef) template:
1111+
D has powerful metaprogramming abilities which allow it to implement
1112+
$(D typedef) as a library feature. Simply import `std.typecons` and
1113+
use the `Typedef` template:
10931114

10941115
-----------------------------
10951116
import std.typecons;
@@ -1103,7 +1124,7 @@ foo(h); // syntax error
11031124
bar(h); // ok
11041125
-----------------------------
11051126

1106-
To handle a default value, pass the initializer to the $(B Typedef)
1127+
To handle a default value, pass the initializer to the `Typedef`
11071128
template as the second argument and refer to it with the
11081129
$(D .init) property:
11091130

@@ -1230,12 +1251,14 @@ qsort(array, sizeof(array)/sizeof(array[0]),
12301251
)
12311252

12321253
A compare() must be written for each type, and much careful
1233-
typo-prone code needs to be written to make it work.
1254+
typo-prone code needs to be written to make it work. The indirect subroutine
1255+
call required for each comparison required by the function pointer limits
1256+
the achievable performance of the `qsort()` routine.
12341257

12351258

12361259
$(H4 The D Way)
12371260

1238-
D has a powerful $(B std.algorithm) module with optimized
1261+
D has a powerful `std.algorithm` module with optimized
12391262
sorting routines, which work for any built-in or user-defined
12401263
type which can be compared:
12411264

@@ -1258,6 +1281,14 @@ $(CCODE
12581281
"This text spans\n\
12591282
multiple\n\
12601283
lines\n"
1284+
)
1285+
1286+
C's string literal concatenation doesn't really solve the problem:
1287+
1288+
$(CCODE
1289+
"This text spans\n"
1290+
"multiple\n"
1291+
"lines\n"
12611292
)
12621293

12631294
If there is a lot of text, this can wind up being tedious.

0 commit comments

Comments
 (0)