The first rule of C programming - do not use it if you can do with other tools.
When programming in C, the lang
defaults to C99, and therefore no additional options are required.
clang
version: clang 3.5
by default works with C99, clang 3.6
- with C11. I'm not sure how tough it is when using out of the box.By default,gcc-5
requests-std=gnu11
, but in practice you need to specify c99 or c11 without GNU.
If you find something likechar
,int
,short
,long
orunsigned
in the new code, here are some bugs.
int_least16_t
option, which will do a great job with functions of the same type, but IMHO is much more detailed than worth it).In modern programs it is necessary to specify #include <stdint.h>
and only then choose standard data types.
int
not spelled «std»
does not mean that we are dealing with something non-standard. Types such as int
, long
and others are built into the C language. And typedefs, recorded in <stdint.h>
, appear later as additional information. This does not make them less “standard” than built-in types, although they are in some way inferior to the latter.float
- 32-bit floating point standarddouble
- 64-bit floating point standard
float
and double
are quite common IEEE types for 32 and 64-bit floating-point standards, in particular, on modern systems, you should not dwell on this when programming in C. I worked on systems where float was used on 64 bits.Please note: no morechar.
Usually in the C programming language, thechar
command is not only named, but also used incorrectly.
Software developers continually use the char command to refer to "byte", even when unsigned byte operations are performed. Much more correctly for individual unsigned byte / octet values specifyuint8_t
, and for a sequence of unsigned byte / octet values selectuint8_t *
.
unsigned char
. If talking about octets, select uint8_t
. In the case when CHAR_BIT > 8
, uint8_t
cannot be created, which means that it will not work and compile the code (perhaps this is what you need). If we work with objects of at least 8 bits, use uint_least8_t
. If bytes are octets, add something like this to the code: #include <limits.h> #if CHAR_BIT != 8 #error "This program assumes 8-bit bytes" #endif
CHAR_BIT == 8
.in the C programming language, string literals("hello")
look likechar *
.
Do not try to write code using unsigned
. Now you know how to write a decent code without the unreasonable conventions of C with numerous data types that not only make the content unreadable, but also call into question the effectiveness of using the finished product.
Who would like to introduce unsigned long long int if you can restrictuint64_t
to a simpleuint64_t
?
unsigned long long
, at least 64-bit, and it may be present or absent indents. uint64_t
designed exactly for 64 bits, and without bits of indents; This type is not necessarily registered in this or that code.unsigned long long
embedded type in C. Anyone familiar with this programming language is familiar.uint_least64_t
, which may be identical or different from unsigned long long
.The types <stdint.h>
much more specific and precise in meaning, they better convey the intentions of the author, are compact - not least important for exploitation and readability.
intN_t
and uintN_t
much more specific. But not all codes are important. Do not specify what is unimportant for you. Choose uint64_t
only when you really need exactly 64 bits - no more, no less.The correct type for pointers in this case isuintptr_t
, it is specified by the files<stdint.h>
.
uintptr_t
is set by <stdint.h>
, not <stddef.h>
.void*
cannot be converted to another integer type without data loss is unlikely to determine uintptr_t
(Such cases are extremely rare, if they exist at all).Instead:
long diff = (long)ptrOld - (long)ptrNew;
Use:
ptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew;
ptrdiff_t diff = ptrOld - ptrNew;
ptrdiff_t diff = (char*)ptrOld - (char*)ptrNew;
ptrOld
and ptrNew
do not indicate the necessary parameters, or simply jump from the end of the object, it will be difficult to track how the pointer causes the command to subtract data. The transition to uintptr_t
guarantees at least a relative result, although it can hardly be called very useful. Comparison or other arithmetic operations with pointers are permissible only when writing code for high-level systems, otherwise it is important that the studied pointers refer to the end of a certain object or jump from it (Exception: == and! = Work fine for pointers that refer to different objects).In such situations, it is rational to refer to intptr_t, an integer data type corresponding to values equal to a word on your platform.
intptr_t
signed integer type that successfully converts void*
to intptr_t
and back without losing data. And it can be a value greater than void*
.On 32-bit platforms,intptr_t
transformed toint32_t
.
On 64-bit platforms,intptr_t
takes the formint64_t
.
Essentially, size_t
is something like an “integer value capable of storing huge array indices.
and, therefore, he is able to fix impressive indicators of bias in the program being created.
In any case, on modern platforms,size_t
has practically the same characteristics asuintptr_t
, and therefore, on 32-bit versions,size_t
transformed intouint32_t
, and on 64-bituint64_t
- intouint64_t
.
size_t
can be used to preserve the size of any individual object, while uintptr_t
sets any pointer value, and, accordingly, with their help, you no longer confuse the byte addresses of various objects. Most modern systems work with indivisible address lines, and therefore, theoretically, the maximum object size is equal to the total memory capacity. C programming standards require strict compliance with this requirement. For example, you may encounter a situation where on a 64-bit system objects do not exceed 32 bits.Do not refer to data types during operation. Always use appropriate type pointers.
The initial pointer value is% p (in modern compilers it is displayed in hexadecimal; initially sends a pointer to void *
)
printf("Local number: %" PRIdPTR "\n\n", someIntPtr);
someIntPtr
implies the type int*
, actually sets the type intptr_t
. some_signed_type n; some_unsigned_type u; printf("n = %jd, u = %ju\n", (intmax_t)n, (uintmax_t)u);
intmax_t
and uintmax_t
, as a rule, 64-bit. Their transformations are much more economical than physical I / O.Note:% falls into the body of the format string, while the type pointer remains outside.
Modern compilers support #pragma once
#ifndef
. In the next section, "Alternatives to the #ifndef Packer" flashed #pragma once, but in this case it’s just noted that this is not a portable option.This function is supported by all compilers, and on different platforms, and is much more efficient mechanism than manually entering the security code for the header.
#ifndef
directive may not be perfect, but is reliable and portable.IMPORTANT: If internal structure is provided in your structure, the {0} method will not reset the additional bytes intended for this purpose. So, for example, it happens if a struct thing has 4 bytes of padding aftercounter
(on a 64-bit platform), because structures are filled in increments equal to one word. If you need to zero the entire structure including unused bytes of indents, specifymemset(&localThing, 0, sizeof(localThing))
, sincesizeof(localThing) == 16 bytes
, even though only 8 + 4 = 12 bytes are available.
memset
to reset them. Although I will note that clearing structures using memset
, even taking into account that whole elements will indeed be assigned a value of zero, does not guarantee the same effect for floating-point types or pointers — they must be equal to 0.0 and NULL
, respectively (although most systems function perfectly works).In C99, variable length arrays appeared
{ unsigned char *buf = malloc(N); if (buf == NULL) { /* allocation failed */ } /* ... */ free(buf); }
{ unsigned char buf[N]; /* ... */ }
If the function works with * arbitrary ** source data and a certain length, do not limit the type of this parameter. *
Knowingly erroneous:
void processAddBytesOverflow(uint8_t *bytes, uint32_t len) { for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } }
Instead, use:
void processAddBytesOverflow(void *input, uint32_t len) { uint8_t *bytes = input; for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } }
void*
an ideal type for fixing parameters of an arbitrary fragment of memory. Take at least the mem*
function in the standard library (But len should be size_t
, not uint32_t
).By declaring the source data type as void *, and re-assigning or once again referring to the actual data type that is needed right in the function body, you will protect users, because they don’t have to think about what is happening in your library.
void*
to uint8_t*
.In this example, some readers are faced with the problem of alignment.
C99 provides us with the whole set of functions<stdbool.h>
, wheretrue
is 1 andfalse - 0
.
bool
, which is used as an alias for the built-in _Bool
type.In the case of successful / unsuccessful return values, the functions should returntrue
orfalse
, rather than the return typeint32_t
, which requires manual input of 1 and 0 (or, even worse, 1 and -1; how to figure it out: 0 -success
, and 1 -failure?
or 0 -success
, and -1 -failure?
)).
true
or false
. Just do not confuse them with successful / unsuccessful outcomes of running the code.bool
necessarily assigned a name in the form of an assertion. In English, this will be the wording that answers the yes / no question. For example, is_foo()
and has_widget()
function designed for a specific action, in the case with which it is important for you to know how successful it can be performed, is likely to be specified by another statement. In some languages it is reasonable to resort to adding / subtracting exceptions. On C, you have to follow certain unspoken rules, including setting a zero value for a positive result of a function.The only product that in 2016 will allow formatting products developed in C is clang-format. Native clang-format settings are an order of magnitude higher than any other automatic C-code formatter.
Never usemalloc
Get used tocalloc
.
calloc
, you will encounter the fact that any bug in the code will be equal to zero, which means that it will be easy to confuse a system error with unnecessary data. Does this sound like code enhancement?calloc
.Source: https://habr.com/ru/post/276611/
All Articles