clang, default
GNU C11
), so no additional options are required for modern programs.C11
standard, specify -std=c11;
if you prefer to work with the C99 standard, mark - std=c99
.gcc
.gcc
, it is important to specify -std=c99
or -std=c11
gcc
creates source files slower than clang
, but sometimes generates faster code. A comparative characteristic of performance and regression testing results is indicative.gcc-5
works in GNU 11
mode (like clang
), but if you need C11 or C99, you will again have to specify -std=c11
or -std=c99
.-O2
fits you, but sometimes you need -O3
Test both versions (including for different compilers), and then save the most efficient executable files.-Os
helps out when there are issues with cache performance (and this is no accident).-Wall -Wextra -pedantic
-Wpedantic
option, although you can also refer to the ancient -pedantic
if necessary, in particular, to enhance the possibilities of backward compatibility.-Werror
and -Wshadow
for all platforms.-Werror
can make the programming process somewhat difficult, since different platforms, compilers and libraries can issue some warnings. I do not think that you want to neglect the development of the customer just because his version of GCC
on a platform with which you have not come across before, attacks with new and new malicious notifications.Wstrict-overflow -fno-strict-aliasing
.-fno-strict-aliasing
, or you can work with objects exclusively in the form in which they were created. Since C programming involves the use of different pseudonyms, it is better to choose -fno-strict-aliasing
, unless it is a question of the need to control the entire source tree.Clang
from sending warnings that you use, yes, yes, the appropriate syntax, just add -Wno-missing-field-initializers
.GCC 4.7.0
and later, this strange warning has been eliminated.LTO
performs “analysis and optimization of the source as part of problems with compilation units”, creating annotations for object files in the form of intermediate notes, which makes it possible to make appropriate adjustments to the source data in the process of merging objects.LTO
can significantly slow down the merge process. Rescues make -j
, but only if the development consists of independent, not related to each other end-users (.a, .so, .dylib, executable test files, executable applications, etc.).clang gcc
took care of creating an auxiliary LTO
, which you can take advantage of by adding -flto
to the list of commands when compiling objects and final merging of library / program elements. However, LTO
still needs an eye and an eye. Sometimes, if the program uses code that is not run directly, but through additional libraries, LTO
can eliminate the corresponding functions or code, because during the general analysis, the utility detects that they are not used, which means that they are not needed in the final version of the product.Arch
-march=native
-msse2 -msse4.2
may be needed if you are working with options prepared by other developers.char, int, short, long unsigned
in the new code, here are some bugs.#include <stdint.h>
and only then choose standard data types.int8_t, int16_t, int32_t, int64_t
are signed integers;uint8_t, uint16_t, uint32_t, uint64_t
are unsigned integers;float
- 32-bit floating point standard;double
- 64-bit floating point standard.char
. Usually, in the C programming language, the char
command is not only named, but also used incorrectly.char
command to refer to "byte", even when unsigned byte operations are performed. Much more correctly for individual unsigned byte / octet values ​​specify uint8_t
, and for a sequence of unsigned byte / octet values ​​select uint8_t
*.int
, which their cold frozen fingers will tell you. It is worth noting that it is technically impossible to program correctly if the sizes of data types change as they like.int
16-bit, on others - 32-bit, and also tested the problem areas on 16 and 32 bits for each use case of int
, you can continue in the same vein.char
”char
command can be accessed in 2016 is if the selected API requests a char
(for example, strncat, printf'ing "%s", ...
) or if you specify strings for reading only (for example, const char *hello = "hello";
), because in the C programming language, string literals ("hello") look like char
[].char
is still used, even if you have to work with multibyte sequences like const char *abcgrr = u8"abc";
.{int,long,etc}
”unsigned
in your code. 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 restrict uint64_t
to a simple uint64_t
? Files of the type <stdint.h> are much more specific and precise in meaning, they better convey the intentions of the author, are compact - which is important for operation and readability.long
, without them all the mathematics will be covered!"uintptr_t
, it is specified by the files <stdint.h>
. At the same time, it is important to note that the very useful ptrdiff_t
is defined by stddef.h
.long diff = (long)ptrOld - (long)ptrNew;
ptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew;
printf("%p is unaligned by %" PRIuPTR " bytes.\n", (void *)p, ((uintptr_t)somePtr & (sizeof(void *) - 1)));
long
, and on the 64th platform, 64-bit!”.long
oriented system-dependent data types.intptr_t
, an integer data type responsible for storing the pointer value for your platform.intptr_t
transformed to int32_t
.intptr_t
takes the form int64_t
.intptr_t
is found in the uintptr_t
variant.ptrdiff_t
— it is this data type that allows you to memorize the parameters of the subtracted pointers.uint64_t
, and in fact there is a more efficient technical solution, thanks to which any variable can be used to store all sorts of values. Safe storage of integer data is guaranteed by intmax_t
(or uintmax_t
). You can entrust any intmax_t
value intmax_t
, being sure that the accuracy of the data will not be affected by this. Similarly with unsigned integers delegated by uintmax_t
.size_t
, guaranteed stddef.h
takes first place in the list of favorites.size_t
is something like “an integer value capable of storing huge array indices”, which means it can capture impressive indicators of bias in the program being created.size_t
acts as a result type for the sizeof operator.size_t
has practically the same characteristics as uintptr_t
, and therefore, on 32-bit versions, size_t
transformed into uint32_t
, and on 64-bit uint64_t
- into uint64_t
.ssize_t
, which is a signed size_t
, used as a result type for library functions — in the event of an error, we get 1. (Note: ssize_t
belongs to the POSIX package and is not suitable for Windows).size_t
for arbitrary system-specific sizes, setting the parameters of your own functions? Technically, size_t
is the result type of sizeof
, so any functions that determine the size of a value in the form of a specific number of bytes can take the form size_t
.size_t
is the argument type for malloc, and ssize_t
is the result type for read()
and write()
(except for Windows interfaces, in which ssize_t
not provided and only int is used for result values).long
function, on others - long long
. These macros provide optimal basic format characteristics for various platforms.intptr_t
- "%" PRIdPTRuintptr_t
- "%" PRIuPTRintmax_t
- "%" PRIdMAXuintmax_t
-% PRIUMAXprintf("Local number: %PRIdPTR\n\n", someIntPtr);
printf("Local number: %" PRIdPTR "\n\n", someIntPtr);
void test(uint8_t input) { uint32_t b; if (input > 3) { return; } b = input; }
void test(uint8_t input) { if (input > 3) { return; } uint32_t b = input; }
uint32_t i; for (i = 0; i < 10; i++)
for (uint32_t i = 0; i < 10; i++)
#ifndef PROJECT_HEADERNAME #define PROJECT_HEADERNAME . . . #endif /* PROJECT_HEADERNAME */
#pragma once
#pragma once
notifies the compiler to request a header only once, therefore, you no longer have to write additional lines to protect it. This function is supported by all compilers, and on different platforms, and is much more efficient mechanism than manually entering the header code. uint32_t numbers[64]; memset(numbers, 0, sizeof(numbers));
uint32_t numbers[64] = {0};
struct thing { uint64_t index; uint32_t counter; }; struct thing localThing; void initThing(void) { memset(&localThing, 0, sizeof(localThing)); }
struct thing { uint64_t index; uint32_t counter; }; struct thing localThing = {0};
memset(&localThing, 0, sizeof(localThing))
, since sizeof (localThing) == 16 bytes, even though only 8 + 4 = 12 bytes are available. struct thing { uint64_t index; uint32_t counter; }; static const struct thing localThingNull = {0}; . . . struct thing localThing = {.counter = 3}; . . . localThing = localThingNull;
localThing = (struct thing){0};
uintmax_t arrayLength = strtoumax(argv[1], NULL, 10); void *array[]; array = malloc(sizeof(*array) * arrayLength); / * () * /
uintmax_t arrayLength = strtoumax(argv[1], NULL, 10); void *array[arrayLength]; /* */
arrayLength
– ( ; 4 ). ( ), , , , 99 VLA, malloc
. void processAddBytesOverflow(uint8_t *bytes, uint32_t len) { for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } }
void processAddBytesOverflow(void *input, uint32_t len) { uint8_t *bytes = input; for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } }
uint8_t
. , , , char *
, - . , void *
, , , , , .<stdbool.h>
, true 1, false — 0.int32_t
, 1 0 (, , 1 -1; : 0 – success, 1 — failure? 0 – success, -1 — failure?). void *growthOptional(void *grow, size_t currentLen, size_t newLen) { if (newLen > currentLen) { void *newGrow = realloc(grow, newLen); if (newGrow) { /* */ grow = newGrow; } else { /* , , */ free(grow); grow = NULL; } } return grow; }
/* : * - 'true' newLen > currentLen * - 'true' , '*_grow' * - 'false' newLen <= currentLen */ bool growthOptional(void **_grow, size_t currentLen, size_t newLen) { void *grow = *_grow; if (newLen > currentLen) { void *newGrow = realloc(grow, newLen); if (newGrow) { /* */ *_grow = newGrow; return true; } /* */ free(grow); *_grow = NULL; /* , * 'true' , */ return true; } return false; }
typedef enum growthResult { GROWTH_RESULT_SUCCESS = 1, GROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY, GROWTH_RESULT_FAILURE_ALLOCATION_FAILED } growthResult; growthResult growthOptional(void **_grow, size_t currentLen, size_t newLen) { void *grow = *_grow; if (newLen > currentLen) { void *newGrow = realloc(grow, newLen); if (newGrow) { /* */ *_grow = newGrow; return GROWTH_RESULT_SUCCESS; } /* , , */ return GROWTH_RESULT_FAILURE_ALLOCATION_FAILED; } return GROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY; }
#!/usr/bin/env bash
clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, AllowShortFunctionsOnASingleLine: None, KeepEmptyLinesAtTheStartOfBlocks: false}" "$@"
cleanup-format
):matt@foo:~/repos/badcode% cleanup-format -i *.{c,h,cc,cpp,hpp,cxx}
#!/usr/bin/env bash # : clang-tidy , # . find . \( -name \*.c -or -name \*.cpp -or -name \*.cc \) |xargs -n1 -P4 cleanup-tidy # clang-format , 12 # () . find . \( -name \*.c -or -name \*.cpp -or -name \*.cc -or -name \*.h \) |xargs -n12 -P4 cleanup-format -i
#!/usr/bin/env bash clang-tidy \ -fix \ -fix-errors \ -header-filter=.* \ --checks=readability-braces-around-statements,misc-macro-parentheses \ $1 \ -- -I.
clang-tidy
— . :readability-braces-around-statements
– if/while/for ;misc-macro-parentheses
– , .clang-tidy
– , , , , . , clang-tidy
— , clang-format
, .calloc
. . calloc(object count, size per object)
, #define mycalloc(N) calloc(1, N)
.calloc()
, :calloc()
( , 64 , , , ). « » , « », .calloc()
– , . calloc()
realloc()
, . . realloc()
, memset()
.memset
( )memset
(ptr, 0, len, () ( , ).memset()
— , , ( {0} , ).Source: https://habr.com/ru/post/275685/
All Articles