Document directory
- 1. Big endian vs. little endian)
- 2. Allocation of bit Fields
- 3. Memory alignment
- 4. Character Set
- 1. File Inclusion macro
- 2. Content
- 1. confusions
- 2. historical issues
Thoughts on C
Cong Wang
May, 2006
Network Engineering Department
Institute of Post and Telecommunications, si'an, p.r. China
Introduction
C language combines all the power of assembly, and its abstraction happens to meet the requirements of programmers and is easy to implement. Thanks to its unique flexibility and powerful portability, system programmers and hackers love it even more. Undoubtedly, C language has achieved unprecedented success, to some extent even comparedUNIXBut also success. However, "the C language is like a knife, simple, sharp, and very useful to technicians. Like any sharp tool, C will hurt those who cannot control it ."[8]Behind the flexibility of C language, there are a lot of things to pay attention to and avoid. If you are not careful, you will be stuck in the bug quagmire. So it's no wonder that only C languages have books like The C Puzzle Book, Obfuscated C and Other Mysteries, and C Traps and Pitfalls. The following article does not repeat the problems mentioned in those books, but discusses other small points and some C-library functions. Most of them will be concentrated on Standard C. The reference standard isISO c99A small part may involve specific platforms.
I.
SizeofAnd
Return
SizeofIt is not a function, but a unary operator, and there is no need to enclose the expression following it. However, if the calculation is a type of size,SizeofYou need to add a parentheses, but this is not to say that it is a function.ReturnNot a function, but a keyword, and parentheses are not required. Note thatSizeofThe operator "return" is an unsigned integer (Size_t), If the "Return Value" andIntType comparison, you must first convert it into a signed integer. In addition, Unix system callsRead/writeThe return type of isSsize_tInsteadSize_t.Ssize_tRepresentativeSigned size.
Ii. 0.0, 0, '/0'
NULL,
NUL
NullIndicates a null pointer, which is not equal to any non-null pointer value. WhileNulIt is '/0', indicating a null character with a value of 0. Although 0.0, 0, '/0' and NULL are completely composed of 0 bits, their lengths are different. 0.0 is double precision, usually 8 bytes; '/0' is a character constant, usually only 1 byte;NullIt indicates a null pointer. Its length is determined by the system, that is, the number of bytes required to store a memory address in the system. 0 indicates an integer, usually four bytes. They have different types. Be careful when assigning values to variables, and perform forced conversion if necessary. Note: C language is not guaranteedCharIt is always 8 bits and contains 7 or 9 bits! Although not specifiedShortIt is 16 bits,IntIt is 32 bits, but it is true in all current architectures.LongNot sumIntIt is consistent with the machine Character length, that is, it is 4 bytes on a 32-bit machine, and 8 bytes on a 64-bit machine.LongIs an extension type, not supported by every compiler. It is 8 bytes on a 32-bit machine.
Iii. Portability
C language is well designed and highly efficient in terms of portability, which is also an important reason why GTK + and Linux Kernel prefer C. However, a program written in C language cannot be "Written once and run everywhere", even if you are using ansi c. The reason is very simple, because the C language was originally designed for the convenience of writing UNIX, it is inevitable to bring "underlying features", the implementation of C on different machines is somewhat different. This is disturbing.
1. big endian vs. little endian)
There was a war in history[9]And eventually ended in peace. The so-called "Big tail" means that the highest valid bits (MSB) are placed first and then placed in sequence. The tail is the opposite. For example, when the number is 66563, which is stored as a 32-bit int, the numbers are as follows:
AddressBig EndianLittle Endian00000000000000011100000001000001002000001000000000130000001100000000
IA-32 architecture is a small tail, while most other architecture is a big tail. The following program can test whether the machine is a large tail or a small tail.
int x = 1;if (*(char *)&x == 1) /* little endian */else /* big endian */
Byte sequence is very important in network transmission because machines on the network have different byte sequence. For convenience,TCP/IPSocket defines a unified network byte sequence (large tail) for the integer type, and also provides a set of conversion functions. Therefore, to write portable code, do not assume any specific byte order.
2. Allocation of bit Fields
"The Structure and link can be defined as a bit field (Bit-field. Bit field can be definedInt,Signed int,Unsigned intAnd can give the additional width value ."[3]The program in can determine how your hardware system and compiler process bit fields:
# Include <stdio. h>
Typedef struct DEMO {
Unsigned int one: 1;
Unsigned int two: 3;
Unsigned int three: 10;
Unsigned int four: 5;
Unsigned int: 2;
Unsigned int five: 8;
Unsigned int six: 8;
} Demo_type;
Int main (void ){
Int k;
Unsigned char * bptr;
Demo_type bit = {, x 81 };
Printf ("/nsizeof demo_type = % u/n", sizeof (demo_type ));
Printf ("initial values: bit = % u, % u/n", bit. one, bit. two, bit. three, bit. four, bit. five, bit. six );
Bptr = (unsigned char *) & bit;
Printf ("hex dump of bit: % 02x % 02x % 02x % 02x % 02x % 02x % 02x % 02x/n", bptr [0], bptr [1], bptr [2], bptr [3], bptr [4], bptr [5], bptr [6], bptr [7]);
Bit. three = 1023;
Printf ("/nassign 1023 to bit. three: % u, % u/n ", bit. one, bit. two, bit. three, bit. four, bit. five, bit. six );
K = bit. two;
Printf ("assign bit. two to k: k = % I/n", k );
Return 0;
}
InIA-32 (gcc4.1.0)The running result is as follows:
Sizeof demo_type = 8
Initial values: bit = 129,129
Hex dump of bit: 1b 60 24 10 81 00 00
Assign 1023 to bit. three: 129,129
Assign bit. two to k: k = 5
As shown above,GccWhen the compiler allocates memory for bit fields, it still allocates 8 bytes. When storing a bit field, the compiler allocates it from right to left, which of course varies with machines. In C language, there are few rules on how the compiler arranges bit fields. There are indeed some types of allocation units, and the size of the allocation Unit also depends on the compiler, but the compiler can assign bit fields from high or low. To write a portable program, do not assume how the bit field is allocated.
3. Memory alignment
In most cases, you don't have to pay attention to memory alignment, because the compiler is ready for us. However, if you are writing a compiler, alignment is one of the main issues you are concerned about. Alignment has obvious advantages-it increases the memory access speed, although it sometimes wastes some space. Some also require strict alignment in the server-level architecture. Otherwise, exceptions may be triggered, which is very important to kernel programmers. Even in CISC systems such as Intel, some SSE and SSE2 commands have strict requirements on alignment, such as movdqa (SSE command) and movapd (SSE2 command ). Therefore, we should "peek" the memory alignment.
The compiler usually performs alignment for us. However, alignment issues when programmers use pointers outside of the compiler's expectation to access data. The following program[11]It may not work properly.
Char Foo [10];
Char * P = & Foo [1];
Unsigned long l = * (unsigned long *) P;
Because the pointer p may not be a multiple of 4. The alignment problem is more obvious in the struct.
struct foo_struct { char foo; /* 1 byte */ unsigned long baz; /* 4 bytes */ unsigned short bar; /* 2 bytes */ char foobar; /* 1 byte */};
The above struct is not stored in the memory as you can see. The compiler will fill the blank space in it to achieve alignment. The baz offset may be 4 rather than 1! The solution is to manually fill in or reschedule the structure member sequence.
struct foo_struct { unsigned long baz; /* 4 bytes */ unsigned short bar; /* 2 bytes */ char foo; /* 1 byte */ char foobar; /* 1 byte */};
Iso cIt is required that the compiler never change the sequence of struct members. This work can only be done by programmers.
4. Character Set
Most systems use ASCII or Unicode to encode characters, while Unicode is compatible with ASCII. Therefore, if you only use English characters, you do not have to worry about character encoding. However, other character sets do exist, which are incompatible with the preceding two character sets, such as the DBCS Character Set and the ibm ebcdic character set. The following program does not work properly on the machine that uses the EBCDIC character set!
For (I = 0; I <strlen (FOO); I ++)
{If (FOO [I] >=32 & Foo [I] <= 126) * (PTR ++) = Foo [I];}
C99 only ensures that all bytes whose bits are 0 must exist in the character set. It is used to end null characters of the string. 26 uppercase English letters, lowercase letters, and 10 decimal numbers, the 29 characters below,
! " # % & ' ( ) * + , - . / :; < = > ? [ / ] ^ _ { | } ~
Space characters, and control characters indicating vertical tabulation, horizontal tabulation, and page feed should be in the most basic source character set and execution character set; the value of numeric characters after 0 should be greater than the value before it; each character should be a byte size; the source character set should contain characters that indicate the end of the row; the execution character set should contain the characters that indicate the ring, backspace, carriage return, and line feed. Beyond this range is undefined.
4. Bad "Hungarian notation"
Many years ago, Hungary programmersCharles SimonyiA naming method is designed to add a specific prefix to the variable name to identify the variable type. Its advantage is that it can be identified directly by the variable name, instead of looking for its definition. Microsoft later adopted this idea and we can see a lot of such expressions in VC. However, this not only makes the name of a variable odd and hard to remember, but also makes it very difficult to change the variable type. Imagine that we defined a global variable in a large project. After using dozens of functions, we suddenly found that the number of bytes of this type is insufficient, we have to change its type. Okay, you have to change the name from the beginning! Now many programmers have abandoned it, except some stubborn Windows programmers. For variable naming, the "all lowercase" style called by Unix systems may be worth learning. It is simple and elegant. If you want to use uppercase letters, you may want to learn the Qt naming style.
V. header file 1. File Inclusion macro
Many believe that# IncludeIn macro commands, <> and "" are equivalent. They are wrong! In the # include macro command, if either of the header file names is <>, it indicates the header file and the standard library area of the program installed in the hard disk (inLinuxThis directory is/usr/include), and if it is "", it means that the header file is saved in the programmer's own disk directory, rather than the local library in the standard system directory. This should be highly valued, not all C language tutorials will clearly point out this point, and the consequence of ignoring it will be the lack of corresponding header files. Unfortunately, some compilers do not give warnings when header files are missing.LintTo further check our program.
2. Content
We know that in the preprocessing phase, the C Preprocessor# IncludeCopy the header file to the source code. Therefore, it is worth noting what content should be put in the header file. Generally, we put the declarations of constants, types, and functions into the header file. But note: do not include the variable declaration! Otherwise, multiple Defined variables may be generated! If you can, "it is best not to include the header file in the header file. Multi-inclusion is the root of system programming. It is not uncommon for a c source file to include more than five times for compilation. UNIX/usr/include/sys is very bad. "[10]# IfdefIt may solve some problems, but it is often misused. Many header files in the standard library of C define their own macros.# IfdefCheck. For example, stdio. H has the following macro:
# Ifndef _ stdio_h
# DEFINE _ stdio_h
/* Omit other code */
# Endif
In this way, we can use macros to check whether there are anyStdio. hThis header file is:
# Ifdef _ stdio_h
/* If stdio. H has already been encoded ded ...*/
# Endif
The consequence of not paying attention to this is that hundreds of lines of useless code are included and passed to the lexical analyzer, consuming a lot of valuable compilation time.
Vi. function return value 1. confusions
Some programming languages do not allow the return value of a function to be abandoned, and C is not one of them. We usually look like thisVoidSame type FunctionScanfFunction, even if we know that its prototype isIntAnd there are no errors!
...
Scanf (...);
...
In fact, this is not surprising. Do you often use the following statements like this?
Int x = 9;
++ X;
...
Expression++ XIt also has its value, but you have not used its "return" value (10 here )! As++ X,ScanfIt also has both the value and the effect (put the input value into a memory, and++ XThe effect isXValue plus one), we only use their effect, instead of their values.To explicitly indicate that no return value is used, it is best to write it like this:
...
(Void) scanf (...);
...
2. historical issues
BecauseISO CNot in the Early DaysVoidType, which allows usVoidType.IntThe compiler relaxed the check on the prototype of these functions. Besides, if you leave the return type field blank in the function title, the default return type isInt, This isISO CTo maintain compatibility with the old version. This should be paid enough attention, because if we omit the prototype or prototype that appears after the nth function call, the compiler may use the form parameter type in the nth function call to build a prototype, and the return type will always beInt! This may be true or not.
7. Be careful
CallocFunction
To the endCallocWhat has been done when the memory is allocated successfully? I believe many people think that it allocates block memory to the array and initializes all its array elements to 0. That's no wonder, because many C language tutorials have said that. HoweverCallocAre you sure you want to do that? The answer is uncertain.[4]Section 7.20.3.1, aboutCallocI wonder if you have noticed the Note after the description. Note is like this: "Note that this need not be the same as the representation of floating-point zero or a null pointer constant." that's right. The C language does not guarantee that all bits 0 are the zero representation of the pointer (null) or floating point number (0.0! If portability is really important to you, don't rely on it.CallocInitialize the variable to 0. If the array element is a pointer or floating point number (or you are creating a struct or joint array containing a floating point number or pointer), you can use the loop to initialize the variable yourself. Initialization is really important, especially when you have high portability requirements, whether the pointer is initializedNull, Whether array elements are initialized or not sometimes determines the program's success or failure, and it is so difficult to identify these errors. The only thing we can do is be very careful.
8. Strange
SprintfFunction
[4]Section 7.19.6.6 aboutSprintfThe function is described as follows:
"# Include <stdio. h>
Int sprintf (char * restrict s, const char * restrict format ,...);
SprintfFunctions are equivalentFprintfExcept that the output is written into an array (marked as parameter s) rather than a stream.NULThe character is written to the end of the character group; it is not included in the return value. If replication occurs between overlapping objects, the behavior is undefined. "
This is not surprising, but someone once wanted to use sprintf () to add different types of values to a character array:
Sprintf (mystring, "% s % d % s % f", string, j, otherstring, d );
Unfortunately, it cannot really add other types to the character array (it sounds like the cellular array in Matlab), but all of them are converted into characters and then written.
Char tempstring [50] = {0 };
Char * otherstring;
Int d = 5;
Float j = 3.14;
Otherstring = "other ";
Sprintf (tempstring, "% f % s % d", j, otherstring, d );
Printf ("% s/n", tempstring );
Printf ("% d/n", tempstring [13]);
Converts other types of data into character Arrays for storage.SprintfFunctions are really a good method. However,SprintfIt is also a problematic function. Its fatal defect is that it does not know whether the number of bytes written exceeds the buffer size!
Int showmsg (int line, unsigned int err, char * msg ){
Char buf [100];
If (msg = NULL) return-1;
Else
Sprintf (buf, "Err occurred in line % d, % u is % s.", line, err, msg );
Return 0;
}
In the above program, the prompt occupies 28 bytes,LineIt may take up to 10 bytes,ErrMay occupy 11 bytes. That is to say,MsgThe size cannot exceed 50 bytes! FromSprintfCan not get anyBufSufficient information. This is dangerous and there is almost no safe way to use it!
WindowsProvides_ SnprintfFunction. Its prototype is:
Int _ snprintf (char * buffer, size_t count, const char * format [, argument]...);
AndSprintfThe difference is that when the buffer is insufficient,_ SnprintfA negative number is returned, but it does not guarantee that the destination buffer will end with NUL. You must do it manually. This is quite useful. Unfortunately,_ SnprintfNotISO C99.
9.
StrncatAnd
StrncpyUnreliable Functions
"StrcpyAndStrcatIt is not safe and should be usedStrncpyAndStrncat." This is what many C programmers talk about. In fact, the people who say this sentence are often unclear.StrncpyAndStrncatHow it works, and this is often worse! AboutStrncpyThe biggest misunderstanding is that it will useNUL(Or '/0') to end the target string. In fact, only when the source string length is smaller than the ParameterNWhen the above sentence is correct, you need to be careful that it will useNULTo fill in the vacant space. When copying a part of the source string, the correct method is to useStrncpyThen manually addNULTo end the string. More accurately, there is no need to manually endStaticString orCallocArray of allocated characters, because they are initialized to 0 at the time of allocation. AboutStrncatThe biggest misuse is the incorrect use of length parameters.N. AlthoughStrncatGuaranteeNULTo end the string, but you should notNULIt is also calculated in the ParameterN. More importantly,NIt is not the length of the target string, but the size of space that can be used. It is usually a variable that should be calculated, not a constant. Finally, you may say, "I know all this ." However, we do not recommend that you useStrncatAndStrncpyFunctions, because they are poorly designed. Let's see how troublesome the following program is!
Strncpy (path, homedir, sizeof (path)-1 );
Path [sizeof (path)-1] = '/0 ';
Strncat (path, "/", sizeof (path)-strlen (path)-1 );
Strncat (path, ". foorc", sizeof (path)-strlen (path)-1 );
Len = strlen (path );
[6]Is recommendedStrlcpyAndStrlcatFunctions (for the two functions, you can view the source code and related manuals in the/pub/OpenBSD/src/lib/libc/string directory on the ftp.openbsd.org server ), because they always end strings with NUL, they all take the full length of the destination character array as the parameter, and they return the total length of the string that the programmer wants. Their prototype is:
Size_t strlcpy (char * dst, const char * src, size_t size );
Size_t strlcat (char * dst, const char * src, size_t size );
The above program can be usedStrlcpyAndStrlcatIt is very convenient.
Strlcpy (path, homedir, sizeof (path ));
Strlcat (path, "/", sizeof (path ));
Strlcat (path, ". foorc", sizeof (path ));
Len = strlen (path );
Unfortunately, they justOpenBSD. We hope that the C Standards Committee will add these two convenient functions to the standards in the future.
10. Difficult to control
StrtokFunction
StrtokThe function is really not useful, isn't it? InISO C99The description of this function is up to 6 (see[4]In section 7.21.5.8), connectLinux Programmer's ManualWe will never use this function.Strtok_rFunction ).StrtokAlthough each line can be broken down into a single field without blank fields, it also makes the code unreentrant. BecauseStrtokA function has a piece of static data related to it-if you pass it a null pointer, it will "continuously search" (You may try to write your ownStrtokFunction .). This will bring a lot of trouble! If you useStrtokYou should be more careful, because objects with static data may not work with recursive functions. In addition, this function does not provide us with a way to reach its internal buffer. Similarly, if multiple strings are parsed in the same programStrtokFunction. parsing strings may interfere with each other and cannot work properly.[2]We recommend that you try extension functions.StrsepBut it is not portable. It correspondsStrtokSimilar functions, but it does not use static buffer. Its prototype is:
# Include <string. h>
Char * strsep (char ** stringp, const char * delim );
If* StringpYesNULL,StrsepDo nothing and return quietlyNULL. Otherwise,StrsepIn* StringpSearch for the first occurrence of a qualifier inDelimAny character in, and useNulTo end the string before it, let* StringpIt is a character that points to the end of it. If no characters are found,* StringpSetNull. It returns the parsed string.
In addition, if youUnix/LinuxWrite programs on the system. When both threads callStrtokThe thread is also unsafe.POSIXDefines a thread-safe function.Strtok_rTo replaceStrtok. "_ R" indicates reentrant (Reentrant). Its prototype is:
# Include <string. h>
Char * strtok_r (char * restrict S, const char * restrict delim, char ** restrict lasts );
Besides the additional parametersLastsBesides,Strtok_rAndStrtokSimilar performance.LastsIs a pointer provided by the user, pointingStrtok_rThe unit used to store the starting address of the next resolution. But it is not easy to use. Generally, do not useStrtokFunction unless you know it very well. However, it is still an excellent tool when the environment is appropriate. Furthermore, do not use static variables unless controlled.
11. Avoid segmentation errors
Segmentation fault, That is, segment errors, which are common errors when we compile C Programs, especially on Linux. However, many people only know that it is related to pointers, but do not know the details of the error. I would like to briefly explain my understanding here. We know that there are two ways to manage virtual memory in the operating system: segmentation and paging. Paging means that the memory is divided into pages of equal size for management. Each page contains words with memory. Segmentation means that each process has a memory segment of the required size, and the segments are separated by blank storage blocks. The operating system knows the upper bound of each segment, and each segment starts with a virtual 0 address. When a program accesses a memory block, it calls a virtual address,MMU(Memory Management Unit) Maps it to a real address. If the operating system finds that the request address does not match the valid segment address, it will send a termination signal to the process.Segmentation faultThis is the case. IfSegmentation faultOccurs, indicating that your program has an error pointer, memory leakage, or other Access Error Memory Address error. Please carefully check whether your pointer and array are correct. GenerallyMallocThe allocated memory is used instead of the unallocated memory pointer.Strcpy.
12. Rename again
Before reading this section, you must understandISO c99Concepts in. A source file and Its contained header files or source files are called preprocessing translation units (Preprocessing Translation Unit). After preprocessing, the previous preprocessing translation unit is called the Translation Unit (Translation Unit). Make an identifier declared in different scopes or declared multiple times in the same scope point to the same target or function, called a link (Linkage). In a series of translation units and libraries that constitute the entire program, the same one has external links (External LinkageExternal refers to the external part of the Translation Unit (External name) Each declaration represents the same target or function; in a translation unit, the same has an internal link (Internal linkageEach declaration of the identifier also represents the same target or function. Macro name or an identifier without external links is called an internal name (Internal name).ISO C99Rule: at least the first 31 characters of the internal name must be unique, and the first 6 Characters of the external name must be unique. The case sensitivity can be insensitive. Therefore, when defining a name, make sure that it does not conflict with the identifier reserved by C. Otherwise, the behavior is undefined. Therefore, the following external names may look different, but they may be the same for the compiler:
Cleared ()AndClearerr (),CallOccasionally ()AndCalloc (),ReallocationAndRealloc ()
Of course, your compiler may be able to distinguish between 6 or more characters and be case sensitive. However, the above rules are worth following. For reserved identifiers of C and C ++,[7]It is a good reference.
13. variable parameters
Standard LibraryStdarg. hIt provides a portable way for programmers to Write Functions with variable parameters, just likePrintf. But how is it implemented? The following analyzes the specific implementation of variable parameters on the IA-32 platform. One implementation is as follows:
#ifndef STDARG_H#define STDARG_Htypedef char* va_list;#define va_rounded_size(type) /(((sizeof (type) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))#define va_start(ap, v) /((void) (ap = (va_list) &v + va_rounded_size (v)))#define va_arg(ap, type) /(ap += va_rounded_size (type), *((type *)(ap - va_rounded_size (type))))#define va_end(ap) ((void) (ap = 0))#endif
The internal macro va_rounded_size specifies that the calculation type isIntThe number of times of the type, the result is rounded up because the stack in the IA-32 is 4-byte aligned. In macroVa_startMedium,VIs the last parameter you declare,Va_startLetApPoint to the parameter next to it and return nothing. MacroVa_argThe next element in the access parameter listApFirst point to the next one, and then return the previous one, that is, the originalApParameter.Va_endSetApIf it is set to zero, nothing is returned. Using these three macros as an interface, you can implement variable parameter functions.
14. Type Modifier
C99 defines three type modifiers (type quali modifier), which are:Const,Volatile,Restrict.
ConstIs not as simple as you think. You cannot simplyConstThat isConstant.ConstRead-only (read-only) is used to prevent some variables from being modified, such as the source string when the string is copied. The position of const is very important, suchConst int * x;Is to define a pointer to a read-only integer, the integer cannot be modified and the pointer itself can;Int const * x;Is to define a read-only pointer to an integer, the integer can be modified but the pointer cannot. Note: "cannot be modified" does not mean that it can never be modified, but cannot be modified through this symbol. The following program is correct:
Const int a = 10;
Int * p;
P = (int *) &;
(* P) ++;
Surprisingly,Const char **AndChar **Incompatible,[5]And I will not go into details here.
VolatileThe keyword indicates that the variable can be changed without warning. It notifies the compiler to reload the variable every time it encounters a marked variable, rather than store it to access its copy. UseVolatileThe best example is to handle hardware interruptions, registers, and synchronization process sharing variables.
The most incomprehensible modifier isRestrictThe definition in section 6.7.3.1 of the C99 standard is very obscure. In fact,RestrictThe role of a pointer is to restrict access to a piece of memory. Further, if a block of memory is accessed by a restricted pointer, it cannot be accessed by another restricted pointer. Visible, different from the first two,RestrictIt can only be used for pointers. IntroductionRestrictThe purpose is to ensure that there are no other references in the same memory, so that the compiler can better optimize commands and generate more effective assembly code.
References:
[1]The C Programming Language, 2nd Ed
Brian W Kernighan and Dennis M Ritchie, Prentice Hall, 1988, ISBN 0-13-110362-8.
[2]C Unleashed
Richard Heathfield, Lawrence Kirby Etc.
[3]Applied C: An Introduction and More
Alice E. Fischer and David W. Eggert, McGraw Hill, ISBN 7-5053-6931-8.
[4]ISO 1999 Programming languages ages-C
[5]Expert C Programming
Peter van der Liden, Prentice Hall, ISBN 0-13-177429-8.
[6]Strlcpy and strlcat-consistent, safe, string copy and concatenation
Todd C. Miller and Theo de Raadt, 1999 USENIX Annual Technical Conference.
[7]C Reserved Identifiers
Stan Brown, 15 Sep 2003.
[8]C Traps and Pitfalls
Andrew Koening, ISBN 0-201-17928-8, Addison-Wesley.
[9]On Holy Wars and A Plea for Peace
Danny Cohen,IEEE Computer, 14 (10): 48-54, October 1981.
[10]Notes on programming in C
Rob Pike, February 21,198 9.
[11]Linux Kernel development, Second Edition
Robert Love, January 12,200 5, Sams Publishing, ISBN 0-672-32720-1.