This commit is contained in:
Miloslav Ciz 2023-02-06 20:06:02 +01:00
parent 55d3362bb2
commit 68f77b2118
10 changed files with 68 additions and 23 deletions

View file

@ -42,10 +42,35 @@ int main(void)
Besides being extra careful about writing memory safe code, one needs to also know that **some functions of the standard library are memory unsafe**. This is regarding mainly string functions such as `strcpy` or `strlen` which do not check the string boundaries (i.e. they rely on not being passed a string that's not zero terminated and so can potentially touch memory anywhere beyond); safer alternatives are available, they have an `n` added in the name (`strncpy`, `strnlen`, ...) and allow specifying a length limit.
## Different Behavior Between C And C++
## Different Behavior Between C And C++ (And Different C Standards)
C is **not** a subset of C++, i.e. not every C program is a C++ program (for simple example imagine a C program in which we use the word `class` as an identifier). Furthermore a C program that is at the same time also a C++ program may behave differently when compiled as C vs C++. Of course, all of this may also apply between different standards of C, not just between C and C++.
For portability sake it is good to try to write C code that will also compile as C++ (and behave the same). For this we should know some basic differences in behavior between C and C++.
TODO: specific examples
TODO: specific examples
## Compiler Optimizations
C compilers perform automatic optimizations and and other transformations of the code, especially when you tell it to optimize aggressively (`-O3`) which is a standard practice to make programs run faster. However this makes compilers perform a lot of [magic](magic.md) and may lead to unexpected and unintuitive undesired behavior such as bug or even the "unoptimization of code". { I've seen a code I've written have bigger size when I set the `-Os` flag (optimize for smaller size). ~drummyfish }
Aggressive optimization may firstly lead to tiny bugs in your code manifesting in very weird ways, it may happen that a line of code somewhere which may somehow trigger some tricky [undefined behavior](undefined_behavior.md) may cause your program to crash in some completely different place. Compilers exploit undefined behavior to make all kinds of big brain reasoning and when they see code that MAY lead to undefined behavior a lot of chain reasoning may lead to very weird compiled results. Remember that undefined behavior, such as overflow when adding signed integers, doesn't mean the result is undefined, it means that ANYTHING CAN HAPPEN, the program may just start printing nonsensical stuff on its own or your computer may explode. So it may happen that the line with undefined behavior will behave as you expect but somewhere later on the program will just shit itself. For these reasons if you encounter a very weird bug, try to disable optimizations and see if it goes away -- if it does, you may be dealing with this kind of stuff. Also check your program with tools like [cppcheck](cppcheck.md).
Automatic optimizations may also be dangerous when writing [multithreaded](multithreading.md) or very low level code (e.g. a driver) in which the compiler may have wrong assumptions about the code such as that nothing outside your program can change your program's memory. Consider e.g. the following code:
```
while (x)
puts("X is set!");
```
Normally the compiler could optimize this to:
```
if (x)
while (1)
puts("X is set!");
```
As in typical code this works the same and is faster. However if the variable *x* is part of shared memory and can be changed by an outside process during the execution of the loop, this optimization can no longer be done as it results in different behavior. This can be prevented with the `volatile` keyword which tells the compiler to not perform such optimizations.
Of course this applies to other languages as well, but C is especially known for having a lot of undefined behavior, so be careful.