mirror of
https://github.com/libretro/cpp-cheat.git
synced 2025-04-13 16:50:26 +00:00
709 lines
17 KiB
C
709 lines
17 KiB
C
/* # Operators */
|
|
|
|
#include "common.h"
|
|
|
|
int* int_ptr_func_int_ptr(int *ip) {
|
|
(*ip)++;
|
|
return ip;
|
|
}
|
|
|
|
int int_func_int(int i) {
|
|
return i;
|
|
}
|
|
|
|
int main() {
|
|
/*
|
|
# Arithmetic operators
|
|
|
|
Always be on the lookout for overflows. Rockets have fallen because of them.
|
|
*/
|
|
{
|
|
/*
|
|
# Sum
|
|
|
|
# +
|
|
*/
|
|
{
|
|
/* Basic example. */
|
|
assert((1 + 2) == 3);
|
|
|
|
/* On overflow, deterministic wrap for unsigned integer types. */
|
|
{
|
|
unsigned char i;
|
|
/*i = UCHAR_MAX + (char)1;*/
|
|
/*assert(i == 0);*/
|
|
}
|
|
|
|
#ifdef UNDEFINED_BEHAVIOUR
|
|
/*
|
|
On overflow, undefined behaviour signed types.
|
|
|
|
http://stackoverflow.com/questions/3948479/integer-overflow-and-undefined-behavior
|
|
*/
|
|
{
|
|
char i;
|
|
i = CHAR_MAX + 1;
|
|
printf("CHAR_MAX + 1 = %x\n", i);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
Detect overflow:
|
|
http://stackoverflow.com/questions/199333/best-way-to-detect-integer-overflow-in-c-c
|
|
*/
|
|
}
|
|
|
|
/*
|
|
# Multiplication
|
|
|
|
# *
|
|
*/
|
|
{
|
|
assert((2 * 3) == 6);
|
|
|
|
/* Unsigned multiplication does modulo: */
|
|
{
|
|
unsigned char uc = 255;
|
|
uc *= 2;
|
|
assert(uc == 254);
|
|
}
|
|
|
|
#ifdef UNDEFINED_BEHAVIOUR
|
|
/* Undefined behaviour because signed. */
|
|
{
|
|
char c = CHAR_MAX;
|
|
c *= 2;
|
|
printf("CHAR_MAX * 2 = %x\n", c);
|
|
}
|
|
#endif
|
|
/*
|
|
Detect overflow:
|
|
http://stackoverflow.com/questions/1815367/multiplication-of-large-numbers-how-to-catch-overflow
|
|
*/
|
|
|
|
}
|
|
|
|
/*
|
|
# Division
|
|
|
|
Division is the most complex of the basic operations.
|
|
|
|
Integer division and floating point division are different
|
|
operations, which translate to different CPU instructions!
|
|
|
|
Remember that if an operation involves a floating point and an integer,
|
|
C first casts the integer type to a floating point type, then does
|
|
the floating point operation.
|
|
|
|
Division by `0` is undefined behaviour. On Linux it raises SIGFPE.
|
|
But note that handling the SIGFPE returns to just before the division. TODO check + example.
|
|
|
|
# INT_MIN / -1
|
|
|
|
`INT_MIN / -1` is undefined in 2's complement,
|
|
and 2's complement is explicitly said to be compliant to the C
|
|
integer representation standard.
|
|
*/
|
|
{
|
|
assert((4 / 2) == 2);
|
|
|
|
/* integer division */
|
|
assert((1 / 2) == 0);
|
|
|
|
/* floating poitn division */
|
|
assert((1.0 / 2.0) == 0.5);
|
|
|
|
/*
|
|
floating poitn division. `1` is cast to `double` point,
|
|
according to the usual arithmetic conversions.
|
|
*/
|
|
assert((1 / 2.0) == 0.5);
|
|
|
|
/* Same as above. */
|
|
assert((1 / (double)2) == 0.5);
|
|
}
|
|
|
|
/* # Unary minus */
|
|
{
|
|
/*
|
|
Unary minus can overflow for the smallest negative number.
|
|
|
|
TODO find quote.
|
|
*/
|
|
{
|
|
#ifdef UNDEFINED_BEHAVIOUR
|
|
printf("-INT_MIN = %x\n", -INT_MIN);
|
|
/* Just to compare. */
|
|
printf("INT_MIN = %x\n", INT_MIN);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
Unary minus on unsigned is well defined and modulo wraps.
|
|
|
|
http://stackoverflow.com/questions/8026694/c-unary-minus-operator-behavior-with-unsigned-operands
|
|
|
|
6.2.5/9 says: A computation involving unsigned operands can never overflow,
|
|
because a result that cannot be represented by the resulting unsigned integer type
|
|
is reduced modulo the number that is one greater than the largest value
|
|
that can be represented by the resulting type.
|
|
*/
|
|
{
|
|
assert(-1u == UINT_MAX);
|
|
}
|
|
}
|
|
|
|
/*
|
|
# Remainder
|
|
|
|
# %
|
|
|
|
a%b = a - (a/b)*b
|
|
|
|
# Modulus
|
|
|
|
It is *not* the mathematical modulus, as it gives different results for negative values.
|
|
|
|
It is the mathematical remainder.
|
|
|
|
http://stackoverflow.com/questions/11720656/modulo-operation-with-negative-numbers
|
|
*/
|
|
{
|
|
assert((-4 % 3) == -1);
|
|
assert((-3 % 3) == 0);
|
|
assert((-2 % 3) == -2);
|
|
assert((-1 % 3) == -1);
|
|
assert(( 0 % 3) == 0);
|
|
assert(( 1 % 3) == 1);
|
|
assert(( 2 % 3) == 2);
|
|
assert(( 3 % 3) == 0);
|
|
assert(( 3 % 3) == 0);
|
|
assert(( 4 % 3) == 1);
|
|
assert(( 5 % 3) == 2);
|
|
assert(( 6 % 3) == 0);
|
|
|
|
assert((-3 % -3) == 0);
|
|
assert((-2 % -3) == -2);
|
|
assert((-1 % -3) == -1);
|
|
assert(( 0 % -3) == 0);
|
|
assert(( 1 % -3) == 1);
|
|
assert(( 2 % -3) == 2);
|
|
assert(( 3 % -3) == 0);
|
|
assert(( 4 % -3) == 1);
|
|
|
|
#ifdef UNDEFINED_BEHAVIOUR
|
|
/*assert((1 % 0) == 0);*/
|
|
#endif
|
|
}
|
|
|
|
/* # Comparison operators */
|
|
{
|
|
assert((1 == 1) == 1);
|
|
assert((0 == 1) == 0);
|
|
|
|
assert((0 > 1) == 0);
|
|
assert((0 > 0) == 0);
|
|
assert((0 > -1) == 1);
|
|
assert((0 < 1) == 1);
|
|
assert((0 < 0) == 0);
|
|
assert((0 < -1) == 0);
|
|
|
|
assert((0 >= 1) == 0);
|
|
assert((0 >= 0) == 1);
|
|
assert((0 >= -1) == 1);
|
|
assert((0 <= 1) == 1);
|
|
assert((0 <= 0) == 1);
|
|
assert((0 <= -1) == 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
# Boolean operators
|
|
|
|
The boolean operators treat all integers as:
|
|
|
|
- 0: false
|
|
- != 0: true
|
|
|
|
The output of the boolean operators is always either 0 or 1.
|
|
*/
|
|
{
|
|
/*
|
|
# !
|
|
|
|
# Negation
|
|
*/
|
|
{
|
|
assert((!0) == 1);
|
|
assert((!1) == 0);
|
|
assert((!2) == 0);
|
|
assert((!-1) == 0);
|
|
|
|
/*
|
|
`x == 0` is equivalent to `!x`.
|
|
|
|
But its likely more readable to use `== 0` when doing comparisons,
|
|
and to leave `!x` just for boolean operations.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
# ||
|
|
|
|
# or
|
|
*/
|
|
assert((0 || 0) == 0);
|
|
assert((0 || 1) == 1);
|
|
assert((1 || 0) == 1);
|
|
assert((1 || 1) == 1);
|
|
|
|
/*
|
|
# &&
|
|
|
|
# and
|
|
*/
|
|
assert((0 && 0) == 0);
|
|
assert((0 && 1) == 0);
|
|
assert((1 && 0) == 0);
|
|
assert((1 && 1) == 1);
|
|
|
|
/*
|
|
# Short circuit evaluation
|
|
|
|
For operators `||`, `&&` and `?`, the second side is only evaluated if needed.
|
|
|
|
On this example:
|
|
|
|
- 1 is evaulated to true
|
|
- || does not need to go any further, so i++ is not evaluated
|
|
|
|
Those operators also define sequence points.
|
|
*/
|
|
{
|
|
int i = 0;
|
|
1 || i++;
|
|
assert(i == 0);
|
|
1 && i++;
|
|
assert(i == 1);
|
|
}
|
|
}
|
|
|
|
/* # Bitwise operators */
|
|
{
|
|
/*
|
|
# ~
|
|
|
|
# NOT bitwise
|
|
*/
|
|
assert((~(char)0x00) == (char)0xFF);
|
|
assert((~(char)0xFF) == (char)0x00);
|
|
|
|
/*
|
|
# &
|
|
|
|
AND bitwise
|
|
|
|
# |
|
|
|
|
OR bitwise
|
|
*/
|
|
{
|
|
assert(((char)0x00 & (char)0x00) == (char)0x00);
|
|
assert(((char)0xFF & (char)0x00) == (char)0x00);
|
|
assert(((char)0xFF & (char)0xFF) == (char)0xFF);
|
|
|
|
/*
|
|
`&` and `|` have lower precedence than `==`!
|
|
|
|
Notorious design choice, since they are analogous to + and * ...
|
|
*/
|
|
{
|
|
assert(!(2 & 0 == 0 ));
|
|
assert(!(2 & (0 == 0)));
|
|
assert( (2 & 0) == 0 );
|
|
}
|
|
|
|
/*
|
|
# Even
|
|
|
|
# Odd
|
|
|
|
# Find if number is even or odd
|
|
|
|
http://stackoverflow.com/questions/160930/how-do-i-check-if-an-integer-is-even-or-odd
|
|
|
|
This is another "application" of `&`.
|
|
|
|
But seems to be as fast as `%`, and is definitely less readable.
|
|
*/
|
|
{
|
|
assert((3 & 1) == 1);
|
|
assert((4 & 1) == 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
# ||
|
|
|
|
# OR bitwise
|
|
*/
|
|
assert(((char)0x00 | (char)0x00) == (char)0x00);
|
|
assert(((char)0xFF | (char)0x00) == (char)0xFF);
|
|
assert(((char)0xFF | (char)0xFF) == (char)0xFF);
|
|
|
|
/*
|
|
# ^
|
|
|
|
# XOR bitwise
|
|
*/
|
|
assert(((char)0x00 ^ (char)0x00) == (char)0x00);
|
|
assert(((char)0xFF ^ (char)0x00) == (char)0xFF);
|
|
assert(((char)0xFF ^ (char)0xFF) == (char)0x00);
|
|
|
|
/*
|
|
# bitmask
|
|
|
|
The major aplication of bitwise operators it making masks to:
|
|
|
|
- set: MASK &
|
|
- reset
|
|
- toggle
|
|
- retrieve
|
|
|
|
bits from unsigned integer fields.
|
|
|
|
These exist to allow to use one bit to store one bit,
|
|
because the minimal addressable unit on computers is 8 bits.
|
|
|
|
While such operators exist in almost all languages,
|
|
they are much more common in low level languages like C
|
|
where optimization is more present.
|
|
|
|
Only work because C fixes the binary representation of unsigned integers.
|
|
*/
|
|
|
|
/*
|
|
# <<
|
|
|
|
# >>
|
|
|
|
# Shift operators
|
|
|
|
Low level bit shifting.
|
|
|
|
For the right input, the result would
|
|
depend on which integer representation is being used,
|
|
which is not fixed by the C standard.
|
|
*/
|
|
{
|
|
assert((1u << 0u) == 1u);
|
|
assert((1u << 1u) == 2u);
|
|
assert((1u << 2u) == 4u);
|
|
assert((1u << 3u) == 8u);
|
|
|
|
assert((8u >> 0) == 8u);
|
|
assert((8u >> 1) == 4u);
|
|
assert((8u >> 2) == 2u);
|
|
assert((8u >> 3) == 1u);
|
|
assert((8u >> 4) == 0u);
|
|
assert((5u >> 1) == 2u);
|
|
|
|
/* Negative operands */
|
|
{
|
|
/* TODO undefined or implementation defined? */
|
|
printf("-1 << 1u = %d\n", -1 << 1u);
|
|
#ifdef UNDEFINED_BEHAVIOUR
|
|
/* http://stackoverflow.com/questions/4945703/left-shifting-with-a-negative-shift-count */
|
|
/*printf("2u << -1 = %d\n", 2u << -1);*/
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
# Binary operator on floating point numbers
|
|
|
|
Fun, but not possible.
|
|
|
|
http://stackoverflow.com/questions/1723575/how-to-perform-a-bitwise-operation-on-floating-point-numbers
|
|
*/
|
|
{
|
|
/*1.2 << 1;*/
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
# assign
|
|
*/
|
|
{
|
|
{
|
|
int i = 0;
|
|
i = 1;
|
|
assert(i == 1);
|
|
}
|
|
|
|
/*
|
|
= returns rvals
|
|
*/
|
|
{
|
|
int i;
|
|
assert((i = 1) == 1);
|
|
|
|
/*
|
|
This is why this works (and probably why it is made behave like this.
|
|
*/
|
|
{
|
|
int i, j, k;
|
|
i = j = k = 1;
|
|
/*i = (j = (k = 1));*/
|
|
assert(i == j && j == k && k == 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
# self assign initialization
|
|
|
|
Good old undefined behaviour through innocent statements.
|
|
|
|
<http://stackoverflow.com/questions/11186261/why-is-int-i-i-legal>
|
|
*/
|
|
{
|
|
int self_assign_init = self_assign_init;
|
|
printf("self_assign_init = %d\n", self_assign_init);
|
|
}
|
|
|
|
/*
|
|
# lvalue
|
|
|
|
Something that can be on the left side of an assign, such as a variable.
|
|
|
|
Every lvalue is a rvalue.
|
|
|
|
# rvalue
|
|
|
|
Something that can only be used on the right side of an assign,
|
|
but not on the left side.
|
|
*/
|
|
{
|
|
/*
|
|
In C, assign does not return lvalues.
|
|
|
|
In C++ it does.
|
|
*/
|
|
{
|
|
int i = 0, j = 1, k = 2;
|
|
/*(i = j) = k;*/
|
|
}
|
|
|
|
/*
|
|
Function returns are rvalues.
|
|
|
|
In C++, this has an exception: functions that return references return lvalues
|
|
*/
|
|
{
|
|
/*int_func_int(1) = 1;*/
|
|
/*struct_func().i = 1;*/
|
|
}
|
|
|
|
/*
|
|
A dereferenced pointer becomes an lvalue.
|
|
*/
|
|
{
|
|
int i = 0;
|
|
(*int_ptr_func_int_ptr(&i)) = 2;
|
|
assert(i == 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
# Increment
|
|
|
|
# Pre-increment vs post-increment
|
|
|
|
<http://stackoverflow.com/questions/24886/is-there-a-performance-difference-between-i-and-i-in-c>
|
|
|
|
Which is faster?
|
|
|
|
- in c, equal
|
|
- in c++, ++i potentially if i is a complex object
|
|
|
|
# Why the increment operator exits
|
|
|
|
Why it exists if equivalent to x=x+1?
|
|
|
|
Because there is an x86 instruction for that
|
|
|
|
Why?
|
|
|
|
- because it takes less program memory `inc eax`, instead of `sum eax,1`
|
|
- and is a *very* common instruction
|
|
|
|
What about +=, -=, etc. ?
|
|
|
|
Same thing: `ax = ax + bx` == `sum ax,bx`
|
|
*/
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
assert(i++ == 0);
|
|
assert(i == 1);
|
|
|
|
i = 0;
|
|
assert(++i == 1);
|
|
assert(i == 1);
|
|
|
|
i = 1;
|
|
assert(i-- == 1);
|
|
assert(i == 0);
|
|
|
|
i = 1;
|
|
assert(--i == 0);
|
|
assert(i == 0);
|
|
|
|
/*
|
|
Also works for floating point,
|
|
although the usage is much less common.
|
|
*/
|
|
double f = 0.5;
|
|
assert(f++ == 0.5);
|
|
assert(f == 1.5);
|
|
}
|
|
|
|
/*
|
|
Composite operators
|
|
|
|
Do an operation and an assign at the same time.
|
|
|
|
Exist for many operators.
|
|
|
|
Why do they exist? Assemby support probably,
|
|
as many assembly operations overwrite one of the operands.
|
|
*/
|
|
{
|
|
int i;
|
|
|
|
i = 0;
|
|
assert((i += 1) == 1);
|
|
assert(i == 1);
|
|
|
|
i = 1;
|
|
assert((i -= 1) == 0);
|
|
assert(i == 0);
|
|
|
|
i = 1;
|
|
assert((i *= 2) == 2);
|
|
assert(i == 2);
|
|
|
|
i = 2;
|
|
assert((i /= 2) == 1);
|
|
assert(i == 1);
|
|
|
|
i = 3;
|
|
assert((i %= 2) == 1);
|
|
assert(i == 1);
|
|
|
|
i = 0xFF;
|
|
assert((i &= (char)0x00) == (char)0x00);
|
|
assert((char)i == (char)0x00);
|
|
|
|
/* Same for others bitwise, except ~= which does not exist. */
|
|
{
|
|
unsigned char i = 0xFF;
|
|
i = ~i;
|
|
/* ? */
|
|
/*i~=;*/
|
|
assert((i & 0xFF) == 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
# Ternary operator
|
|
|
|
# Question mark
|
|
|
|
# ?
|
|
|
|
Called ternary operator since it is the only operator that
|
|
takes 3 inputs.
|
|
|
|
It seems that the only use for the ternary operator is writing less,
|
|
so it is completely redundant with and if else:
|
|
http://stackoverflow.com/questions/758849/the-ternary-conditional-operator-in-c
|
|
*/
|
|
{
|
|
assert((1 < 2 ? 3 : 4) == 3);
|
|
assert((1 > 2 ? 3 : 4) == 4);
|
|
|
|
/* The ternary operator can also yield lvalues. */
|
|
{
|
|
int x = 0, y = 1, *xp = &x, *yp = &y;
|
|
*(1 ? xp : yp) = 10;
|
|
assert(x == 10);
|
|
}
|
|
|
|
/* The possible to initialize consts with the ternary operator. */
|
|
{
|
|
const int i = 0 ? 1 : 2;
|
|
char *s = 0 ? "a" : "b";
|
|
}
|
|
}
|
|
|
|
/*
|
|
# Comma operator
|
|
|
|
Obscure and almost useless C operator.
|
|
*/
|
|
{
|
|
/*
|
|
Commas here are part of the declarator sequence,
|
|
just like in functions calls/defs. They are not
|
|
comma operators!
|
|
*/
|
|
|
|
int i=0, a=1, b=2, c=3;
|
|
|
|
/*
|
|
ignores values on left
|
|
takes only last value on right
|
|
|
|
BAD: operations on left has no effect
|
|
*/
|
|
|
|
assert((i = 0, 1 ) == 1);
|
|
assert((i = 0, i = 1, 2) == 2);
|
|
|
|
/*
|
|
assign has precedence over comma
|
|
BAD: operation on right has no effect
|
|
*/
|
|
{
|
|
i = 2;
|
|
(i = 0), 1;
|
|
i = 0, 1;
|
|
assert(i == 0);
|
|
}
|
|
|
|
/* ERROR */
|
|
/* declaration int j does not return a value */
|
|
/*int j=0, 1;*/
|
|
|
|
/* operation on left comes first */
|
|
{
|
|
i=2;
|
|
assert((i=0, i) == 0);
|
|
i=2;
|
|
assert((i=0, i++, i) == 1);
|
|
}
|
|
|
|
/* must be parenthesis protected when passesd as function argument */
|
|
/* to differentiate from argument separtor comma */
|
|
{
|
|
int i = 0;
|
|
assert(int_func_int((i++, i)) == 1);
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|