We are still actively working on the spam issue.

Difference between revisions of "C Help and Discussion"

From InstallGentoo Wiki
Jump to: navigation, search
m (update.)
Line 119: Line 119:
 
=== Standards ===
 
=== Standards ===
  
<code>-std=</code> (valid: c89, c99 c11 c17, c2x) (additional: c90:c89, c18:c17)
+
<code>-std=</code> (valid: c89, c99 c11 c17, c2x) (additional: c90=c89, c18=c17). Any 'c' can be replaced with 'gnu' for GNU extensions, but you can use them anyway and the compiler won't even warn you unless you specify <code>-pedantic</code> or <code>-Wpedantic</code> so don't worry about it too much unless you're looking to maximize compiler portability.
  
 
Determine the language standard. See [https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Standards.html#Standards Language Standards Supported by GCC], for details of these standard versions. This option is currently only supported when compiling C or C++. <code>-std=c99</code> is usually a good choice.
 
Determine the language standard. See [https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Standards.html#Standards Language Standards Supported by GCC], for details of these standard versions. This option is currently only supported when compiling C or C++. <code>-std=c99</code> is usually a good choice.
Line 165: Line 165:
  
 
<code>-Wno-logical-op-parentheses</code>: C has an order precedence of first <code>&&</code> then <code>||</code>. This is however warned against, and at a glance with this knowledge it is much easier to tell the difference between <code>(a && b || c)</code> and <code>(a && (b || c))</code> than enforcing that warning like <code>((a && b) || c)</code> and <code>(a && (b || c))</code>.
 
<code>-Wno-logical-op-parentheses</code>: C has an order precedence of first <code>&&</code> then <code>||</code>. This is however warned against, and at a glance with this knowledge it is much easier to tell the difference between <code>(a && b || c)</code> and <code>(a && (b || c))</code> than enforcing that warning like <code>((a && b) || c)</code> and <code>(a && (b || c))</code>.
 +
 +
 +
<code>-Wshadow</code>: Warns when a block re-declares a variable already declared in a higher block. This is often done intentionally but beginners may wish to be warned on it because the bugs it can cause are particularly subtle and difficult to debug, since the debugger will not tell you what the actual problem is.
 +
 +
 +
<code>-Weverything</code>: Exclusive to clang and only intended for developing clang itself. May require many <code>-Wno-...</code> options to not emit too many false positives, but the perfectionist or anon who simply can't be bothered to run a linter may find it useful.
  
 
=== Optimizing & Release options ===
 
=== Optimizing & Release options ===
Line 178: Line 184:
  
  
<code>-O3</code>: Enables all optimizations specified by <code>-O2</code> and some additional flags.
+
<code>-O3</code>: Enables all optimizations specified by <code>-O2</code> and some additional flags. Can drastically increase binary size due to loop unrolling, particularly with clang. It is somewhat overfitted to x86-64 processors, and other architectures may actually see worse performance with this option even with an appropriate <code>-march</code>.
 +
 
 +
 
 +
<code>-Ofast</code>: Disregard strict standards compliance. Enables all <code>-O3</code> optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on <code>-ffast-math</code>, <code>-fallow-store-data-races</code> and the Fortran-specific <code>-fstack-arrays</code>, unless <code>-fmax-stack-var-size</code> is specified, and <code>-fno-protect-parens</code>. It turns off <code>-fsemantic-interposition</code>. Do not use this if your code relies on specified IEEE float behavior or if you have multiple threads accessing the same data, even with locks. Even if your code doesn't, extensive testing is required to guarantee you can get away with this flag, and the benefit is small even when you can. Avoid if you are a beginner.
  
  
<code>-Ofast</code>: Disregard strict standards compliance. Enables all <code>-O3</code> optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on <code>-ffast-math</code>, <code>-fallow-store-data-races</code> and the Fortran-specific <code>-fstack-arrays</code>, unless <code>-fmax-stack-var-size</code> is specified, and <code>-fno-protect-parens</code>. It turns off <code>-fsemantic-interposition</code>.  
+
<code>-Os</code>: Optimize for size. Enables all <code>-O2</code> optimizations except those that often increase code size. It also enables <code>-finline-functions</code>, causes the compiler to tune for code size rather than execution speed, and performs further optimizations designed to reduce code size. Generally not recommended due to its "hyper-focus" on minimizing the size of a program, even at the expense of obvious, highly beneficial optimizations.
  
  
<code>-Os</code> Optimize for size. Enables all <code>-O2</code> optimizations except those that often increase code size. It also enables <code>-finline-functions</code>, causes the compiler to tune for code size rather than execution speed, and performs further optimizations designed to reduce code size. Generally not recommended due to its "hyper-focus" on minimizing the size of a program, even at the expense of obvious, highly beneficial optimizations.
+
<code>-Oz</code>: Optimize strongly for size. The difference is small for gcc, but clang's <code>-Os is not nearly as aggressive as gcc's, so the difference there is larger.</code>
  
  
Line 190: Line 199:
  
  
<code>-flto=N</code>: LTO provides Link time optimization, try <code>-flto=auto</code> on release builds.  
+
<code>-flto=auto</code>: Perform link-time optimization. There is some confusion about LTO because the supported way to do it has changed a lot over the years but in 2023 with gcc 13 or clang 16 just using <code>-flto=auto</code> should make everything just work. Or, you may need <code>SET(CMAKE_AR  "gcc-ar")</code> and <code>SET(CMAKE_RANLIB "gcc-ranlib")</code> when using cmake because cmake hates using gcc correctly for some reason. And with clang you may need <code>-fuse-ld=lld</code> because Ubuntu is shit. There is quite a bit of difference between how LTO works between gcc and clang. With clang, <code>-flto=auto</code> is an alias for <code>-flto=full</code>, which treats the entire program as one codegen unit at the cost of parallelism. Clang can only parallelize LTO with <code>-flto=thin</code>, which skips many optimizations. This is distinct from the concept of thin LTO objects in gcc which are just object files with only GIMPLE bytecote and no machine code so they require LTO at link-time to be used. Note that the reverse is never true: executables compiled with LTO will happily link against objects and static libraries that lack it with no problem (except foregone performance). In gcc you do not need to worry about this full/thin tradeoff because whole-program analysis is performed unconditionally. In particular, you DO NOT need <code>-flto=1</code>, <code>-fuse-linker-plugin</code>, <code>-ffat-lto-objects</code>, <code>-fwhole-program</code>, or <code>-flto-partition=one</code> to squeeze the maximum performance out: gcc will prove its LTO partitions do not forego any optimizations. The <code>-flto=N</code> option only controls how many jobs the LTO wrapper can accept at once, it does not parallelize the jobs themselves. The jobs will be parallelized by <code>-flto-partition=balanced</code> (the default) in a way that still catches all optimizations. The LTO plugin is loaded automatically when passing <code>-flto</code> to gcc. <code>-fwhole-program</code> manually guarantees what the plugin has already either proven true or false, so it will either not help or will introduce UB. Don't use it. <code>-ffat-lto-objects</code> allows objects and static library archives to be linked against executables that do not themselves use <code>-flto</code>, and should be left off unless you want LTO to silently break and not tell you. It has nothing to do with fat LTO in Rust. Make sure you use the <code>=auto</code> part or you will bottleneck your Makefiles for no reason because you are only allowing one lto-wrapper to run at once despite that fact that the parallelism has no drawbacks. Or, in the case of clang, you are getting parallelism but with big drawbacks so <code>=auto</code> turns it off. At any rate when using a Makefile the number of threads used for LTO with <code>=auto</code> is capped at <code>-j</code> so you don't need two <code>$(nproc)</code> calls. For more information, see [https://documentation.suse.com/sbp/all/html/SBP-GCC-10/index.html#sec-gcc10-lto here]. '''TL;DR''': Just use <code>-flto=auto</code>! Reading all the wrong StackOverflow answers about this is giving me brain damage!
 +
 
 +
 
 +
<code>-march=native</code>: Optimize the code for your processor. This is of limited usefulness with gcc since it barely auto-vectorizes compared to clang, but it will still enable any code paths conditional on architecture support and will take caching and microarchitecture behaviors into consideration.
 +
 
 +
 
 +
<code>-s and strip</code>: strip is primarily useful for release builds, it strips unneeded symbols and can be invoked at compile- or link-time with <code>-s</code> or separately after the fact with <code>strip PROGRAM</code>
 +
 
 +
 
 +
<code>-pipe</code>: Use pipes instead of temporary files when compiling. Saves your SSD for negligible (probably) compile-time memory cost.
  
  
<code>-fwhole-program</code>: provides optimization by the knowledge of 'this is the whole program' whether or not that is true, in principle it provides no advantage.  
+
<code>-f1337-epic-option</code>: Some of these are useful if you are minifying a nolibc executable but in general if you are a beginner or are just looking for performance these will just cause you headaches. Stick to the basics: <code>-O3</code>, <code>-flto=auto</code>, <code>-march=native</code>, <code>-pipe</code>, <code>-s</code>.
You cannot use it for compiling libraries because the symbols need to be externally visibly in those cases, but for making a binary all you should need exposed is _start and main, which this flag preserves.
 
  
  
<code>-s and strip</code>: strip is primarily useful for release builds, it strips unneeded symbols and can be invoked at link-time with <code>-s</code> or separately after the fact with <code>strip PROGRAM</code>
+
'''NOTE''': Optimizers aren't magic. Your code will still be slow if it's shit. See [https://www.youtube.com/watch?v=WDIkqP4JbkE this talk] for just the tip of the iceberg. Don't use linked lists just because they're easy!
  
 
=== Debug options ===
 
=== Debug options ===
Line 220: Line 237:
 
Good coding habits will prevent many such bugs.
 
Good coding habits will prevent many such bugs.
 
There are also tools like ASan and UBSan which help find memory bugs during testing.
 
There are also tools like ASan and UBSan which help find memory bugs during testing.
 +
Large codebases may consider using [https://talloc.samba.org talloc], which provides safe wrappers around the system <code>malloc()</code> and will catch 97% of memory errors at compile- or run-time.
  
 
[[Category:Generals]]
 
[[Category:Generals]]
 
[[Category:Software]]
 
[[Category:Software]]

Revision as of 01:37, 18 July 2023

The C Programming Logo
The Better Logo
/g/ Programming Challenges
The Boys
The Bible

/chad/

C Help and Discussion - or /chad/, is a ongoing general where people discuss all things C.

Show and talk about what your currently working on, or things you've worked on in the past.

Join our IRC channel: #/g/chad at irc.rizon.net

The past threads are enumerated here.

Template

Let's have a C thread. Post what you're working on! Show what you're interested in!

Last thread(s): >>OLD...

Wiki: https://wiki.installgentoo.com/wiki//chad/

IRC: #/g/chad at irc.rizon.net / irc://irc.rizon.net:+6697/%2Fg%2Fchad
Matrix: https://matrix.to/#/#chad:data.haus
Telegram: https://t.me/+itOpQDA2Nbk3ZDZh 

Don't know how to write C? Start here:
K&R PDF: https://files.catbox.moe/80f07b.pdf
KING PDF: https://files.catbox.moe/a875c2.pdf
Modern C: https://files.catbox.moe/xeb93p.pdf

Standards:
C89: https://files.catbox.moe/rfqd57.pdf
C99: https://files.catbox.moe/hs2tqa.pdf

The old matrix was deleted by site admins.

Useful Links

Getting started

Challenge

Books

Standards

Articles

Tools

Building and Build systems

Small Scale

Makefile: It is best to use only for small and simple projects.

Ninja: Not for humans. Fast.

redo: One day... It'll be the best and the universal standard!

Scalable

CMake: CMake is a multi-platform build system that is a modern alternative to Autotools. See this /gedg/ CMake Guide.

Meson: CMake with better syntax.

Autotools: GNU Autotools is a build system that generates Makefiles which comply to GNU Coding Standards, which makes it easier for users of your software to adjust the build process for their needs. The ability to do out-of-tree builds, cross-compilation and staged installs comes out of the box, so you don't have to implement it yourself.

Video guide by David A. Wheeler · Basic Template: >>92441749

Debugging

Compilers

  • GCC: The GNU Compiler Collection (Originally known as the GNU C Compiler...)
  • Clang/LLVM: An "LLVM native" C/C++/Objective-C compiler.
  • MSVC: The Microsoft C/C++ Compiler. Don't know anything about it since using spyware is discouraged.
  • ICC: The Intel C Compiler. Uses LLVM as its backend. Caution should be taken if used due to previous behavior by Intel (Maliciously generating slower code for non-Intel processors)
  • TCC: The Tiny C Compiler, notable for its extremely fast compilation speeds.
  • CompCert: A C99 compiler intended for the compilation of life-critical and mission-critical software and meeting high levels of assurance. Non-free.

Obscure Compilers

  • Turbo C: A free C++ compiler from Borland. It comes with an IDE and debugger.
  • Movfuscator: A single instruction (MOV) C89 compiler created for lulz by the reverse-engineering god Christopher Domas.

Recommended Build Options

Standards

-std= (valid: c89, c99 c11 c17, c2x) (additional: c90=c89, c18=c17). Any 'c' can be replaced with 'gnu' for GNU extensions, but you can use them anyway and the compiler won't even warn you unless you specify -pedantic or -Wpedantic so don't worry about it too much unless you're looking to maximize compiler portability.

Determine the language standard. See Language Standards Supported by GCC, for details of these standard versions. This option is currently only supported when compiling C or C++. -std=c99 is usually a good choice.

On MSVC use /D_CRT_SECURE_NO_WARNINGS to disable warnings regarding the so-called "secure" functions. These aren't widely supported outside of MSVC, and their benefits are questionable. See N1967 for more information.

-ansi: Common alias for -std=c89.

Warnings

GCC Warnings are listed here. For both GCC and Clang, it is generally recommended to use -Werror -Wall -Wextra -Wpedantic.

-Werror: Make all warnings into errors.


-Wall: Enables a large set of warnings, some of which may be undesirable. Very recommended to use.


-Wextra: This enables some extra warning flags that are not enabled by -Wall. Recommended to use.


-Wpedantic: Issue all the warnings demanded by strict ISO C and ISO C++; reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++. For ISO C, follows the version of the ISO C standard specified by any -std option used (Example: -std=c99).


-Wstrict-aliasing=3: Pointer aliasing is when two different pointers can point to the same memory location. Strict aliasing is a set of rules C compilers use to determine when this can happen and when it can't. 3 may be too high for beginners and can spit out some false-positives, 2 is typically a better choice.


-Wwrite-strings: Warns on write to string literals, which have the type of `char []` however, writing to a string literal is Undefined Behavior (UB), so it makes more sense to treat them as `const char []` (even DMR wanted to make string literals const: https://www.lysator.liu.se/c/dmr-on-noalias.html).


-Wvla: Warns if there is a variable length array used in the code. VLAs are either unnecessary because you know the upper bound and are able to do buf[UPPER_BOUND] or are a stack overflow waiting to happen. Some smaller compilers like cproc do not implement VLAs, possibly avoiding use of this option may aid portability.


-Wcast-align=strict: can warn on some newb casting.


-Wstrict-prototypes: Warns on function declarations that lack an explicit set of parameters like f(), which have a specialized purpose in C and only C, where the set arguments are set at the implementation site.


-Wstringop-overflow=4: Warns for calls to string manipulation functions such as memcpy or strcpy that are determined to overflow the destination buffer. At =4 it additionally warns about overflowing any data members, and when the destination is one of several objects it uses the size of the largest of them to decide whether to issue a warning.


-Wno-logical-op-parentheses: C has an order precedence of first && then ||. This is however warned against, and at a glance with this knowledge it is much easier to tell the difference between (a && b || c) and (a && (b || c)) than enforcing that warning like ((a && b) || c) and (a && (b || c)).


-Wshadow: Warns when a block re-declares a variable already declared in a higher block. This is often done intentionally but beginners may wish to be warned on it because the bugs it can cause are particularly subtle and difficult to debug, since the debugger will not tell you what the actual problem is.


-Weverything: Exclusive to clang and only intended for developing clang itself. May require many -Wno-... options to not emit too many false positives, but the perfectionist or anon who simply can't be bothered to run a linter may find it useful.

Optimizing & Release options

GCC optimization options can be seen here.

-O0: Reduce compilation time and make debugging produce the expected results. This is the default.


-O1: Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function. With -O, the compiler tries to reduce code size and execution time, without performing any optimizations that take a great deal of compilation time.


-O2: Optimize even more. GCC performs nearly all supported optimizations that do not involve a space-speed tradeoff. As compared to -O, this option increases both compilation time and the performance of the generated code.


-O3: Enables all optimizations specified by -O2 and some additional flags. Can drastically increase binary size due to loop unrolling, particularly with clang. It is somewhat overfitted to x86-64 processors, and other architectures may actually see worse performance with this option even with an appropriate -march.


-Ofast: Disregard strict standards compliance. Enables all -O3 optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on -ffast-math, -fallow-store-data-races and the Fortran-specific -fstack-arrays, unless -fmax-stack-var-size is specified, and -fno-protect-parens. It turns off -fsemantic-interposition. Do not use this if your code relies on specified IEEE float behavior or if you have multiple threads accessing the same data, even with locks. Even if your code doesn't, extensive testing is required to guarantee you can get away with this flag, and the benefit is small even when you can. Avoid if you are a beginner.


-Os: Optimize for size. Enables all -O2 optimizations except those that often increase code size. It also enables -finline-functions, causes the compiler to tune for code size rather than execution speed, and performs further optimizations designed to reduce code size. Generally not recommended due to its "hyper-focus" on minimizing the size of a program, even at the expense of obvious, highly beneficial optimizations.


-Oz: Optimize strongly for size. The difference is small for gcc, but clang's -Os is not nearly as aggressive as gcc's, so the difference there is larger.


-Og: Optimize debugging experience. Should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience. It is a better choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0. Like -O0, -Og completely disables a number of optimization passes so that individual options controlling them have no effect. Otherwise -Og enables all -O1 optimization flags except for those that may interfere with debugging.


-flto=auto: Perform link-time optimization. There is some confusion about LTO because the supported way to do it has changed a lot over the years but in 2023 with gcc 13 or clang 16 just using -flto=auto should make everything just work. Or, you may need SET(CMAKE_AR "gcc-ar") and SET(CMAKE_RANLIB "gcc-ranlib") when using cmake because cmake hates using gcc correctly for some reason. And with clang you may need -fuse-ld=lld because Ubuntu is shit. There is quite a bit of difference between how LTO works between gcc and clang. With clang, -flto=auto is an alias for -flto=full, which treats the entire program as one codegen unit at the cost of parallelism. Clang can only parallelize LTO with -flto=thin, which skips many optimizations. This is distinct from the concept of thin LTO objects in gcc which are just object files with only GIMPLE bytecote and no machine code so they require LTO at link-time to be used. Note that the reverse is never true: executables compiled with LTO will happily link against objects and static libraries that lack it with no problem (except foregone performance). In gcc you do not need to worry about this full/thin tradeoff because whole-program analysis is performed unconditionally. In particular, you DO NOT need -flto=1, -fuse-linker-plugin, -ffat-lto-objects, -fwhole-program, or -flto-partition=one to squeeze the maximum performance out: gcc will prove its LTO partitions do not forego any optimizations. The -flto=N option only controls how many jobs the LTO wrapper can accept at once, it does not parallelize the jobs themselves. The jobs will be parallelized by -flto-partition=balanced (the default) in a way that still catches all optimizations. The LTO plugin is loaded automatically when passing -flto to gcc. -fwhole-program manually guarantees what the plugin has already either proven true or false, so it will either not help or will introduce UB. Don't use it. -ffat-lto-objects allows objects and static library archives to be linked against executables that do not themselves use -flto, and should be left off unless you want LTO to silently break and not tell you. It has nothing to do with fat LTO in Rust. Make sure you use the =auto part or you will bottleneck your Makefiles for no reason because you are only allowing one lto-wrapper to run at once despite that fact that the parallelism has no drawbacks. Or, in the case of clang, you are getting parallelism but with big drawbacks so =auto turns it off. At any rate when using a Makefile the number of threads used for LTO with =auto is capped at -j so you don't need two $(nproc) calls. For more information, see here. TL;DR: Just use -flto=auto! Reading all the wrong StackOverflow answers about this is giving me brain damage!


-march=native: Optimize the code for your processor. This is of limited usefulness with gcc since it barely auto-vectorizes compared to clang, but it will still enable any code paths conditional on architecture support and will take caching and microarchitecture behaviors into consideration.


-s and strip: strip is primarily useful for release builds, it strips unneeded symbols and can be invoked at compile- or link-time with -s or separately after the fact with strip PROGRAM


-pipe: Use pipes instead of temporary files when compiling. Saves your SSD for negligible (probably) compile-time memory cost.


-f1337-epic-option: Some of these are useful if you are minifying a nolibc executable but in general if you are a beginner or are just looking for performance these will just cause you headaches. Stick to the basics: -O3, -flto=auto, -march=native, -pipe, -s.


NOTE: Optimizers aren't magic. Your code will still be slow if it's shit. See this talk for just the tip of the iceberg. Don't use linked lists just because they're easy!

Debug options

Generally -Og -g, use -ggdb instead of -g if you intend to use GNU Debugger. -fsanitize=... has many useful features described in The GCC PDF, you however cannot combine some directives with any debugger.

Tools like Valgrind, Splint may also help you debug and improve your code.

Diagnostic options

Consider -fno-diagnostics-show-caret for GCC or -fno-caret-diagnostics for Clang to reduce the number of lines per actual error in the compiler output.

C Misconceptions

C is too small of a language to be useful!

While C is a relatively small language, it provides enough facilities to create anything you can imagine. It's no secret that most interpreted languages like Perl, Python, Lua, and countless Lisps/Schemes/Forths are implemented in C. Anything you can implement in the aforementioned languages, can also be implemented in C. This could be said about many small languages which aren't usable at all, but C provides enough tools of abstraction to be useful in projects of any scale, from /usr/bin/true to /boot/vmlinuz.

On the other hand, C's simplicity makes it much easier to learn the whole language. Anyone with previous programming experience can learn the entirety of C in just a few weeks. After learning the language itself, one spends the rest of their C programming career figuring out the best way to apply it. This is more productive, as you're gaining actual CS knowledge and not focusing on superficial things like a particular language's syntax/implementation details.

C has no package manager!

C has many, many package managers, one for every GNU/Linux Distribution. Language-specific package managers tend to be a bad idea anyway

C's lack of memory safety leads to buggy programs!

Good coding habits will prevent many such bugs. There are also tools like ASan and UBSan which help find memory bugs during testing. Large codebases may consider using talloc, which provides safe wrappers around the system malloc() and will catch 97% of memory errors at compile- or run-time.