"Undefined behavior" in the instructions?

They say that with UB, the program can do whatever it wants.

But if I have UB in one expression, for example

signed char a = 0x40; a <<= 2; 

or perhaps even an unused (!) array of variable length of zero size:

 int l = 0; char data[l]; 

is this permissible in any case, since only the result is undefined, or is it "bad", nevertheless?

I especially like these situations:

 signed char a = 0x40; a <<= 2; switch (state) { case X: return case Y: do something with a; break; case Z: do something else with a; break; } 

Suppose case X covers the case where the value of a is undefined, while other cases use this case. This would simplify things if I were allowed to calculate it the way I want and make a distinction later.

Another situation is that I talked about other days :

 void send_stuff() { char data[4 * !!flag1 + 2 * !!flag2]; uint8_t cursor = 0; if (flag1) { // fill 4 bytes of data into &data[cursor] cursor += 4; } if (flag2) { // fill 2 bytes of data into &data[cursor] cursor += 2; } for (int i=0; i < cursor; i++) { send_byte(data[i]) } } 

If both flags are not set, I have an array of "undefined" data with a length of 0. But since I do not read and write to it, I do not understand why and how it can damage ...

+6
source share
3 answers

Undefined behavior means that it is not defined by the C specification. It can be very well defined (or partially defined) for a particular compiler.

Most compilers define unsigned shift behavior.

Most compilers determine if arrays of zero length are allowed.

Sometimes you can change bahaviour using compiler flags, for example --pedantic or flags that handle all warnings as errors.

So the answer to your question is:

It depends on the compiler. You need to check the documentation for your specific compiler.

Can you rely on a specific result when you use something that is UB according to the C standard?

It depends on what you code. If this is the code for a specific embedded system, where the likelihood that something will ever be transferred to another place is low, then, in any case, rely on UB if it gives a large income. But it is best to avoid UB whenever possible.

Edit:

is this permissible in any case, since only the result is undefined, or is it "bad", nevertheless?

Yes (only the result of undefined is true in practice, but theoretically, the compiler manufacturer can terminate the program without violating the C specification), and yes, this is bad, nevertheless (since this requires additional tests to ensure that the behavior remains unchanged after changes are made )

If the behavior is unspecified, you can observe what behavior you get. Best if you checked the generated assembly code.

You need to know that behavior can change if you change something. Changes that may change behavior include, but are not limited to, changes to the optimization level and the application of updates or patches to the compiler.

The people who write compilers are usually rational people, which means that in most cases the program will behave as it was easier for the compiler developer.

The best practice is still to avoid UB whenever possible.

+4
source

You mislead undefined and implementation-specific behavior.

When transferring a value to more bits than it is, an implementation is defined. This will have an effect, and you will need to read your compiler documentation. For example, on some architectures this may not affect; on others, you will be left with zero. An implementation defined by behavior is not portable between versions of the architecture or compiler, does not require diagnostics, but will be consistent between runs.

However, declaring an array with size 0 is undefined behavior. The compiler is free to optimize based on the fact that you are not doing anything like this, and create code that does not work when you do it. The compiler can do anything if you invoke undefined behavior, and perhaps your program will work today, not tomorrow, or will work until you add another line somewhere else in the program or ... .

Undefined means - not defined. There is no run in an attempt to determine how it will behave or depending on the results of the specified behavior.

+1
source

In the context of your send_stuff function, the compiler can optimize cursor computation:

 uint8_t cursor = flag1 ? 4 + 2 * !!flag2 : 2; 

Although this gives a different result for the cursor when both flags1 and flag2 are 0, this is fine, because in any case it will lead to undefined behavior, so in this case it is allowed to do what it needs.

This is a completely machine-independent optimization, so even if you β€œknow” that you will always work in the same architecture with the same compiler, you may find that one day you make an apparently unrelated change, the compiler gets polled for another a decision about what the optimal code looks like and your previous working code suddenly behaves differently.

+1
source

Source: https://habr.com/ru/post/970729/


All Articles