source: doc/theses/mike_brooks_MMath/background.tex@ f1ffc47

Last change on this file since f1ffc47 was f1ffc47, checked in by Michael Brooks <mlbrooks@…>, 30 hours ago

assure consistent section title capitals

  • Property mode set to 100644
File size: 77.1 KB
Line 
1\chapter{Background}
2
3Since this work builds on C, it is necessary to explain the C mechanisms and their shortcomings for array, linked list, and string.
4
5
6\section{Ill-Typed Expressions}
7
8C reports many ill-typed expressions as warnings.
9For example, these attempts to assign @y@ to @x@ and vice-versa are ill-typed.
10\lstinput{12-15}{bkgd-c-tyerr.c}
11with warnings:
12\begin{cfa}
13warning: assignment to 'float *' from incompatible pointer type 'void (*)(void)'
14warning: assignment to 'void (*)(void)' from incompatible pointer type 'float *'
15\end{cfa}
16Similarly,
17\lstinput{17-19}{bkgd-c-tyerr.c}
18with warning:
19\begin{cfa}
20warning: passing argument 1 of 'f' from incompatible pointer type
21note: expected 'void (*)(void)' but argument is of type 'float *'
22\end{cfa}
23Clearly, @gcc@ understands these ill-typed cases, and yet allows the program to compile, which seems inappropriate.
24Compiling with flag @-Werror@, which turns warnings into errors, is often too pervasive, because some warnings are just warnings, \eg an unused variable.
25In the following discussion, \emph{ill-typed} means giving a nonzero @gcc@ exit condition with a message that discusses typing.
26Note, \CFA's type-system rejects all these ill-typed cases as type mismatch errors.
27
28% That @f@'s attempt to call @g@ fails is not due to 3.14 being a particularly unlucky choice of value to put in the variable @pi@.
29% Rather, it is because obtaining a program that includes this essential fragment, yet exhibits a behaviour other than "doomed to crash," is a matter for an obfuscated coding competition.
30
31% A "tractable syntactic method for proving the absence of certain program behaviours by classifying phrases according to the kinds of values they compute"*1 rejected the program.
32% The behaviour (whose absence is unprovable) is neither minor nor unlikely.
33% The rejection shows that the program is ill-typed.
34%
35% Yet, the rejection presents as a GCC warning.
36% *1 TAPL-pg1 definition of a type system
37
38
39\section{Reading Declarations}
40
41A significant area of confusion is reading C declarations, which results from interesting design choices.
42\begin{itemize}[leftmargin=*]
43\item
44In C, it is possible to have a value and a pointer to it.
45\begin{cfa}
46int i = 3, * pi = &i;
47\end{cfa}
48Extending this idea, it should be possible to have an array of values and pointer to it.
49\begin{cfa}
50int a[5] = { 1, 2, 3, 4, 5 }, * pa[5] = &a;
51\end{cfa}
52However, the declaration of @pa@ is incorrect because dimension has higher priority than pointer, so the declaration means an array of 5 pointers to integers.
53The declarations for the two interpretations of @* [5]@ are:
54\begin{cquote}
55\begin{tabular}[t]{@{}ll@{\hspace{15pt}}|@{\hspace{15pt}}ll@{}}
56\begin{cfa}
57int (* pa)[5]
58\end{cfa}
59&
60\raisebox{-0.4\totalheight}{\includegraphics{PtrToArray.pdf}}
61&
62\begin{cfa}
63int * ap[5]
64\end{cfa}
65&
66\raisebox{-0.75\totalheight}{\includegraphics{ArrayOfPtr.pdf}}
67\end{tabular}
68\end{cquote}
69If the priorities of dimension and pointer were reversed, the declarations become more intuitive: @int * pa[5]@ and @int * (ap[5])@.
70\item
71This priority inversion extends into an expression between dereference and subscript, so usage syntax mimics declaration.
72\begin{cquote}
73\setlength{\tabcolsep}{20pt}
74\begin{tabular}{@{}ll@{}}
75\begin{cfa}
76int (* pa)[5]
77 (*pa)[i] += 1;
78\end{cfa}
79&
80\begin{cfa}
81int * ap[5]
82 *ap[i] += 1;
83\end{cfa}
84\end{tabular}
85\end{cquote}
86(\VRef{s:ArraysDecay} shows pointer decay allows the first form to be written @pa[i] += 1@, which is further syntax confusion.)
87Again, if the priorities were reversed, the expressions become more intuitive: @*pa[i] += 1@ and @*(ap[i]) += 1@.
88Note, a similar priority inversion exists between deference '@*@' and field selection '@.@' (period), so @*ps.f@ means @*(ps.f)@;
89this anomaly is \emph{fixed} with operator @->@, which performs the two operations in the more intuitive order: @sp->f@ $\Rightarrow$ @(*sp).f@.
90\end{itemize}
91While attempting to make the declaration and expression contexts consistent is a laudable goal, it has not worked out in practice, Dennis Richie's contrary position on the issue conceded:
92\begin{quote}
93In spite of its difficulties, I believe that the C's approach to declarations remains plausible, and am comfortable with it; it is a useful unifying principle.~\cite[p.~12]{Ritchie93}
94\end{quote}
95As it stands, typed must be read from their declared identifier outward, following the ``clock-wise spiral rule''~\cite{Anderson94}.
96Unfortunately, \CFA cannot correct these operator priority inversions without breaking C compatibility.
97
98The alternative solution is for \CFA to provide its own, more intuitive declaration syntax for types, variables and routines.
99The new declarations place type operators to the left of the base type, while C declarations place them (to the right of the base type and) spiraling around the declared object name.
100The type operators have the same semantics in \CFA as in C, so there is nothing to learn.
101Then, a \CFA declaration is read left to right, where a function return-type is enclosed in brackets @[@\,@]@.
102\begin{cquote}
103\begin{tabular}{@{}l@{\hspace{3em}}ll@{}}
104\multicolumn{1}{c@{\hspace{3em}}}{\textbf{C}} & \multicolumn{1}{c}{\textbf{\CFA}} & \multicolumn{1}{c}{\textbf{read left to right}} \\
105\begin{cfa}
106int @*@ x1 @[5]@;
107int @(*@x2@)[5]@;
108int @(*@f( int p )@)[5]@;
109\end{cfa}
110&
111\begin{cfa}
112@[5] *@ int x1;
113@* [5]@ int x2;
114@[ * [5] int ]@ f( int p );
115\end{cfa}
116&
117\begin{cfa}
118// array of 5 pointers to int
119// pointer to array of 5 int
120// function returning pointer to array of 5 ints
121\end{cfa}
122\\
123& &
124\LstCommentStyle{//\ \ \ and taking an int argument}
125\end{tabular}
126\end{cquote}
127As declaration size increases, it becomes corresponding difficult to read and understand the C form, whereas reading and understanding a \CFA declaration has linear complexity.
128Note, writing declarations left to right is common in other programming languages, where the function return-type is often placed after the parameter declarations, \eg \CC \lstinline[language=C++]{auto f( int ) -> int}.
129However, putting the return type at the end deviates from where the return value logically appears in an expression, @x = f(...)@ versus @f(...) = x@.
130Interestingly, programmers normally speak a declaration from left to right, regardless of how it is written.
131(It is unclear if Hebrew or Arabic speakers, say declarations right to left.)
132
133Specifically, \VRef[Table]{bkgd:ar:usr:avp} introduces the many layers of the C and \CFA array story, where the \CFA story is discussed in \VRef[Chapter]{c:Array}.
134The \CFA-thesis column shows the new array declaration form, which is my contribution to safety and ergonomics.
135The table shows there are multiple yet equivalent forms for the array types under discussion, and subsequent discussion shows interactions with orthogonal (but easily confused) language features.
136Each row of the table shows alternate syntactic forms.
137The simplest occurrences of types distinguished in the preceding discussion are marked with $\triangleright$.
138Removing the declared variable @x@, gives the type used for variable, structure field, cast, or error messages.
139Unfortunately, parameter declarations have more syntactic forms and rules \see{Section~\ref{s:ArraysDecay}~\VPageref{p:ParameterDeclarations}}.
140
141\begin{table}
142\centering
143\caption{Syntactic Reference for Array vs Pointer. Includes interaction with \lstinline{const}ness.}
144\label{bkgd:ar:usr:avp}
145\setlength{\tabcolsep}{12pt}
146\begin{tabular}{@{}ll|l|l|l@{}}
147 & Description & \multicolumn{1}{c|}{C} & \multicolumn{1}{c|}{\CFA} & \multicolumn{1}{c}{\CFA-thesis} \\
148 \hline
149$\triangleright$ & value & @T x;@ & @T x;@ & \\
150 \hline
151 & immutable value & @const T x;@ & @const T x;@ & \\
152 & & @T const x;@ & @T const x;@ & \\
153 \hline \hline
154$\triangleright$ & pointer to value & @T * x;@ & @* T x;@ & \\
155 \hline
156 & immutable ptr. to val. & @T * const x;@ & @const * T x;@ & \\
157 \hline
158 & ptr. to immutable val. & @const T * x;@ & @* const T x;@ & \\
159 & & @T const * x;@ & @* T const x;@ & \\
160 \hline \hline
161$\triangleright$ & array of value & @T x[10];@ & @[10] T x@ & @array(T, 10) x@ \\
162 \hline
163 & ar.\ of immutable val. & @const T x[10];@ & @[10] const T x@ & @const array(T, 10) x@ \\
164 & & @T const x[10];@ & @[10] T const x@ & @array(T, 10) const x@ \\
165 \hline
166 & ar.\ of ptr.\ to value & @T * x[10];@ & @[10] * T x@ & @array(T *, 10) x@ \\
167 & & & & @array(* T, 10) x@ \\
168 \hline
169 & ar.\ of imm. ptr.\ to val. & @T * const x[10];@ & @[10] const * T x@ & @array(* const T, 10) x@ \\
170 & & & & @array(const * T, 10) x@ \\
171 \hline
172 & ar.\ of ptr.\ to imm. val. & @const T * x[10];@ & @[10] * const T x@ & @array(const T *, 10) x@ \\
173 & & @T const * x[10];@ & @[10] * T const x@ & @array(* const T, 10) x@ \\
174 \hline \hline
175$\triangleright$ & ptr.\ to ar.\ of value & @T (*x)[10];@ & @* [10] T x@ & @* array(T, 10) x@ \\
176 \hline
177 & imm. ptr.\ to ar.\ of val. & @T (* const x)[10];@ & @const * [10] T x@ & @const * array(T, 10) x@ \\
178 \hline
179 & ptr.\ to ar.\ of imm. val. & @const T (*x)[10];@ & @* [10] const T x@ & @* const array(T, 10) x@ \\
180 & & @T const (*x)[10];@ & @* [10] T const x@ & @* array(T, 10) const x@ \\
181 \hline
182 & ptr.\ to ar.\ of ptr.\ to val. & @T *(*x)[10];@ & @* [10] * T x@ & @* array(T *, 10) x@ \\
183 & & & & @* array(* T, 10) x@ \\
184 \hline
185\end{tabular}
186\end{table}
187
188
189\section{Array}
190\label{s:Array}
191
192At the start, the C language designers made a significant design mistake with respect to arrays.
193\begin{quote}
194In C, there is a strong relationship between pointers and arrays, strong enough that pointers and arrays really should be treated simultaneously.
195Any operation which can be achieved by array subscripting can also be done with pointers.~\cite[p.~93]{C:old}
196\end{quote}
197Accessing any storage requires pointer arithmetic, even if it is just base-displacement addressing in an instruction.
198The conjoining of pointers and arrays could also be applied to structures, where a pointer references a structure field like an array element.
199Finally, while subscripting involves pointer arithmetic (as does a field reference @x.y.z@), the computation is complex for multi-dimensional arrays and requires array descriptors to know stride lengths along dimensions.
200Many C errors result from manually performing pointer arithmetic instead of using language subscripting so the compiler performs the arithmetic.
201
202Some modern C textbooks and web sites erroneously suggest manual pointer arithmetic is faster than subscripting.
203When compiler technology was young, this statement might have been true.
204However, a sound and efficient C program coupled with a modern C compiler does not require explicit pointer arithmetic.
205For example, the @gcc@ compiler at @-O3@ generates identical code for the following two summation loops.
206\begin{cquote}
207\vspace*{-10pt}
208\begin{cfa}
209int a[1000], sum;
210\end{cfa}
211\setlength{\tabcolsep}{20pt}
212\begin{tabular}{@{}ll@{}}
213\begin{cfa}
214for ( int i = 0; i < 1000; i += 1 ) {
215 sum += a[i];
216}
217\end{cfa}
218&
219\begin{cfa}
220for ( int * ip = a ; ip < &a[1000]; ip += 1 ) {
221 sum += *ip;
222}
223\end{cfa}
224\end{tabular}
225\end{cquote}
226I believe it is possible to refute any code examples purporting to show pointer arithmetic is faster than subscripting.
227This believe stems from the performance work I did on \CFA arrays, where it is possible to generate equivalent \CFA subscripting and performance to C subscripting.
228
229Unfortunately, C semantics want a programmer to \emph{believe} an array variable is a \emph{pointer to its first element}.
230This desire becomes apparent by a detailed inspection of an array declaration.
231\lstinput{34-34}{bkgd-carray-arrty.c}
232The inspection begins by using @sizeof@ to provide program semantics for the intuition of an expression's type.
233An architecture with 64-bit pointer size is used, to remove irrelevant details.
234\lstinput{35-36}{bkgd-carray-arrty.c}
235Now consider the @sizeof@ expressions derived from @ar@, modified by adding pointer-to and first-element (and including unnecessary parentheses to avoid any confusion about precedence).
236\lstinput{37-40}{bkgd-carray-arrty.c}
237Given that arrays are contiguous and the size of @float@ is 4, then the size of @ar@ with 10 floats being 40 bytes is common reasoning for C programmers.
238Equally, C programmers know the size of a pointer to the first array element is 8.
239% Now, set aside for a moment the claim that this first assertion is giving information about a type.
240Clearly, an array and a pointer to its first element are different.
241
242In fact, the idea that there is such a thing as a pointer to an array may be surprising.
243It it is not the same thing as a pointer to the first element.
244\lstinput{42-45}{bkgd-carray-arrty.c}
245The first assignment generates:
246\begin{cfa}
247warning: assignment to `float (*)[10]' from incompatible pointer type `float *'
248\end{cfa}
249and the second assignment generates the opposite.
250
251The inspection now refutes any suggestion that @sizeof@ is informing about allocation rather than type information.
252Note, @sizeof@ has two forms, one operating on an expression and the other on a type.
253Using the type form yields the same results as the prior expression form.
254\lstinput{46-49}{bkgd-carray-arrty.c}
255The results are also the same when there is no allocation at all, \eg a parameter declaration.
256This time, starting from a pointer-to-array type:
257\lstinput{51-57}{bkgd-carray-arrty.c}
258Hence, in all cases, @sizeof@ is reporting on type information.
259
260Therefore, thinking of an array as a pointer to its first element is too simplistic an analogue and it is not backed up by the type system.
261This misguided analogue works for a single-dimension array but there is no advantage other than possibly teaching beginner programmers about basic runtime array-access.
262
263Continuing, there is a short form for declaring array variables using length information provided implicitly by an initializer.
264\lstinput{59-62}{bkgd-carray-arrty.c}
265The compiler counts the number of initializer elements and uses this value as the first dimension.
266Unfortunately, the implicit element counting does not extend to dimensions beyond the first.
267\lstinput{64-67}{bkgd-carray-arrty.c}
268
269My observation is recognizing:
270\begin{itemize}[leftmargin=*,itemsep=0pt]
271 \item There is value in using a type that knows its size.
272 \item The type pointer to the (first) element does not.
273 \item C \emph{has} a type that knows the whole picture: array, \eg @T[10]@.
274 \item This type has all the usual derived forms, which also know the whole picture.
275 A noteworthy example is pointer to array, \eg @T (*)[10]@.
276\end{itemize}
277
278
279\subsection{Arrays Decay and Pointers Diffract}
280\label{s:ArraysDecay}
281
282The last section established the difference among these four types:
283\lstinput{3-6}{bkgd-carray-decay.c}
284But the expression used for obtaining the pointer to the first element is pedantic.
285The root of all C programmer experience with arrays is the shortcut
286\lstinput{8-8}{bkgd-carray-decay.c}
287which reproduces @pa0@, in type and value:
288\lstinput{9-9}{bkgd-carray-decay.c}
289The validity of this initialization is unsettling, in the context of the facts established in \VRef{s:Array}.
290Notably, it initializes name @pa0x@ from expression @ar@, when they are not of the same type:
291\lstinput{10-10}{bkgd-carray-decay.c}
292
293So, C provides an implicit conversion from @float[10]@ to @float *@.
294\begin{quote}
295Except when it is the operand of the @sizeof@ operator, or the unary @&@ operator, or is a string literal used to
296initialize an array, an expression that has type ``array of \emph{type}'' is converted to an expression with type
297``pointer to \emph{type}'' that points to the initial element of the array object~\cite[\S~6.3.2.1.3]{C11}
298\end{quote}
299This phenomenon is the famous (infamous) \newterm{pointer decay}, which is a decay of an array-typed expression into a pointer-typed one.
300It is worthy to note that, in the expression @ar[i]@, the subexpression @ar@, is not one of the standard's exceptions; @ar@ is converted to a pointer, before subscripting occurs.
301Thus, \emph{all} subscripting happens on pointers, never arrays.
302
303After this pointer decay (when needed), subscripting proceeds.
304In the next step, \cite[\S~6.5.2.1.2]{C11} explains that @ar[i]@ is treated as if it is @(*(ar+i))@.
305\cite[\S~6.5.6.8]{C11} explains that the addition, of a pointer with an integer type, is defined only when the pointer refers to an element that is in an array.
306The addition gives the address @i@ elements away from the element (@ar@, or @&ar[0]@).
307This address is valid if @ar@ is big enough and @i@ is small enough.
308Finally, \cite[\S~6.5.3.2.4]{C11} explains that the @*@ operator's result is the referenced element.
309Taken together, these rules illustrate that @ar[i]@ and @i[a]@ mean the same thing, as addition is commutative.
310
311Subscripting a pointer when the target is standard-inappropriate is still, practically, well-defined.
312While the standard affords a C compiler freedom about the meaning of an out-of-bound access, or of subscripting a pointer that does not refer to an array element at all,
313the fact that C is famously both generally high-performance, and specifically not bound-checked, leads to an expectation that the runtime handling is uniform across legal and illegal accesses.
314Moreover, consider the common pattern of subscripting on a @malloc@ result:
315\begin{cfa}
316float * fs = malloc( 10 * sizeof(float) );
317fs[5] = 3.14;
318\end{cfa}
319The @malloc@ behaviour is specified as returning a pointer to ``space for an object whose size is [the size requested]'' (\cite[\S~7.22.3.4.2]{C11}).
320But \emph{nothing} more is said about this pointer, much less that its referent might \emph{be} an array, \ie that subscripting might be allowed at all.
321
322Under this assumption, a pointer, @p@, being subscripted (or added to, then dereferenced) by any value (positive, zero, or negative), gives a view of the program's entire address space, centred around @p@'s address, divided into adjacent @sizeof(*p)@ chunks, each potentially (re)interpreted as @typeof(*p)@.
323I call this phenomenon \emph{array diffraction}, which is a diffraction of a single-element pointer into the assumption that its target is conceptually in the middle of an array, whose size is unlimited in both directions, \eg @(&ar[5])[-200]@ or @(&ar[5])[200]@.
324
325No pointer is exempt from array diffraction.
326No array shows its elements without pointer decay.
327
328\label{p:ParameterDeclarations}
329A further pointer--array confusion, closely related to decay, occurs in parameter declarations.
330\cite[\S~6.7.6.3.7]{C11} explains that when an array type is written for a parameter,
331the parameter's type changes into (what is practically) the pointer-decayed type.
332But pointer decay applies to an expression, while this transformation applies to a declaration.
333The respective handling of the following two parameter spellings shows that the array and pointer versions are identical.
334\lstinput{12-16}{bkgd-carray-decay.c}
335As the @sizeof(x)@ meaning changed, compared with when run on a similarly-spelled local variable declaration,
336@gcc@ also gives this code the warning for the first assertion:
337\begin{cfa}
338warning: 'sizeof' on array function parameter 'x' will return size of 'float *'
339\end{cfa}
340The caller of such a function is left with the reality that a pointer parameter is a pointer, no matter how it is spelled:
341\lstinput{18-21}{bkgd-carray-decay.c}
342This fragment gives the warning for the first argument, in the second call.
343\begin{cfa}
344warning: 'f' accessing 40 bytes in a region of size 4
345\end{cfa}
346
347The shortened parameter syntax @T x[]@ is a further way to spell \emph{pointer}.
348Note the opposite meaning of this spelling now, compared with its use in local variable declarations.
349This point of confusion is illustrated in:
350\lstinput{23-30}{bkgd-carray-decay.c}
351Note, \CC gives a warning for the initialization of @cp@.
352\begin{cfa}
353warning: ISO C++ forbids converting a string constant to 'char*'
354\end{cfa}
355and C gives a warning at the call of @edit@, if @const@ is added to the declaration of @cp@.
356\begin{cfa}
357warning: passing argument 1 of 'edit' discards 'const' qualifier from pointer target type
358\end{cfa}
359The basic two meanings, with a syntactic difference helping to distinguish, are illustrated in the declarations of @ca@ \vs @cp@, whose subsequent @edit@ calls behave differently.
360The syntax-caused confusion is in the comparison of the first and last lines, both of which use a literal to initialize an object declared with spelling @T x[ ]@.
361But these initialized declarations get opposite meanings, depending on whether the object is a local variable or a parameter!
362
363In summary, when a function is written with an array-typed parameter,
364\begin{itemize}[leftmargin=*]
365 \item an appearance of passing an array by value is always an incorrect understanding,
366 \item a dimension value, if any is present, is ignored,
367 \item pointer decay is forced at the call site and the callee sees the parameter having the decayed type.
368\end{itemize}
369
370Pointer decay does not affect pointer-to-array types, because these are already pointers, not arrays.
371As a result, a function with a pointer-to-array parameter sees the parameter exactly as the caller does:
372\par\noindent
373\begin{tabular}{@{\hspace*{-0.75\parindentlnth}}l@{}l@{}}
374\lstinput{32-36}{bkgd-carray-decay.c}
375&
376\lstinput{38-42}{bkgd-carray-decay.c}
377\end{tabular}
378\par\noindent
379\VRef[Table]{bkgd:ar:usr:decay-parm} gives the reference for the decay phenomenon seen in parameter declarations.
380
381\begin{table}
382\caption{Syntactic Reference for Decay during Parameter-Passing.
383Includes interaction with \lstinline{const}ness, where \emph{immutable} refers to a restriction on the callee's ability.}
384\label{bkgd:ar:usr:decay-parm}
385\centering
386\setlength{\tabcolsep}{10pt}
387\begin{tabular}{@{}llllll@{}}
388 & Description & Type & Parameter Declaration & \CFA \\
389 \hline
390 & & & @T * x,@ & @* T x,@ \\
391$\triangleright$ & pointer to value & @T *@ & @T x[10],@ & @[10] T x,@ \\
392 & & & @T x[],@ & @[] T x,@ \\
393 \hline
394 & & & @T * const x,@ & @const * T x@ \\
395 & immutable ptr.\ to val. & @T * const@ & @T x[const 10],@ & @[const 10] T x,@ \\
396 & & & @T x[const],@ & @[const] T x,@\\
397 \hline
398 & & & @const T * x,@ & @ * const T x,@ \\
399 & & & @T const * x,@ & @ * T const x,@ \\
400 & ptr.\ to immutable val. & @const T *@ & @const T x[10],@ & @[10] const T x,@ \\
401 & & @T const *@ & @T const x[10],@ & @[10] T const x,@ \\
402 & & & @const T x[],@ & @[] const T x,@ \\
403 & & & @T const x[],@ & @[] T const x,@ \\
404 \hline \hline
405 & & & @T (*x)[10],@ & @* [10] T x,@ \\
406$\triangleright$ & ptr.\ to ar.\ of val. & @T(*)[10]@ & @T x[3][10],@ & @[3][10] T x,@ \\
407 & & & @T x[][10],@ & @[][10] T x,@ \\
408 \hline
409 & & & @T ** x,@ & @** T x,@ \\
410 & ptr.\ to ptr.\ to val. & @T **@ & @T * x[10],@ & @[10] * T x,@ \\
411 & & & @T * x[],@ & @[] * T x,@ \\
412 \hline
413 & ptr.\ to ptr.\ to imm.\ val. & @const char **@ & @const char * argv[],@ & @[] * const char argv,@ \\
414 & & & \emph{others elided} & \emph{others elided} \\
415 \hline
416\end{tabular}
417\end{table}
418
419
420\subsection{Variable-Length Arrays}
421
422As of C99, the C standard supports a \newterm{variable length array} (VLA)~\cite[\S~6.7.5.2.5]{C99}, providing a dynamic-fixed array feature \see{\VRef{s:ArrayIntro}}.
423Note, the \CC standard does not support VLAs, but @g++@ and @clang@ provide a limited form of them.
424A VLA is used when the desired number of array elements is \emph{unknown} at compile time.
425\begin{cfa}
426size_t n;
427scanf( "%d", &n );
428double ar[n];
429\end{cfa}
430The array dimension is read from outside the program and used to create an array of size @n@ on the stack.
431The VLA is implemented by the @alloca@ routine, which bumps the stack pointer.
432Unfortunately, there is significant misinformation about VLAs, \eg the stack size is limited (small), or VLAs cause stack failures or are inefficient.
433VLAs exist as far back as Algol W~\cite[\S~5.2]{AlgolW} and are a sound and efficient data type.
434Most shells limit the maximum stack size to guard against infinite recursion, but the stack size can be set to unlimited, just like the heap.
435For types with a dynamic-fixed stack, \eg coroutines or user-level threads, large VLAs can overflow the stack without appropriately sizing the stack, so heap allocation is used when the array size is unbounded.
436
437
438\subsection{Multidimensional Arrays}
439\label{toc:mdimpl}
440
441% TODO: introduce multidimensional array feature and approaches
442
443When working with arrays, \eg linear algebra, array dimensions are referred to as \emph{rows} and \emph{columns} for a matrix, adding \emph{planes} for a cube.
444(There is little terminology for higher dimensional arrays.)
445For example, an acrostic poem\footnote{A written presentation of words where the first, last or other letters in a line spell out a word or phrase in a vertical column.}
446can be treated as a grid of characters, where the rows are the text and the columns are the embedded keyword(s).
447Within a poem, there is the concept of a \newterm{slice}, \eg a row is a slice for the poem text, a column is a slice for a keyword.
448In general, the dimensioning and subscripting for multidimensional arrays has two syntactic forms: @m[r,c]@ or @m[r][c]@.
449
450Commonly, an array, matrix or cube is visualized (especially in mathematics) as a contiguous row, rectangle or block.
451This conceptualization is reinforced by subscript ordering, \eg $m_{r,c}$ for a matrix and $c_{p,r,c}$ for a cube.
452Few programming languages differ from the mathematical subscript ordering.
453However, computer memory is flat, and hence, array forms are structured in memory as appropriate for the runtime system.
454The closest representation to the conceptual visualization is for an array object to be contiguous, and the language structures this memory using pointer arithmetic to access the values using various subscripts.
455This approach still has degrees of layout freedom, such as row or column major order, \ie juxtaposed rows or columns in memory, even when the subscript order remains fixed.
456For example, programming languages like MATLAB, Fortran, Julia and R store matrices in column-major order since they are commonly used for processing column-vectors in tabular data sets but retain row-major subscripting to match with mathematical notation.
457In general, storage layout is hidden by subscripting, and only appears when passing arrays among different programming languages or accessing specific hardware.
458
459\VRef[Figure]{f:FixedVariable} shows two C90 approaches for manipulating a contiguous matrix.
460Note, C90 does not support VLAs.
461The fixed-dimension approach (left) uses the type system;
462however, it requires all dimensions except the first to be specified at compile time, \eg @m[][6]@, allowing all subscripting stride calculations to be generated with constants.
463Hence, every matrix passed to @fp1@ must have exactly 6 columns but the row size can vary.
464The variable-dimension approach (right) ignores (violates) the type system, \ie the parameter type has no suggestion of mutidimensionality and some acrobatics are required for a w\footnote{
465 One may be tempted to phrase a call as \lstinline{fp2( 4, 4, vm1 )}, but this call is ill-typed. Argument \lstinline{vm1} could match parameter declarations \lstinline{int m[][4]} or \lstinline{int (*m)[4]}. But only the argument \lstinline{&vm1[0][0]}, or its equivalent, but confusing, \lstinline{vm1[0]}, relate \lstinline{vm1} to parameter type \lstinline{int*}.
466}, and subscripting is performed manually using pointer arithmetic in the macro @sub@.
467
468\begin{figure}
469\begin{tabular}{@{}l@{\hspace{40pt}}l@{}}
470\multicolumn{1}{c}{\textbf{Fixed Dimension}} & \multicolumn{1}{c}{\textbf{Variable Dimension}} \\
471\begin{cfa}
472
473void fp1( int rows, int m[][@6@] ) {
474 ... printf( "%d ", @m[r][c]@ ); ...
475}
476int fm1[4][@6@], fm2[6][@6@]; // no VLA, same
477// initialize matrixes
478fp1( 4, fm1 ); // implicit 6 columns
479fp1( 6, fm2 );
480\end{cfa}
481&
482\begin{cfa}
483#define sub( m, r, c ) *(m + r * sizeof( m[0] ) + c)
484void fp2( int rows, int cols, int *m ) {
485 ... printf( "%d ", @sub( m, r, c )@ ); ...
486}
487int vm1[4][@4@], vm2[6][@8@]; // no VLA, different
488// initialize matrixes
489fp2( 4, 4, @&vm1[0][0]@ );
490fp2( 6, 8, &vm2[0][0] );
491\end{cfa}
492\end{tabular}
493\caption{Pre-VLA contiguous matrix styles, fixed \vs variable}
494\label{f:FixedVariable}
495\end{figure}
496
497Many languages allow multidimensional arrays-of-arrays, \eg Pascal and \CC.
498\begin{cquote}
499\setlength{\tabcolsep}{15pt}
500\begin{tabular}{@{}ll@{}}
501\begin{pascal}
502(* Pascal *)
503var m : array[0..4, 0..4] of Integer; (* matrix *)
504type AT = array[0..4] of Integer; (* array type *)
505type MT = array[0..4] of AT; (* array of array type *)
506var aa : MT; (* array of array variable *)
507m@[1][2]@ := 1; aa@[1][2]@ := 1 (* same subscripting *)
508\end{pascal}
509&
510\begin{c++}
511// C++
512int m[5][5];
513
514typedef vector< vector<int> > MT;
515MT vv( 5, vector<int>( 5 ) );
516m@[1][2]@ = 1; vv@[1][2]@ = 1;
517\end{c++}
518\end{tabular}
519\end{cquote}
520The language decides if the matrix and array-of-array are laid out the same or differently.
521For example, an array-of-array may be an array of row pointers to arrays of columns, so the rows may not be contiguous in memory nor even the same length (\eg triangular matrix).
522Regardless, there is usually a uniform subscripting syntax masking the memory layout, even though a language could differentiate between the two forms using subscript syntax, \eg @m[1,2]@ \vs @aa[1][2]@.
523Nevertheless, controlling memory layout can make a difference in what operations are allowed and in performance (\eg caching/NUMA effects).
524C also allows non-contiguous arrays-of-arrays:
525\begin{cfa}
526int m[5][5]; $\C{// contiguous}$
527int * aa[5]; $\C{// non-contiguous}$
528\end{cfa}
529both with different memory layout using the same subscripting and different degrees of issues.
530
531The focus of this work is on the contiguous multidimensional arrays in C.
532The reason is that programmers are often forced to use the more complex array-of-array form when a contiguous array would be simpler, faster, and safer.
533Nevertheless, the C array-of-array form is still important for special circumstances.
534
535\VRef[Figure]{f:ContiguousNon-contiguous} shows a powerful extension made in C99, for manipulating contiguous \vs non-contiguous arrays.\footnote{C90 also supported non-contiguous arrays. Though GNU-flavoured C++ eventually got VLAs, it never got this enhancement for managing a multidimensionl VLA parameter. }
536For contiguous-array arguments (including VLA), C99 conjoins one or more of the parameters as a downstream dimension(s), \eg @cols@, implicitly using this parameter to compute the row stride of @m@.
537Hence, if the declaration of @fc@ is changed to:
538\begin{cfa}
539void fc( int rows, int cols, int m[@rows@][@cols@] ) ...
540\end{cfa}
541there is now sufficient information to support array copying and subscript checking to prevent buffer-overflow problems, \emph{but neither feature is provided}.
542While this contiguous-array capability is a step forward, it is still the programmer's responsibility to manually manage the number of dimensions and their sizes, both at the function definition and call sites.
543That is, the array does not automatically carry its structure and sizes for use in computing subscripts.
544While the non-contiguous style in @faa@ looks very similar to @fc@, the compiler only understands the unknown-sized array of row pointers, and it relies on the programmer to traverse the columns in a row with a correctly bounded loop index.
545Specifically, there is no requirement that the rows are the same length, like for a poem with different-length lines.
546
547\begin{figure}
548\begin{tabular}{@{}ll@{}}
549\multicolumn{1}{c}{\textbf{Contiguous}} & \multicolumn{1}{c}{\textbf{ Non-contiguous}} \\
550\begin{cfa}
551void fc( int rows, @int cols@, int m[ /* rows */ ][@cols@] ) {
552 for ( size_t r = 0; r < rows; r += 1 ) {
553 for ( size_t c = 0; c < cols; c += 1 )
554 ... @m[r][c]@ ...
555 // each r-step: cols * sizeof(int)
556}
557int m@[5][5]@;
558for ( int r = 0; r < 5; r += 1 ) {
559
560 for ( int c = 0; c < 5; c += 1 )
561 m[r][c] = r + c;
562}
563fc( 5, 5, m );
564\end{cfa}
565&
566\begin{cfa}
567void faa( int rows, int cols, int * m[ @/* cols */@ ] ) {
568 for ( size_t r = 0; r < rows; r += 1 ) {
569 for ( size_t c = 0; c < cols; c += 1 )
570 ... @m[r][c]@ ...
571 // each r-step: 1 * sizeof(int*)
572}
573int @* aa[5]@; // row pointers
574for ( int r = 0; r < 5; r += 1 ) {
575 @aa[r] = malloc( 5 * sizeof(int) );@ // create rows
576 for ( int c = 0; c < 5; c += 1 )
577 aa[r][c] = r + c;
578}
579faa( 5, 5, aa );
580\end{cfa}
581\end{tabular}
582\caption{C99 matrix styles, contiguous \vs non-contiguous }
583\label{f:ContiguousNon-contiguous}
584\end{figure}
585
586
587\subsection{Multi-Dimensional Arrays Decay and Pointers Diffract}
588
589As for single-dimension, multi-dimensional arrays have similar issues \see{\VRef{s:Array}}.
590Again, the inspection begins by using @sizeof@ to provide program semantics for the intuition of an expression's type.
591\lstinput{16-18}{bkgd-carray-mdim.c}
592There are now three means of deriving expressions from @mx@: \emph{itself}, \emph{first element}, and \emph{first grand-element} (meaning, first element of first element).
593\lstinput{20-26}{bkgd-carray-mdim.c}
594Given that arrays are contiguous and the size of @float@ is 4, then the size of @mx@ with 3 $\times$ 10 floats is 120 bytes, the size of its first element (row) is 40 bytes, and the size of the first element of the first row is 4.
595Again, an array and a pointer to anything it contains are different.
596\lstinput{28-36}{bkgd-carray-mdim.c}
597As well, there is pointer decay from each of the matrix axes to pointers, all having the same address.
598Finally, subscripting is allowed on a @malloc@ result, where the referent may or may not allow subscripting or have the right number of subscripts.
599
600
601\subsection{Array Parameter Declaration}
602
603Passing an array as an argument to a function is necessary.
604Assume a parameter is an array where the function intends to subscript it.
605This section asserts that a more satisfactory/formal characterization does not exist in C, then surveys the ways that C API authors communicate that @p@ has zero or more dimensions, and finally calls out the minority cases where the C type system is using or verifying such claims.
606
607A C parameter declaration looks different from the caller's and callee's perspectives.
608Both perspectives consist of the text read by a programmer and the semantics enforced by the type system.
609The caller's perspective is available from a function declaration, which allows definition-before-use and separate compilation, but can also be read from (the non-body part of) a function definition.
610The callee's perspective is what is available inside the function.
611\begin{cfa}
612int foo( int, float, char ); $\C{// declaration, parameter names optional}$
613int bar( int i, float f, char c ) { $\C{// definition, parameter names mandatory}$
614 // callee's perspective of foo and bar
615}
616// caller's perspectives of foo and bar
617\end{cfa}
618From the caller's perspective, the parameter names are semantically optional, but provide useful comments.
619From the callee's perspective, parameter names are semantically essential.
620Array parameters introduce a further, subtle, semantic difference and considerable freedom to comment.
621
622At the semantic level, there is no such thing as an array parameter, except for one case, @T [static 5]@, discussed shortly.
623Instead, there are only pointer parameters.
624This fact probably shares considerable responsibility for the common sense of \emph{an array is just a pointer}, which has been refuted in non-parameter contexts.
625This fact holds in both the caller's and callee's perspectives.
626However, a parameter's type can include ``array of'', \eg the type ``pointer to array of 5 ints'' (@T (*)[5]@), which is a pointer type.
627This type is fully meaningful in the sense that its description does not contain any information that the type system ignores, and the type appears the same in the caller's and callee's perspectives.
628In fact, the outermost type constructor (syntactically first dimension) is really the one that determines the parameter flavour.
629
630C ignores length information given in parameter declarations entirely, when determining a function's type.
631For example, it accepts this pair of declarations
632\begin{cfa}
633 void f( int, float[][42] ); $\C{// array of len-42 arrays}$
634 void f( int n, float[][n] ); $\C{// array of VLAs}$
635\end{cfa}
636without an error about conflicting types for @f@.
637Yet, entirely different stride calculations occur in the function body for each of the two parameter styles.
638
639\begin{figure}
640\setlength{\tabcolsep}{7pt}
641\begin{tabular}{@{}llll@{}}
642\begin{cfa}
643float sum( float a[5] );
644float sum( float m[5][4] );
645float sum( float a[5][] );
646float sum( float a[5]* );
647float sum( float *a[5] );
648\end{cfa}
649&
650\begin{cfa}
651float sum( float a[] );
652float sum( float m[][4] );
653float sum( float a[][] );
654float sum( float a[]* );
655float sum( float *a[] );
656\end{cfa}
657&
658\begin{cfa}
659float sum( float *a );
660float sum( float (*m)[4] );
661float sum( float (*a)[] );
662float sum( float (*a)* );
663float sum( float **a );
664\end{cfa}
665&
666\begin{cfa}
667// array of float
668// matrix of float
669// invalid
670// invalid
671// array of ptr to float
672\end{cfa}
673\end{tabular}
674\caption[Multiple ways to declare an array parameter]{
675Multiple ways to declare an array parameter.
676Across a valid row, every declaration is equivalent.
677Each column gives a declaration style, where the style for that column is read from the first row.
678The second row begins the style for multiple dimensions, with the rows thereafter providing context for the choice of which second-row \lstinline{[]} receives the column-style variation.}
679\label{f:ArParmEquivDecl}
680\end{figure}
681
682Yet, C allows array syntax for the outermost type constructor, from which comes the freedom to comment.
683An array parameter declaration can specify the outermost dimension with a dimension value, @[10]@ (which is ignored), an empty dimension list, @[ ]@, or a pointer, @*@, as seen in \VRef[Figure]{f:ArParmEquivDecl}.
684Examining the rows, the rationale for rejecting the first invalid row (row 3) follows shortly, while the second invalid row (row 4) is nonsense, included to complete the pattern; its syntax hints at what the final row actually achieves.
685Note, in the leftmost style, the typechecker ignores the actual value, even for a dynamic expression.
686\begin{cfa}
687int N;
688void foo( float @a[N] ) ; // N is ignored
689\end{cfa}
690
691
692% To help contextualize the matrix part of this example, the syntaxes @float [5][]@, @float [][]@ and @float (*)[]@ are all rejected, for reasons discussed shortly.
693% So are @float[5]*@, @float[]*@ and @float (*)*@. These latter ones are simply nonsense, though they hint at ``1d array of pointers'', whose equivalent syntax options are, @float *[5]@, @float *[]@, and @float **@.
694
695It is a matter of taste as to whether a programmer should use the left form to get the most out of commenting subscripting and dimension sizes, sticking to the right (avoiding false comfort from suggesting the typechecker is checking more than it is), or compromising in the middle (reducing unchecked information, yet clearly stating, ``I am for subscripting'').
696
697Note that this equivalence of pointer and array declarations is special to parameters.
698It does not apply to local variables, where true array declarations are possible (formal \vs actual declarations).
699\begin{cfa}
700void f( float * a ) {
701 float * b = a; // ok
702 float c[] = a; // reject
703 float d[] = { 1.0, 2.0, 3.0 }; // ok
704 static_assert( sizeof(b) == sizeof(float*) );
705 static_assert( sizeof(d) != sizeof(float*) );
706}
707\end{cfa}
708The consequence of this equivalence is that the type system does not help a caller get it right.
709\begin{cfa}
710float sum( float v[] );
711float arg = 3.14;
712sum( &arg ); $\C{// accepted, v = \&arg}$
713\end{cfa}
714
715Given the syntactic dimension forms @[ ]@ or @[5]@, it raises the question of qualifying the implied array pointer rather than the array element type.
716For example, the qualifiers after the @*@ apply to the array pointer.
717\begin{cfa}
718void foo( const volatile int * @const volatile@ );
719void foo( const volatile int [ ] @const volatile@ ); // does not parse
720\end{cfa}
721C instead puts these pointer qualifiers syntactically into the first dimension.
722\begin{cquote}
723@[@ \textit{type-qualifier-list}$_{opt}$ \textit{assignment-expression}$_{opt}$ @]@
724\end{cquote}
725\begin{cfa}
726void foo( int [@const volatile@] );
727void foo( int [@const volatile@ 5] ); $\C{// 5 is ignored}$
728\end{cfa}
729To make the first dimension size meaningful, C adds this form.
730\begin{cquote}
731@[@ @static@ \textit{type-qualifier-list}$_{opt}$ \textit{assignment-expression} @]@
732\end{cquote}
733\begin{cfa}
734void foo( int [static @3@] );
735int ar[@10@];
736foo( ar ); // check argument dimension 10 > 3
737\end{cfa}
738Here, the @static@ storage qualifier defines the minimum array size for its argument.
739Earlier versions of @gcc@ ($<$ 11) and possibly @clang@ ignore this dimension qualifier, while later versions implement the check, in accordance with the standard.
740
741
742Note that there are now two different meanings for modifiers in the same position. In
743\begin{cfa}
744void foo( int x[static const volatile 3] );
745\end{cfa}
746the @static@ applies to the 3, while the @const volatile@ applies to the @x@.
747
748With multidimensional arrays, on dimensions after the first, a size is required and, is not ignored.
749These sizes (strides) are required for the callee to subscript.
750\begin{cfa}
751void f( float a[][10], float b[][100] ) {
752 static_assert( ((char*)&a([1])) - ((char*)&a([0])) == 10 * sizeof(float) );
753 static_assert( ((char*)&b([1])) - ((char*)&b([0])) == 100 * sizeof(float) );
754}
755\end{cfa}
756Here, the distance between the first and second elements of each array depends on the inner dimension size.
757
758The significance of an inner dimension's length is a fact of the callee's perspective.
759In the caller's perspective, the type system is quite lax.
760Here, there is (some, but) little checking if the argument being passed matches the parameter.
761% void f( float [][10] );
762% int n = 100;
763% float a[100], b[n];
764% f(&a); // reject
765% f(&b); // accept
766\begin{cfa}
767void foo() {
768 void f( float [][10] );
769 int n = 100;
770 float a[100], b[3][12], c[n], d[n][n];
771 f( a );
772 f( b ); $\C{// reject: inner dimension 12 for 10}$
773 f( c );
774 f( @d@ ); $\C{// accept with inner dimension n for 10}$
775 f( &a ); $\C{// reject: inner dimension 100 for 10}$
776 f( &b );
777 f( @&c@ ); $\C{// accept with inner dimension n for 10}$
778 f( &d );
779}
780\end{cfa}
781The cases without comments are rejections, but simply because the array ranks do not match; in the commented cases, the ranks match and the rules are discussed apply.
782The cases @f( b )@ and @f( &a )@ show where some length checking occurs.
783But this checking misses the cases @f( d )@ and @f( &c )@, allowing the calls with mismatched lengths, actually 100 for 10.
784The C checking rule avoids false alarms, at the expense of safety, by allowing any combinations that involve dynamic values.
785Ultimately, an inner dimension's size is a callee's \emph{assumption} because the type system uses declaration details in the callee's perspective that it does not enforce in the caller's perspective.
786
787Finally, to handle higher-dimensional VLAs, C repurposed the @*@ \emph{within} the dimension in a declaration to mean that the callee must make an assumption about the size, but no (unchecked, possibly wrong) information about this assumption is included for the caller-programmer's benefit/\-over-confidence.
788\begin{cquote}
789@[@ \textit{type-qualifier-list$_{opt}$} @* ]@
790\end{cquote}
791\begin{cfa}
792void foo( float [][@*@] ); $\C{// declaration}$
793void foo( float a[][10] ) { ... } $\C{// definition}$
794\end{cfa}
795Repeating it with the full context of a VLA is useful.
796\begin{cfa}
797void foo( int, float [][@*@] ); $\C{// declaration}$
798void foo( int n, float a[][n] ) { ... } $\C{// definition}$
799\end{cfa}
800Omitting the dimension from the declaration is consistent with omitting parameter names, for the declaration case has no name @n@ in scope.
801The omission is also redacting all information not needed to generate correct caller-side code.
802
803
804\subsection{Arrays Could be Values}
805\label{s:ArraysCouldbeValues}
806
807All arrays have a known runtime size at their point of declaration.
808Furthermore, C provides an explicit mechanism to pass an array's dimensions to a function.
809Nevertheless, an array cannot be copied, and hence, not passed by value to a function, even when there is sufficient information to do so.
810
811However, if an array is a structure field (compile-time size), it can be copied and passed by value.
812For example, a C @jmp_buf@ is an array.
813\begin{cfa}
814typedef long int jmp_buf[8];
815\end{cfa}
816An instance of this array can be declared as a structure field.
817\begin{cfa}
818struct Jmp_Buf {
819 @jmp_buf@ jb;
820};
821\end{cfa}
822Now the array can be copied (and passed by value) because structures can be copied.
823\begin{cfa}
824Jmp_Buf jb1, jb2;
825jb1 = jb2; // copy
826void foo( Jmp_Buf );
827foo( jb2 ); // copy
828\end{cfa}
829
830This same argument applies to returning arrays from functions.
831There can be sufficient information to return an array by value but it is unsupported.
832Again, array wrapping allows an array to be returned from a function and copied into a variable.
833
834
835\section{Linked List}
836
837Linked lists are blocks of storage connected using one or more pointers.
838The storage block (node) is logically divided into data (user payload) and links (list pointers), where the links are the only component used by the list structure.
839Since the data unused, list structures are often polymorphic over the data, which is often homogeneous.
840
841The links organize nodes into a particular shape of data structure, \eg chain, tree, hash table, \etc, with operations specific to that kind.
842In all these cases, a node's address is significant, so nodes are communicated by reference/pointer, and never by copy (by value).
843
844
845\subsection{Design Issues}
846\label{toc:lst:issue}
847
848This thesis focuses on a reduced design space for linked lists that are important to, but not limited to, \emph{system programmers}.
849Within this restricted space, all design-issue discussions assume the following invariants.
850\begin{itemize}
851 \item The chain shape, as opposed to tree or hash table, is considered.
852 \item A doubly-linked list is being designed.
853 Generally, the discussed issues apply similarly for singly-linked lists.
854 Ordered linking (as opposed to circularly-linked) occurs.
855 \item Link fields are system-managed.
856 The system has freedom over how to represent these links.
857 The user works with the system-provided API to query and modify list membership.
858\begin{comment} % yes, that's what I'm building; no, it's not an invariant for the design-issue discussions
859 \item The user data must provide storage for the list link-fields.
860 Hence, a list node is \emph{statically} defined as data and links \vs a node that is \emph{dynamically} constructed from data and links \see{\VRef{toc:lst:issue:attach}}.
861\end{comment}
862\end{itemize}
863Alternatives to these assumptions are discussed under Future Work (\VRef{toc:lst:futwork}).
864
865
866\subsection{Preexisting Linked-List Libraries}
867\label{s:PreexistingLinked-ListLibraries}
868
869To show examples of the concepts being defined, these two preexisting linked-list libraries are used throughout, and further libraries are introduced as needed.
870\begin{enumerate}
871 \item Linux Queue library~\cite{lst:linuxq} (LQ) of @<sys/queue.h>@.
872 \item \CC Standard Template Library's (STL)\footnote{The term STL is contentious as some people prefer the term standard library.} @std::list@\cite{lst:stl}
873\end{enumerate}
874%A general comparison of libraries' abilities is given under Related Work (\VRef{toc:lst:relwork}).
875For the discussion, assume the type @req@ (request) is the user's payload in examples.
876Then the job of a list library is to help a user manage (organize) requests.
877A request might be a network arrival event processed by a web browser or a thread blocked/scheduled by a runtime.
878
879
880\subsection{Link Attachment: Intrusive \vs Wrapped}
881\label{toc:lst:issue:attach}
882
883Link attachment deals with the question:
884Where are the libraries' inter-node link-fields stored, in relation to the user's payload data fields?
885
886\VRef[Figure]{fig:lst-issues-attach} shows three basic styles.
887\VRef[Figure]{f:Intrusive} shows the \newterm{intrusive} style, placing the link fields inside the payload structure.
888\VRef[Figures]{f:WrappedRef} and \subref*{f:WrappedValue} show the two \newterm{wrapped} styles, which place the payload inside a generic library-provided structure that then defines the link fields.
889The wrapped style distinguishes between wrapping a reference or a value, \eg @list<req *>@ or @list<req>@.
890(For this discussion, @list<req &>@ is similar to @list<req *>@.)
891This difference is one of user style, performance (due to copying) and ownership; it is not a matter of framework capability.
892Library LQ is intrusive; STL is wrapped, supporting either reference or value.
893
894\begin{comment}
895\begin{figure}
896 \begin{tabularx}{\textwidth}{Y|Y|Y}
897 \lstinput[language=C]{20-39}{lst-issues-intrusive.run.c}
898 &\lstinputlisting[language=C++]{20-39}{lst-issues-wrapped-byref.run.cpp}
899 &\lstinputlisting[language=C++]{20-39}{lst-issues-wrapped-emplaced.run.cpp}
900 \\ & &
901 \\
902 \includegraphics[page=1]{lst-issues-attach.pdf}
903 &
904 \includegraphics[page=2]{lst-issues-attach.pdf}
905 &
906 \includegraphics[page=3]{lst-issues-attach.pdf}
907 \\ & &
908 \\
909 (a) & (b) & (c)
910 \end{tabularx}
911\caption{
912 Three styles of link attachment: (a)~intrusive, (b)~wrapped reference, and (c)~wrapped value.
913 The diagrams show the memory layouts that result after the code runs, eliding the head object \lstinline{reqs};
914 head objects are discussed in \VRef{toc:lst:issue:ident}.
915 In (a), the field \lstinline{req.x} names a list direction;
916 these are discussed in \VRef{s:Axis}.
917 In (b) and (c), the type \lstinline{node} represents a system-internal type,
918 which is \lstinline{std::_List_node} in the GNU implementation.
919 (TODO: cite? found in /usr/include/c++/7/bits/stl\_list.h )
920 }
921 \label{fig:lst-issues-attach}
922\end{figure}
923\end{comment}
924
925\begin{figure}
926\centering
927\begin{lrbox}{\myboxA}
928\begin{tabular}{@{}l@{}}
929\lstinput[language=C]{20-35}{lst-issues-intrusive.run.c} \\
930\includegraphics[page=1]{lst-issues-attach.pdf}
931\end{tabular}
932\end{lrbox}
933
934\begin{lrbox}{\myboxB}
935\begin{tabular}{@{}l@{}}
936\lstinput[language=C++]{20-35}{lst-issues-wrapped-byref.run.cpp} \\
937\includegraphics[page=2]{lst-issues-attach.pdf}
938\end{tabular}
939\end{lrbox}
940
941\begin{lrbox}{\myboxC}
942\begin{tabular}{@{}l@{}}
943\lstinput[language=C++]{20-35}{lst-issues-wrapped-emplaced.run.cpp} \\
944\includegraphics[page=3]{lst-issues-attach.pdf}
945\end{tabular}
946\end{lrbox}
947
948\subfloat[Intrusive]{\label{f:Intrusive}\usebox\myboxA}
949\hspace{6pt}
950\vrule
951\hspace{6pt}
952\subfloat[Wrapped reference]{\label{f:WrappedRef}\usebox\myboxB}
953\hspace{6pt}
954\vrule
955\hspace{6pt}
956\subfloat[Wrapped value]{\label{f:WrappedValue}\usebox\myboxC}
957
958\caption[Three styles of link attachment]{
959 Three styles of link attachment.
960 % \protect\subref*{f:Intrusive}~intrusive, \protect\subref*{f:WrappedRef}~wrapped reference, and \protect\subref*{f:WrappedValue}~wrapped value.
961 The diagrams show the memory layouts that result after the code runs, eliding the head object \lstinline{reqs};
962 head objects are discussed in \VRef{toc:lst:issue:ident}.
963 In \protect\subref*{f:Intrusive}, the field \lstinline{req.d} names a list axis;
964 these are discussed in \VRef{s:Axis}.
965 In \protect\subref*{f:WrappedRef} and \protect\subref*{f:WrappedValue}, the type \lstinline{node} represents a
966 library-internal type, which is \lstinline{std::_List_node} in the GNU implementation
967 \see{\lstinline{/usr/include/c++/X/bits/stl_list.h}, where \lstinline{X} is the \lstinline{g++} version number}.
968 }
969 \label{fig:lst-issues-attach}
970\end{figure}
971
972Each diagram in \VRef[Figure]{fig:lst-issues-attach} is using the fewest dynamic allocations for its respective style:
973in intrusive, there is no dynamic allocation, in wrapped reference only the linked fields are dynamically allocated, and in wrapped value the copy data-area and linked fields are dynamically allocated.
974The advantage of intrusive is the control in memory layout and storage placement.
975Both wrapped styles have independent storage layout and imply library-induced heap allocations, with lifetime that matches the item's membership in the list.
976
977\begin{comment} % duplicated from s:Axis, blurs the taxonomy
978In all three cases, a @req@ object can enter and leave a list many times.
979However, in intrusive a @req@ can only be on one list at a time, unless there are separate link-fields for each simultaneous list.
980In wrapped reference, a @req@ can appear multiple times on the same or different lists, but since @req@ is shared via the pointer, care must be taken if updating data also occurs simultaneously, \eg concurrency.
981In wrapped value, the @req@ is copied, which increases storage usage, but allows independent simultaneous changes;
982however, knowing which of the @req@ object is the \emph{true} object becomes complex.
983\see*{\VRef{s:Axis} for further discussion.}
984\end{comment}
985
986In the LQ column, \subref*{f:Intrusive}, the macro @LIST_INSERT_HEAD( &reqs, &r2, d )@ takes the list header, a pointer to the node, and the offset of the link fields in the node.
987The user's last argument is the field name; the macro expansion uses it as @(&r2)->d@, causing the compiler to insert the link-field offset.
988With this information, the API can use node pointers for conversing with the user, and also work on the link fields within.
989However, when there is only one set of link fields, the user is required to manage a redundant parameter.
990
991An aspect of layout control is allowing the user to specify link fields explicitly, controlling field order and attributes within the @req@ object.
992LQ allows this ability through the @LIST_ENTRY@ macro\footnote{It is possible to have multiple of these link fields allowing a node to appear on multiple lists simultaneously.}, which can be placed anywhere in the node.
993An example of an attribute on the link fields is cache alignment, possibly in conjunction with other @req@ fields, improving locality and/or avoiding false sharing.
994If a list is frequently traversed in the forward direction, and infrequently gets elements removed at arbitrary positions, then an ideal layout for cache locality puts the forward links, together with frequently-used payload data on one cache line, leaving the reverse links on a colder cache line.
995In contrast, supplying link fields by inheritance makes them implicit and relies on compiler placement, such as the start or end of @req@, and no explicit attributes.
996Wrapped reference has no control over the link fields, but the separate data allows some control;
997wrapped value has no control over data or links.
998
999Another subtle advantage of intrusive attachment is that a reference to a user-level item (@req@) is sufficient to navigate or manage the item's membership.
1000In LQ, the intrusive @req@ pointer is the correct argument type for operations @LIST_NEXT@ or @LIST_REMOVE@;
1001there is no distinguishing a @req@ from a @req@ in a list.
1002The same is not true of STL, wrapped reference or value.
1003There, the analogous operations, @iterator::operator++()@, @iterator::operator*()@, and @list::erase(iterator)@, work on a parameter of type @list<T>::iterator@;
1004there is no mapping from @req &@ to @list<req>::iterator@. %, for linear search.
1005
1006The advantage of wrapped is the abstraction of a data item from its list membership(s).
1007In the wrapped style, the @req@ type can come from a library that serves many independent uses,
1008which generally have no need for listing.
1009Then, a novel use can put a @req@ in a list, without requiring any upstream change in the @req@ library.
1010In intrusive, the ability to be listed must be planned during the definition of @req@.
1011
1012\begin{figure}
1013 \lstinput[language=C++]{100-117}{lst-issues-attach-reduction.hpp}
1014 \lstinput[language=C++]{150-150}{lst-issues-attach-reduction.hpp}
1015 \caption[Simulation of wrapped using intrusive]{
1016 Simulation of wrapped using intrusive.
1017 Illustrated by pseudocode implementation of an STL-compatible API fragment using LQ as the underlying implementation.
1018 The gap that makes it pseudocode is that
1019 the LQ C macros do not expand to valid \CC when instantiated with template parameters---there is no \lstinline{struct El}.
1020 When using a custom-patched version of LQ to work around this issue,
1021 the programs of \VRef[Figure]{f:WrappedRef} and wrapped value work with this shim in place of real STL.
1022 Their executions lead to the same memory layouts.
1023 }
1024 \label{fig:lst-issues-attach-reduction}
1025\end{figure}
1026
1027Finally, it is possible to simulate wrapped using intrusive, illustrated in \VRef[Figure]{fig:lst-issues-attach-reduction}.
1028This shim layer performs the implicit dynamic allocations that pure intrusion avoids.
1029But there is no reduction going the other way.
1030No shimming can cancel the allocations to which wrapped membership commits.
1031
1032Because intrusion is a lower-level listing primitive, the system design choice is not between forcing users to use intrusion or wrapping.
1033The choice is whether or not to provide access to an allocation-free layer of functionality.
1034An intrusive-primitive library like LQ lets users choose when to make this tradeoff.
1035A wrapped-primitive library like STL forces users to incur the costs of wrapping, whether or not they access its benefits.
1036Like LQ, the \CFA intrusive library of \VRef[Chapter]{ch:list} is capable of supporting a wrapped library, if need arose.
1037
1038
1039\subsection{Axis: Single \vs Multi-Static \vs Dynamic}
1040\label{s:Axis}
1041
1042\begin{figure}
1043 \parbox[t]{3.5in} {
1044 \lstinput[language=C++]{20-60}{lst-issues-multi-static.run.c}
1045 }\parbox[t]{20in} {
1046 ~\\
1047 \includegraphics[page=1]{lst-issues-direct.pdf} \\
1048 ~\\
1049 \hspace*{1.5in}\includegraphics[page=2]{lst-issues-direct.pdf}
1050 }
1051 \caption[Example of simultaneity using LQ lists]{
1052 Example of simultaneity using LQ lists.
1053 The zoomed-out diagram (right/top) shows the complete multi-linked data structure.
1054 This structure can navigate all requests in priority order ({\color{blue}blue}), and navigate among requests with a common request value ({\color{orange}orange}).
1055 The zoomed-in diagram (right/bottom) shows how the link fields connect the nodes on different lists.
1056 }
1057 \label{fig:lst-issues-multi-static}
1058\end{figure}
1059
1060\newterm{Axis} deals with the question:
1061In how many different lists can a node be stored, at the same time?
1062\VRef[Figure]{fig:lst-issues-multi-static} shows an example that can traverse all requests in priority order (field @pri@) or navigate among requests with the same requestor (field @rqr@).
1063Each of ``by priority'' and ``by common requestor'' is a separate axis.
1064The example has a single priority-list linked (first axis): [1, 2, 2, 3, 3, 4], where nodes may have the same priority.
1065And it has three common-requestor lists (second axis): [42, 42], [17, 17, 17], and [99], giving four head nodes, one for each list.
1066The example shows an axis can encompass all the nodes (by-priority) or only a subset of the nodes (three by-common-requestor lists).
1067
1068As stated, the limitation of intrusive is knowing a priori how many groups of links are needed for the maximum number of simultaneous lists.
1069Thus, the intrusive LQ example supports multiple, but statically many, linked lists.
1070Note, it is possible to reuse links for different purposes, \eg if an item is linked one way at one time and another way at another time, and these times do not overlap, the two different linkings can use the same link fields.
1071This feature is used in the \CFA runtime, where a thread node may be on a blocked or running list, but never on both simultaneously.
1072
1073Now consider the STL in the wrapped-reference arrangement of \VRef[Figure]{f:WrappedRef}.
1074Here it is possible to construct the same simultaneity by creating multiple STL lists, each pointing at the appropriate nodes.
1075Each group of intrusive links become the links for each separate STL list.
1076The upside is the unlimited number of lists a node can be associated with simultaneously, as any number of STL lists can be created dynamically.
1077The downside is the dynamic allocation of the link nodes and managing multiple lists.
1078Note, it might be possible to wrap the multiple lists in another type to hide this implementation issue.
1079
1080Now consider the STL in the wrapped-value arrangement of \VRef[Figure]{f:WrappedValue}.
1081Again, it is possible to construct the same simultaneity by creating multiple STL lists, each copying the appropriate nodes, where the intrusive links become the links for each separate STL list.
1082The upside is the same as for wrapped-reference arrangement with an unlimited number of list bindings.
1083The downside is the dynamic allocation, significant storage usage, and cost of copying nodes.
1084As well, it is unclear how node updates work in this scenario, without some notation of ultimately merging node information.
1085
1086% https://www.geeksforgeeks.org/introduction-to-multi-linked-list/ -- example of building a bespoke multi-linked list out of STL primitives (providing indication that STL doesn't offer one); offers dynamic directionality by embedding `vector<struct node*> pointers;`
1087
1088% When allowing multiple static directions, frameworks differ in their ergonomics for
1089% the typical case: when the user needs only one direction, vs.\ the atypical case, when the user needs several.
1090% LQ's ergonomics are well-suited to the uncommon case of multiple list directions.
1091% Its intrusion declaration and insertion operation both use a mandatory explicit parameter naming the direction.
1092% This decision works well in \VRef[Figure]{fig:lst-issues-multi-static}, where the names @by_pri@ and @by_rqr@ work well,
1093% but it clutters \VRef[Figure]{f:Intrusive}, where a contrived name must be invented and used.
1094% The example uses @x@; @reqs@ would be a more readily ignored choice. \PAB{wording?}
1095
1096An alternative system providing intrusive data-structures is \uCpp, a concurrent extension of \CC.
1097It provides a basic set of intrusive lists~\cite[appx.~F]{uC++}, where the link fields are defined with the data fields using inheritance.
1098\begin{cquote}
1099\setlength{\tabcolsep}{15pt}
1100\begin{tabular}{@{}ll@{}}
1101\multicolumn{1}{c}{singly-linked list} & \multicolumn{1}{c}{doubly-linked list} \\
1102\begin{c++}
1103struct Node : public uColable {
1104 int i; // data
1105 Node( int i ) : i{ i } {}
1106};
1107\end{c++}
1108&
1109\begin{c++}
1110struct Node : public uSeqable {
1111 int i; // data
1112 Node( int i ) : i{ i } {}
1113};
1114\end{c++}
1115\end{tabular}
1116\end{cquote}
1117A node can be placed in the following data structures depending on its link fields: @uStack@ and @uQueue@ (singly linked), and @uSequence@ (doubly linked).
1118A node inheriting from @uSeqable@ can appear in a singly or doubly linked structure.
1119Structure operations implicitly know the link-field location through the inheritance.
1120\begin{c++}
1121uStack<Node> stack;
1122Node node;
1123stack.push( node ); // link fields at beginning of node
1124\end{c++}
1125
1126Simultaneous axes cannot be done with multiple inheritance, because there is no mechanism to either know the order of inheritance fields or name each inheritance.
1127Instead, a special type is required that contains the link fields and points at the node.
1128\begin{cquote}
1129\setlength{\tabcolsep}{10pt}
1130\begin{tabular}{@{}ll@{}}
1131\begin{c++}
1132struct Node : public uColable {
1133 int i; // data
1134 @NodeDL nodeseq;@ // embedded intrusive links
1135 Node( int i ) : i{ i }, @nodeseq{ this }@ {}
1136};
1137\end{c++}
1138&
1139\begin{c++}
1140struct NodeDL : public uSeqable {
1141 @Node & node;@ // node pointer
1142 NodeDL( Node & node ) : node( node ) {}
1143 Node & get() const { return node; }
1144};
1145\end{c++}
1146\end{tabular}
1147\end{cquote}
1148This node can now be inserted into a doubly-linked list through the embedded intrusive links.
1149\begin{c++}
1150uSequence<NodeDL> sequence;
1151sequence.add_front( @node.nodeseq@ ); $\C{// link fields in embedded type}$
1152NodeDL nodedl = sequence.remove( @node.nodeseq@ );
1153int i = nodedl.@get()@.i; $\C{// indirection to node}$
1154\end{c++}
1155Hence, the \uCpp approach streamlines one set of intrusive links through the \CC inheritance mechanism, and falls back onto the LQ approach of explicit declarations for additional intrusive links.
1156However, \uCpp does not have the LQ trick of passing a field name as a macro argument, resulting in the somewhat more cumbersome realization of subsequent link fields.
1157
1158The major ergonomic difference among the approaches is naming and name usage.
1159The intrusive model requires naming each set of intrusive links, \eg @by_pri@ and @by_rqr@ in \VRef[Figure]{fig:lst-issues-multi-static}.
1160\uCpp cheats by using inheritance for the first intrusive links, after which explicit naming of intrusive links is required.
1161Furthermore, these link names must be used in all list operations, including iterating, whereas wrapped reference and value hide the list names in the implicit dynamically-allocated node-structure.
1162At issue is whether an API for simultaneity can support one \emph{implicit} list, when several are not wanted.
1163\uCpp allows it, LQ does not, and the STL does not have this question.
1164
1165
1166\subsection{User Integration: Preprocessed \vs Type-System Mediated}
1167
1168While the syntax for LQ is reasonably succinct, it comes at the cost of using C preprocessor macros for generics, while macros are not part of the language's type system.
1169Hence, small errors in macro arguments can lead to large substitution mistakes, as the arguments may be textually written in many places and/or concatenated with other arguments/text to create new names and expressions.
1170Hence, textual expansion can lead to a cascade of error messages that are confusing and difficult to debug.
1171For example, argument errors like @a.b@{\Large\color{red},}@c@, comma instead of period, or @by@{\Large\color{red}-}@pri@, minus instead of underscore, can produce many error messages.
1172Note, similar problems exist for \CC templates.
1173
1174Further issues with macros occur when the substitution result contains artifacts related to evaluation order that are not evident in the original.
1175A real problem that occurred while preparing the linked-list performance evaluation stemmed from the innocent-looking
1176\begin{c++}
1177TAILQ_REMOVE(reqs, TAILQ_LAST(reqs, reql), d);
1178\end{c++}
1179not being equivalent to the explicitly multi-step version:
1180\begin{c++}
1181struct req * last = TAILQ_LAST(reqs, reql);
1182TAILQ_REMOVE(reqs, last, d);
1183\end{c++}
1184It turns out that @TAILQ_REMOVE@ uses its ``which element to remove'' parameter at several places, importantly, one occurring after the removal's changes are in progress.
1185When the second use encounters the macro substitution @TAILQ_LAST(reqs, reql)@, it obtains a different node than the first use got, with the removal's changes having alredy started.
1186This macro-induced phenomenon led to an invalid pointer dereference (safety violation), at a run-time well after the removal at issue (costly to resolve).
1187
1188Instead, language function calls (even with inlining) handle argument mistakes locally at the call, giving a very specific error message. In the world of \CC templates, concepts were introduced to deal with this problem. Furthermore, language function calls use (only) C-language semantics for argument evaluation, instead of appealing to reasoning about how the semantics might play out in an invisible substitution result.
1189
1190So, avoiding a macro-centric implementation helps users avoid and diagnose critical mistakes.
1191
1192% example of poor error message due to LQ's preprocessed integration
1193% programs/lst-issues-multi-static.run.c:46:1: error: expected identifier or '(' before 'do'
1194% 46 | LIST_INSERT_HEAD(&reqs_rtr_42, &r42b, by_rqr);
1195% | ^~~~~~~~~~~~~~~~
1196%
1197% ... not a wonderful example; it was a missing semicolon on the preceding line; but at least real
1198
1199
1200\subsection{List Identity: Headed \vs Ad-Hoc}
1201\label{toc:lst:issue:ident}
1202
1203All examples so far use two distinct types for:
1204an item found in a list (type @req@ of variable @r1@, in \VRef[Figure]{fig:lst-issues-attach}), and the list ``itself'' (type @reql@ of variable @reqs@ in the same example, or @reqs_pri@, in \VRef[Figure]{fig:lst-issues-ident}).
1205This kind of list is \newterm{headed}, where the empty list is just a head.
1206An alternate \emph{ad-hoc} approach omits the header, where the empty list is no nodes.
1207Here, all link traversals begin from an existing reference to a node.
1208A give node may be able to see that it is first, but there is no direct access to a global first.
1209Note, a headed-list API is a superset of an ad-hoc list's, and can normally perform all of the ad-hoc operations.
1210\VRef[Figure]{fig:lst-issues-ident} shows both approaches for different list lengths and unlisted elements.
1211For headed, there are length-zero lists (heads with no elements), and an element can be listed or not listed.
1212For ad-hoc, there are no length-zero lists and every element belongs to a list of length at least one.
1213
1214\begin{figure}
1215 \centering
1216 \includegraphics[width=\textwidth]{lst-issues-ident.pdf}
1217 \caption[Comparison of headed and ad-hoc list identities]{
1218 Comparison of headed and ad-hoc list identities, for various list lengths.
1219 Pointers are logical, meaning that no claim is intended about which part of an object is being targeted.
1220 }
1221 \label{fig:lst-issues-ident}
1222\end{figure}
1223
1224The purpose of a header is to provide specialized but implicit node access, such as the first/last nodes in the list, where accessing these nodes is deemed a commonly occurring operation and should be $O(1)$ for performance of certain operations.
1225For example, without a last pointer in a singly-linked list, adding to the end of the list is an $O(N)$ operation to traverse the list to find the last node.
1226Without the header, this specialized information must be managed explicitly, where the programmer builds their own external, equivalent header information.
1227However, external management of particular nodes might not be beneficial because the list does not provide operations that can take advantage of them, such as using an external pointer to update an internal link.
1228Clearly, there is a cost maintaining this specialized information, which needs to be amortized across the list operations that use it, \eg rarely adding to the end of a list.
1229A runtime component of this cost is evident in LQ's offering the choice of type generators @LIST@ \vs @TAILQ@.
1230Its @LIST@ maintains a \emph{first}, but not a \emph{last};
1231its @TAILQ@ maintains both roles.
1232(Both types are doubly linked and an analogous choice is available for singly linked.)
1233
1234Libraries have not traditionally offered an ad-hoc list abstraction, but it is a common pattern when programmers roll their own inter-linked type.
1235The ad-hoc pattern obviates the questions, ``Who should own the head?'' or, ``Are we sure the head outlives the elements?''
1236Providing library support for ad-hoc lists is an opportunity to lower the incentive for rolling one's own.
1237
1238\subsection{End Treatment: Cased \vs Uniform }
1239
1240All lists must have a logical \emph{beginning/ending}, otherwise list traversal is infinite.
1241\emph{End treatment} refers to how the list represents the lack of a predecessor/successor to demarcate end point(s).
1242For example, a doubly-linked list implementation might represent a node that is solo in a list, by using null next/prev pointers.
1243Note, a list does not need to use links to denote its size;
1244it can use a node counter in the header, where $N$ node traversals indicates complete navigation of the list.
1245However, managing the number of nodes is an additional cost, whereas the links must always be managed.
1246
1247The following discussion refers to the LQ representations, detailed in \VRef[Figure]{fig:lst-issues-end}, which uses a null @succ@ pointer to mark the last element.
1248For example, consider the operation of inserting after a given element.
1249A doubly-linked list must update the given node's successor, to make its predecessor-pointer refer to the new node.
1250This step must happen when the given node has a successor (when its successor pointer is non-null),
1251and must be skipped when it does not (when its successor pointer cannot be navigated).
1252So this operation contains a branch, to decide which case is happening.
1253All branches have pathological cases where branch prediction's success rate is low and the execution pipeline stalls often.
1254Hence, this issue is relevant to achieving high performance.
1255
1256\begin{figure}
1257 \centering
1258 \includegraphics[width=0.55\textwidth]{lst-issues-end.pdf}
1259 \caption[LQ field-level representation of links and ends]{
1260 LQ field-level representation of links and ends.
1261 Each object's memory is pictured as a vertical strip.
1262 The location, within a strip, at which an arrow points, is significant.
1263 Uniform treatment of the first-end is evident from an assertion like \lstinline{(**this->pred == this)} holding for all nodes \lstinline{this}, including the first one.
1264 Cased treatment of the last-end is evident from the last-end analogous proposition, \lstinline{(this->succ->pred == &this->succ)}, failing when \lstinline{this} is the last node.
1265 }
1266 \label{fig:lst-issues-end}
1267\end{figure}
1268
1269Interestingly, this branch is sometimes avoidable, giving a uniform end-treatment in the code.
1270For example, LQ is uniform headed at the front.
1271For predecessor/first navigation, the relevant operation is inserting before a given element.
1272LQ's predecessor representation is not a pointer to a node, but a pointer to a pseudo-successor pointer.
1273When there is a predecessor node, that node contains a real-successor pointer; it is the target of the reference node's predecessor pointer.
1274When there is no predecessor node, the reference node (now known to be first node) acts as the pseudo-successor of the list head.
1275Now, the list head contains a pointer to the first node.
1276When inserting before the first node, the list head's first-pointer is the one that must change.
1277So, the first node's \emph{predecessor} pointer (to a pseudo-successor pointer) is set as the list head's first-pointer.
1278Now, inserting before a given element does the same logic in both cases:
1279follow the guaranteed-non-null predecessor pointer, and update that location to refer to the new node.
1280Applying this trick makes it possible to have list management routines that are completely free of conditional control-flow.
1281Considering a path length of only a few instructions (less than the processor's pipeline length),
1282such list management operations are often practically free,
1283with all the variance being due to the (inevitable) cache status of the nodes being managed.
1284
1285
1286\section{String}
1287\label{s:String}
1288
1289A string is a sequence of symbols, where the form of a symbol can vary significantly: 7/8-bit characters (ASCII/Latin-1), or 2/4/8-byte (UNICODE) characters/symbols or variable length (UTF-8/16/32) characters.
1290When drawn for human reading, text can be left-to-right, right-to-left, top-to-bottom, or have stacked elements (\eg Arabic).
1291But a string serves all of these presentations by forming the sequence of symbols that matches the order in which the text is read.
1292
1293A C character constant is an ASCII/Latin-1 character enclosed in single-quotes, \eg @'x'@, @'@\textsterling@'@.
1294A wide C character constant is the same, except prefixed by the letter @L@, @u@, or @U@, \eg @u'\u25A0'@ (black square), where the @'\u'@ identifies a universal character name.
1295A character can be formed from an escape sequence, which expresses a non-typable character @'\f'@, form feed, a delimiter character @'\''@, embedded single quote, or a raw character @'\xa3'@, \textsterling.
1296
1297A C string constant is zero or more regular, wide, or escape characters enclosed in double-quotes @"xyz\n"@.
1298The kind of characters in the string is denoted by a prefix: wide-character strings are prefixed by the same @L@, @u@, or @U@; UTF-8 strings, where characters have nonuniform byte lengths, are prefixed by @u8@.
1299
1300For wide string literals prefixed by the letter @L@, the array elements have type @wchar_t@ and are initialized with the wide characters corresponding to the multi-byte character sequence, \eg @L"abc@$\mu$@"@ and are read/printed using @wscanf@/@wprintf@.
1301The value of a wide-character is implementation defined, usually a UTF-16 character.
1302For wide string literals prefixed by the letter @u@ or @U@, the array elements have type @char16_t@ or @char32_t@, respectively, and are initialized with wide characters corresponding to the multi-byte character sequence, \eg @u"abc@$\mu$@"@, @U"abc@$\mu$@"@.
1303The value of a @"u"@ character is an UTF-16 character;
1304the value of a @"U"@ character is an UTF-32 character.
1305The value of a string literal containing a multi-byte character or escape sequence not represented in the execution character set is implementation defined.
1306For UTF-8 string literals, the array elements have type @char@ and are initialized with the characters of the multi-byte character sequences, \eg @u8"\xe1\x90\x87"@ (Canadian syllabic Y-Cree OO).
1307
1308C strings are null-terminated rather than maintaining a separate string length.
1309\begin{quote}
1310Technically, a string is an array whose elements are single characters.
1311The compiler automatically places the null character @\0@ at the end of each such string, so programs can conveniently find the end.
1312This representation means that there is no real limit to how long a string can be, but programs have to scan one completely to determine its length.~\cite[p.~36]{C:old}
1313\end{quote}
1314This property is only preserved by the compiler with respect to character constants, \eg @"abc"@ is actually @{'a', 'b', 'c', '\0'}@, \ie 4 characters rather than 3.
1315Otherwise, the compiler does not participate, making string operations both unsafe and inefficient.
1316For example, it is common in C to:
1317\begin{itemize}
1318\item
1319forget that a character constant is larger than it appears during manipulation,
1320\item
1321that extra storage is needed in a character array for the terminator,
1322\item
1323or that the terminator must be preserved during string operations, otherwise there are array overruns.
1324\end{itemize}
1325Finally, the need to repeatedly scan an entire string to determine its length can result in significant cost, as it is impossible to cache the length in many cases, \eg when a string is passed into another function.
1326
1327C strings are fixed size, because arrays are used for the implementation.
1328However, string manipulation commonly results in dynamically-sized temporary and final string values, \eg @strcpy@, @strcat@, @strcmp@, @strlen@, @strstr@, \etc.
1329As a result, storage management for C strings is a nightmare, quickly resulting in array overruns and incorrect results.
1330
1331Collectively, these design decisions make working with strings in C awkward, time consuming and unsafe.
1332While there are companion string routines that take the maximum lengths of strings to prevent array overruns, \eg @strncpy@, @strncat@, @strncpy@, that means the semantics of the operation can fail because strings are truncated.
1333Suffice it to say, C is not a go-to language for string applications, which is why \CC introduced the dynamically-sized @string@ type.
Note: See TracBrowser for help on using the repository browser.