Operator details

Main contents of this chapter:

  1. Introduction to various operators.
  2. Expression evaluation

Operator

1. Arithmetic operator

  • +-*/
    For the division sign (/) operator, both sides are integers, and integer division is performed; Floating point division is performed only if there is a floating point number on both sides.
#include <stdio.h>
int main()
{
	int ret = 9/2;
	double d = 9 / 2;
	printf("%d\n",ret);//4
	printf("%lf\n",d);//4.000000


	double a = 9 / 2.0;
	printf("%lf\n",a);//4.500000

	//%- Take mold
	int b = 9 % 2;
	printf("%d\n",b);//1

	//Modulo operators can only be used for integer types
	//int c = 10.0 % 4.0;

	return 0;
}

2. Shift operator

< < shift left operator

>>Shift right operator

The shift operator moves bits

There are three representations of integers: original code, inverse code and complement code
 For positive integers, the original code = Inverse code = Complement
 For negative integers: the original code is a binary sequence written directly according to the positive and negative of numbers (the highest bit is 0, indicating a positive number, and the highest bit is 1, indicating a negative number)
          The inverse code is obtained by inverting the sign bits of the original code
          Complement is inverse+1
 Integers are stored in memory as binary complements.

(1) Shift left operator

int main()
{
	int a = 10;
	//Original code: 00000000 00000000 00000000 00001010
	//Inverse code: 00000000 00000000 00000000 00001010
	//Complement: 00000000 00000000 00000000 00001010

	//< < - shift left operator: discard on the left and fill 0 on the right
	int b = -1;
	//Original code: 10000000 0000000 0000000 00000001
	//Inverse code: 11111111111111111111111111111111110
	//Complement: 11111111111111111111111111111111111111111111

	int c = b << 1;
	//11111111111111111111111111111111110 - complement
	//10000000 00000000 00000000 00000001
	//10000000 0000000 0000000 00000010 - original code
	//c is a signed int, so the value of c is - 2

	return 0;
}

(2) Shift right operator
Shift right operator
1. Logical shift right - discard on the right and fill 0 on the left
2. Arithmetic shift right - discard the right and fill the original symbol bit on the left

int main()
{
	//Shift right operator
	//1. Logical shift right - discard on the right and fill 0 on the left
	//2. Arithmetic shift right - discard the right and fill the original symbol bit on the left

	int a = 5;
	//00000000 00000000 00000000 00000101
	int b = a >> 1;

	//00000000 00000000 00000000 00000010
	printf("%d\n",b);//2

	int c = -1;
	//10000000 00000000 00000000 00000001
	//11111111 11111111 11111111 11111110
	//11111111111111111111111111111111111111111 - complement

	int d = c >> 1;
	//11111111 11111111 11111111 11111111
	printf("%d\n",d);//-1

	//The current VS2019 compiler uses arithmetic shift right
	return 0;
}

warning ⚠: For shift operators, do not move negative digits, which is not defined by the standard. For example:

int num = 10;
num>>-1;//error

float num = 4.5f;
num>>1;//error

3. Bitwise operator

Bitwise operators are:

&    //Bitwise AND
|    //Bitwise OR
^    //Bitwise XOR

Note: their operands must be integers.

int main()
{
	int num1 = 3;
	//00000000 00000000 00000000 00000011

	int num2 = -2;
	//10000000 00000000 00000000 00000010
	//11111111 11111111 11111111 11111101
	//11111111 11111111 11111111 11111110

	int a = num1& num2;
	//00000000 00000000 00000000 00000010
	printf("%d\n",a);//2

	//%d - print in signed form a
	//%u - print a in unsigned form

	int b = num1 | num2;
	//11111111 11111111 11111111 11111111
	printf("%d\n",b);//-1


	int c = num1^ num2;
	//11111111 11111111 11111111 11111101
	//10000000 00000000 00000000 00000010
	//10000000 00000000 00000000 00000011
	printf("%d\n",c);//-3

	return 0;
}

Exercise: you cannot create a temporary variable (the third variable) to exchange two numbers.

//Code 1 - create temporary variable
int main()
{
	int a = 10;
	int b = 20;
	int tmp = 0;
	tmp = a;
	a = b;
	b = tmp;
	printf("a = %d,b = %d\n",a,b);
	return 0;
}
//Code 2 - addition and subtraction: there will be an overflow problem
int main()
{
	int a = 10;
	int b = 20;
	a = a + b;
	b = a - b;
	a = a - b;
	printf("a = %d,b = %d\n",a,b);
	return 0;
}
//Code 3
int main()
{
	int a = 10;
	int b = 20;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("a = %d b = %d\n", a, b);
	return 0;
}

It can be understood as: variable a and variable b generate a password, then decode with password and B to get a assigned to B, and then decode with password and B (original a) to get the original B assigned to a, so as to exchange a and B.

a ^ a = 0
0 ^ a = a
a ^ a ^ b = b
a ^ b ^ a = b

XOR supports commutative law.

Exercise: write code to find the number of an integer stored in binary 1 in memory.

#include <stdio.h>
int main()
{
	int num = -1;
	int i = 0;
	int count = 0;//count
	for(i=0; i<32; i++)
	{
		if( ((num>>i)&1) == 1 )
			count++;
	}
	printf("Number of 1 in binary = %d\n",count);
	return 0;
}

4. Assignment operator

int weight = 120;//weight
weight = 89;//Assign value if you are not satisfied

double salary = 10000.0;
salary = 20000.0;//Assign values using the assignment operator.

//Assignment operators can be used continuously, such as:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//Continuous assignment, not recommended

//It's easier to write this way
x = y+1;
a = x;

5. Compound assignment operator

+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=

int x = 10;
x = x+10;
x += 10;//Compound Assignment 
//The same is true for other operators. It's more concise.

6. Monocular operator

! Logical reverse operation

int main()
{
	printf("%d\n", !2);
	printf("%d\n", !0);
	int a = 10;
	if (a)
	{
		printf("a Not 0\n");
	}

	int b = 0;
	if (!b)
	{
		printf("b Is 0\n");
	}
	return 0;
}

-Negative value
+Positive value

int main()
{
	int a = 10;
	int b = -a;
}

&Get address

int main()
{
	int a = 10;
	int* p = &a;
	
	//Here * p is understood as an lvalue, * p is the concept of space. Put 20 in the space pointed to by p
	//20 is an lvalue and a value
	*p = 20;
	
	//*p is the right value. Assign the value in the space pointed to by p to b
	//b is an lvalue, representing the space b
	int b = *p;
}

Note: & A takes the address of variable a, which refers to the lowest of the four bytes (four addresses) occupied by variable a.

sizeof operator
sizeof is an operator, not a function. It calculates the memory size of variables or types and creates variables, which has nothing to do with what data is stored in memory.

int main()
{
	char arr[10] = "abc";
	printf("%d\n",sizeof(arr));//It has nothing to do with what data is stored in memory
	printf("%d\n",strlen(arr));//The length of the string. Pay attention to whether there is' \ 0 'in memory

	return 0;
}
#include <stdio.h>
int main()
{
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof a);
	//printf("%d\n", sizeof int);// error
	return 0;
}

Note: if sizeof is followed by a type, the parentheses cannot be omitted.

#include <stdio.h>
void test1(int arr[])
{
	printf("%d\n", sizeof(arr));// 4 / 8
}

void test2(char ch[])
{
	printf("%d\n", sizeof(ch));// 4 / 8
}

int main()
{
	int arr[10] = {0};
	char ch[10] = {0};
	printf("%d\n", sizeof(arr));//40
	printf("%d\n", sizeof(ch));//10
	test1(arr);
	test2(ch);
	return 0;
}
int main()
{
	int a = 5;
	short s = 10;
	printf("%d\n",sizeof(s=a+2));//2
	printf("%d\n",s);//10
	return 0;
}

A is an integer and 2 is an integer. Put a+2 into the variable s of short type. If it cannot be put, truncation will occur. Only the lower two bytes of data will be put into the variable s.
Note: the expression in sizeof does not really operate when the program is running, because the value of the expression is completed in the compilation stage. In the compilation stage, s=a+2, so sizeof(s=a+2) is sizeof(s = 7). After the program is running, s=a+2 is not executed, and the value of S is 10.

~Bitwise negation of a number

int main()
{
	int a = 0;
	//00000000 00000000 00000000 00000000

	int b = ~a;
	//11111111 11111111 11111111 11111111
	printf("%d\n",b);//-1
	return 0;
}
int main()
{
	int a = 13;
	//00000000 00000000 00000000 00001101
	a |= (1 << 1);
	//00000000 00000000 00000000 00001101
	//00000000 00000000 00000000 00000010
	//00000000 00000000 00000000 00001111
	printf("%d\n",a);//15

	a &= (~(1<<1));
	//00000000 00000000 00000000 00001111
	//11111111 11111111 11111111 11111101
	//00000000 00000000 00000000 00001101

	printf("%d\n",a);//13
	return 0;
}

– front, rear –
++Front and rear++

//Pre + + and--
int main()
{
	int a = 10;
	int x = ++a;
	//First self increment a, and then use a, that is, the value of the expression is the value after the self increment of A. x is 11.
	int y = --a;
	//First self subtract a, and then use a, that is, the value of the expression is the value after self subtraction of A. y is 10;
	return 0;
}


//Post + + and--
int main()
{
	int a = 10;
	int x = a++;
	//First use a and then increase it, so that the value of x is 10; Then a becomes 11;
	int y = a--;
	//First use a and then subtract from it, so that the value of y is 11; Then a becomes 10;
	return 0;
}

*Indirect access operator (dereference operator)

int main()
{
	int a = 0;
	int*p = &a;
	*p = 20;
	printf("%d\n",a);
	return 0;
}

(type) - cast type

int main()
{
	int num = 3.14;//By default, the floating-point number face value written out is of type double. A warning is given here and the precision is lost
	int n = 3.14f;//This means that 3.14 is a float type
	
	float a = 10.5;
	int b = a;//Implicit type conversion
	int c = (int)a;//Cast type

	printf("%d\n", a);//0 - A is a floating point number, but here read a in the form of% d
	printf("%lf\n", a);//10.500000

	printf("%d\n",b);//10
	printf("%d\n",c);//10

	return 0;
}

7. Relational operators

=
<
<=
!= Used to test "inequality"
==Used to test equality

Relational operators must be used to compare variables of the same type.
Warning: errors caused by careless writing of = = and = during programming. When judging whether a variable and a constant are equal, it is recommended to put the constant to the left of = =.

8. Logical operators

&&Logic and
||Logical or

int main()
{
	int a = 10;
	int b = 0;

	int c = a || b;//Logical or
	printf("%d\n",c);//1

	int d = a && b;
	printf("%d\n",d);//0

	int num1 = 10;
	int num2 = 20;

	if (num1 > 10 && num2 < 30)
	{
		printf("hehe\n");
	}
	else
	{
		printf("haha\n");
	}
	return 0;
}

What is the result of the following code?

#include <stdio.h>
int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	i = a++ && ++b && d++;
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	return 0;
}

The operation results are as follows:

The result of expression a + + is 0, i=0. There is no need to calculate the short-circuit operation later.

#include <stdio.h>
int main()
{
	int i = 0,a=0,b=2,c =3,d=4;
	i = a++||++b||d++;
	printf("a = %d\nb = %d\nc = %d\nd = %d\n", a, b, c, d);
	return 0;
}

The operation results are as follows:

The result of expression a + + is 1, i=1, short circuit operation, and there is no need to calculate later.

9. Conditional operator (ternary operator)

exp1 ? exp2 : exp3

int main()
{
	int a = 10;
	int b = 30;

	int max = 0;
	//max = a > b ? a : b;
	//printf("max = %d\n",max);

	(a > b) ? (max = a) : (max = b);
	printf("max = %d\n", max);

	return 0;
}

10. Comma expression

exp1, exp2, exp3, ...expN

Comma expressions are multiple expressions separated by commas. Comma expression, executed from left to right. The result of the entire expression is the result of the last expression.

//Code 1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1);//comma expression 
//What is c? thirteen

//Code 2
if (a =b + 1, c=a / 2, d > 0)


//Code 3
a = get_val();
count_val(a);
while (a > 0)
{
	//Business processing
	a = get_val();
	count_val(a);
}
//If you use a comma expression, override:
while (a = get_val(), count_val(a), a>0)
{
	//Business processing
}

11. Subscript references, function calls, and structure members

1. Subscript reference operator
There are two operands: array name and index value

int main()
{
	int arr[] = {1,2,3,4,5};
	int i = 0;
	
	//&arr[4] = arr+4;
	//arr[4] = *(arr+4);
	//*(4+arr) = 4[arr];
	
	int i = 0;
	for(i = 0; i <5;i++)
	{
		printf("%p --- %p\n",&arr[i],arr+i);
		//arr+i is equivalent to & arr [i]
	}
}

[] is an operator. Two operands can be interchanged. arr[9] = 9[arr].

2. () function call operator
Accept one or more operands: the first operand is the function name, and the remaining operands are the parameters passed to the function.

#include <stdio.h>
void test1()
{
	printf("hehe\n");
}

void test2(const char *str)
{
	printf("%s\n", str);
}

int main()
{
	test1(); //() as a function call operator, there is only one operand test1.
	test2("hello world.");//() as a function call operator, two operands test2 and "hello world.".
	return 0;
}

3. Visit members of a structure

. Structure variables member name
->Structure pointer - > member name
(* structure pointer) member name

//Define structure type
struct Stu
{
	char name[10];
	int age;
	char sex;
};

void print1(struct Stu s)
{
	printf("full name:%s\n",s.name);
	printf("Age: %d\n",s.age);
	printf("Gender: %c\n",s.sex);
}

int main()
{
	//Define a structure variable
	struct Stu s1 = {"zhangsan",25,'M'};
	print1(s1);

	//s1.name = "lisi";
	return 0;
}

s1.name = “lisi”; The writing method of changing the value of name in this way is wrong, because s.name is the array name, the array name is the address of the first element, and it is a constant address, which cannot be changed and cannot be assigned to the array name. Here, you need to use the strcpy function - string copy.

strcpy(s1.name,"lisi");

Using structure pointers

void print2(struct Stu *ps)
{
	//Structure pointer dereference to get structure variable
	//printf("Name:% s \ n", (* PS) name);
	//printf("age:% d \ n", (* PS) age);
	//printf("gender:% C \ n", (* PS) sex);

	//Using structure pointers
	printf("full name:%s\n",ps->name);
	printf("Age:%d\n", ps->age);
	printf("Gender:%c\n", ps->sex);
}


int main()
{
	//Define a structure variable
	struct Stu s1 = {"zhangsan",25,'M'};
	print2(&s1);
	return 0;
}

Expression evaluation

Learning operators is to evaluate expressions.
The order in which expressions are evaluated is partly determined by the priority and associativity of operators.
Similarly, the operands of some expressions may need to be converted to other types during evaluation.

Implicit type conversion

The so-called implicit type conversion is arithmetic operation. In some cases, we will "secretly" convert some types without our awareness.

The integer arithmetic operation of C is always performed at least with the precision of the default integer type.

integral promotion

To achieve this precision, the characters and short integer operands in the expression are converted to ordinary integers (int type) before use. This conversion is called integer promotion. Integer promotion is one of implicit type conversion.

When will integer upgrade occur?
As long as the operand is of a type with a length less than int, it needs to be promoted to int before calculation.

Significance of integer lifting:
The integer operation of the expression shall be executed in the corresponding operation device of the CPU. The byte length of the operand of the integer operator (ALU) in the CPU is generally the byte length of int and the length of the general register of the CPU. Therefore, even if the two char types are added, they actually have to be converted to the standard length of integer operands in the CPU when executed by the CPU.

General purpose CPU is difficult to directly implement the direct addition of two 8-bit bytes (although there may be such a byte addition instruction in machine instructions). Therefore, all integer values with length less than int in the expression must be converted to int or unsigned int before they can be sent to the CPU for operation.

How to improve integer?
Integer promotion is promoted according to the sign bit of the data type of the variable.

//Shaping and lifting of negative numbers
char c1 = -1;
//-1 Complement: 1111111111111111111111111111111111111111
//There are only 8 bits in the binary bit (complement) of variable c1:
//1111111
//Because char is a signed char
//Therefore, when shaping and lifting, the high supplementary symbol bit is 1
//The result of the promotion is:
//11111111111111111111111111111111
//Positive integer lifting
char c2 = 1;
//There are only 8 bits in the binary bit (complement) of variable c2:
//00000001
//Because char is a signed char
//Therefore, when shaping and lifting, the high supplementary symbol bit is 0
//The result of the promotion is:
//00000000000000000000000000000001

//Unsigned shaping lifting, high complement 0

For an example of integer lifting, see the following code:

int main()
{
	char a = 3;
	//Complement of 3
	//00000000 00000000 00000000 00000011
	//Because a is of char type and occupies 8 bit s, truncation occurs when integer 3 is stored in variable a
	//00000011 - a
	
	char b = 127;
	//Complement of 127
	//00000000 00000000 00000000 01111111
	//Truncation occurred
	//01111111 - b

	char c = a + b;
	//Both a and b are char types, signed bits, and the size is 1 byte. Therefore, integer promotion is required before calculation
	//According to the sign bit of variable a, it is promoted to:
	//00000000 00000000 00000000 00000011
	//According to the sign bit of variable b, it is promoted to:
	//00000000 00000000 00000000 01111111
	//So a+b
	//00000000 00000000 00000000 00000011
	//           +
	//00000000 00000000 00000000 01111111
	//           =
	//00000000 00000000 00000000 10000010
	//Because c is a char type, it is truncated
	//10000010 - c
	
	printf("%d\n",c);
	//%d - Print c as signed int
	//c first, promote integer to int type
	//c is a signed char, and integer promotion is filled with sign bits
	//11111111111111111111111111111 10000001 - complement
	//10000000 00000000 00000000 01111101
	//10000000 0000000 01111110 - original code 
	//%d-form printing -- 126
	
	return 0;
}

The following code:

int main()
{
	char a = 0xb6;
	//0xb6:00000000 00000000 00000000 10110110  
	//Truncation occurs when saving into a,
	//10110110 is stored in a
	//Then a is the signed char, and the highest 1 is the sign bit

	printf("%d\n", a);
	//Print a with% d, integer promotion is required
	//11111111 11111111111 10110110 - complement
	//10000000 00000000 00000000 01001001
	//10000000 0000000 0000000 01001010 - original code
	//a -  -(2^6+2^3+2) = -74

	printf("%d\n", 0xb6);//182

	short b = 0xb600;
	//0xb600:00000000 00000000 10110110 00000000
	//Truncation occurred while saving in b
	//b is 10110110 00000000
	//Then b is signed short, and the highest 1 is the sign bit
	printf("%d\n", b);
	//b integer lift required
	//11111111 11111111 10110110 00000000 - complement
	//10000000 00000000 01001001 11111111
	//10000000 0000000 01001010 0000000 - original code
	//-18944


	printf("%d\n", 0xb600);//46592

	int c = 0xb6000000;
	//0xb6000000:10110110 00000000 00000000 00000000

	printf("%d\n", c);
	//c itself is an int type, so there is no integer promotion
	printf("%d\n", 0xb6000000);

	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");//c
	return 0;
}

Because variables a and b need integer promotion and variable c does not need integer promotion, c == 0xb6000000.

In the following example, the expression + c and expression in the sizeof operator! There are operators in c to participate in the operation. Because c is of char type, integer promotion must be carried out before participating in the operation;

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));//1. No operation is required 
	printf("%u\n", sizeof(+c));//4. Participate in the operation
	printf("%u\n", sizeof(!c));//4. Participate in the operation
	return 0;
}

Arithmetic conversion

If the operands of an operator are of different types, the operation cannot be performed unless one of the operands is converted to the type of the other operand. The following hierarchy is called ordinary arithmetic conversion

If the type of an operand is low in the above list, it must be converted to the type of another operand before performing the operation. (the lower ranked type shall be the higher ranked type)
Warning: but the arithmetic conversion should be reasonable, otherwise there will be some potential problems.

int main()
{
	float f = 5.1434;//Floating point storage f
	printf("%d\n",f);//(1) Print f as an integer - 2147483648

	int num = f;//(2) Implicit conversion, there will be precision loss
	printf("%d\n",num);//5
	return 0;
}

Properties of the operator

There are three factors that affect the evaluation of complex expressions.

  1. operator precedence
  2. Associativity of operators
  3. Whether to control the evaluation sequence (short circuit operation).

Which of the two adjacent operators to execute first (the adjacent operator will talk about the priority, and the one with the higher priority will be calculated first)? It depends on their priority.

If the two have the same priority, it depends on their combination.

Operator precedence
The priority in the following table is from high to low




Problem expression
Sometimes, even if you know the priority and associativity of operators, you still can't determine the unique evaluation path of expressions. For example, the following problem expressions:

//Code 1
a*b + c*d + e*f

Code 1 can only ensure that * is calculated earlier than + due to its higher priority than + during calculation, but the priority does not determine that the third * is executed earlier than the first +

//Expression 2
c + --c;

As above, the priority of the operator can only determine whether the operation of self subtraction - precedes the operation of + but we have no way to know whether the left operand of the + operator is evaluated before or after the right operand, so the result is unpredictable and ambiguous.

//Code 3 - illegal expression
int main()
{
	int i = 10;
	i = i-- - --i * ( i = -3 ) * i++ + ++i;
	printf("i = %d\n", i);
	return 0;
}

Expression 3 test results in different compilers: results of illegal expression programs.

//Code 4
int fun()
{
	static int count = 1;
	return ++count;
}
int main()
{
	int answer;
	answer = fun() - fun() * fun();
	printf( "%d\n", answer);
	return 0;
}

There is a problem with this code!
Although the results are the same on most compilers.
However, the above code answer = fun() - fun() * fun(); In, we can only know from the priority of the operator: multiply first and then subtract. However, the call order of the function cannot be determined by the priority of the operator, and the value of answer is uncertain.

//Code 5
#include <stdio.h>
int main()
{
	int i = 1;
	int ret = (++i) + (++i) + (++i);
	printf("%d\n", ret);
	printf("%d\n", i);
	return 0;
}

Under vs compiler: first calculate + + i three times, then + i= 4, ret=4+4+4 = 12
Under gcc compiler: i = 4, ret = 2+3+4 = 10

The same code produces different results under different compilers. Why?
Simply look at the assembly code and you can analyze it clearly
When the first + in this code is executed, whether the third + + is executed or not is uncertain, because depending on the priority and associativity of operators, the order of the first + and the third preceding + + cannot be determined. (priority is only discussed for adjacent operators)

If the expression we write cannot determine the unique calculation path through the properties of the operator, there is a problem with this expression.

Keywords: C

Added by shawnyoung on Mon, 03 Jan 2022 19:06:25 +0200