C language explanation: operator

Operator

classification

arithmetic operator

Shift operators

Bitwise operators

Assignment operator

unary operator

Relational operator

Logical operator

Conditional Operator

comma expression

Subscript references, function calls, and structure members

arithmetic operator
	+   -   *   /   %
//1.
int ret = 9 / 2;
printf("%d", ret);//4
//2.
double ret2 = 9 / 2;
printf("%lf", ret2);//4.000000
//3.
double ret3 = 9 / 2.0;
printf("%lf", ret3);//4.500000

From these two comparisons, we can see that it is not the problem of storage, but that 9 / 2 in the computer is equal to 4. How to save is 4.

To get the correct result, change to 9.0/2 or 9 / 2.0.

  1. /When both operands of are integers, integer division is performed, and floating-point division is performed as long as there are floating-point numbers.
  2. %The two operands of must be integers, and the resulting range is [ 0 , except number − 1 ] [0, divisor-1] Between [0, divisor − 1].
The following shift operators and bit operators are more complex, involving binary bits.
Shift operators
<<   //Shift left operator
>>	 //Shift right operator
Integer storage rule

The shift operator moves the binary bits, the integer stores the binary complement in memory, and the shift operation is also the complement in memory.

Storage of integers in memory:

  1. Positive number:
    • The original code is the same as the inverse code and the complement code
  2. Negative number:
    • Original code: binary sequence
    • Inverse code: the sign bit of the original code remains unchanged, and other bits are reversed by bit
    • Complement: inverse code + 1 +1 +1
Left right shift rule

After we know how to convert binary bits, let's look at the moving rules of the shift operator.

  1. Shift left operator

Discard on the left and fill 0 on the right

int a = 5;
int b = a << 1;

A < < 1 means that the complement of a moves one bit to the left, and the original inverse complement of a positive number is the same, so the complement is 00000000 00000000 00000101. Moving one bit to the left gets 00000000 00000000 000001010. After conversion, you can get 10.

At this time, the value of a is still 5, which can be compared with b=a+1, and a will not change.

int c = -1;
int d = c << 1;

First write the original code of - 1, then add one inversely to get the complement, move the complement one bit to the left, and then convert the obtained complement into the original code according to the same rules to get - 2.

10000000 00000000 00000000 00000001 - -1 Original code of
11111111 11111111 11111111 11111110 - -1 Inverse code of
11111111 11111111 11111111 11111111 - -1 Complement of

11111111 11111111 11111111 11111110 - -1<<1 Complement of
11111111 11111111 11111111 11111101 - Inverse code
10000000 00000000 00000000 00000010 - Original code = -2
  1. Shift right operator

    There are two kinds of shift right rules, one is logical shift right, and the other is arithmetic shift right. But most compilers use arithmetic shift right.

Arithmetic shift right: fill the original sign bit on the left and discard it on the right

Logical shift right: fill 0 on the left and discard it on the right

int a = -1;
printf("%d\n", a >> 1);
//10000000 0000000 0000000 00000001 - original code
//11111111111111111111111111111111110 - inverse code 
//11111111111111111111111111111111111111111 - complement
//11111111111111111111111111111111111111111 - complement

Logical right shift will make negative numbers into integers, so arithmetic right shift is more correct.

It is worth mentioning that the complement of - 1 is still - 1 after moving one bit to the right.

Supplement:
  1. It is not difficult to find that moving left makes the data larger, moving right makes the data smaller, and moving left is the data × 2 ×2 × 2. Move right is data ÷ 2 ÷2 ÷2 .
  2. The shift left or shift right operand must be an integer.
  3. The shift operator cannot move negative digits, i.e. 1 > > - 1. The standard does not define behavior.
Bitwise operators
&	//Bitwise AND	
|   //Bitwise OR
^    //Bitwise XOR

Similarly, the bit operator is also in binary bits.

Operation rules
  1. Bitwise AND&

    All 1 means 1, and 0 means 0

  2. Bitwise OR|

    If there is 1, then 1; if there is all 0, then 0

  3. Bitwise XOR^

    The same is 0 and the difference is 1

It can be seen from the operation rules that bitwise and bitwise or are similar to logical and, logical or.

int a = 3;
int b = -2;
int c = a & b;

//1. Find the complement of a
100000000 000000000 000000000 000000010 - -2 Original code of
111111111 111111111 111111111 111111101 - -2 Inverse code of
111111111 111111111 111111111 111111110 - -2 Complement of
//2. Find the complement of b   
000000000 000000000 000000000 000000011 -  3 The original complement is the same!!
//3. Find a & B 
111111111 111111111 111111111 111111110 - -2 Complement of
000000000 000000000 000000000 000000011 -  3 Complement of
000000000 000000000 000000000 000000010 - Complement of the resulting number!! (All 1 is 1, and 0 is 0) 
//4. Convert to original code
000000000 000000000 000000000 000000010 - The original inverse complement of a positive number is the same 
computing method
  1. Find the complement of two operands
  2. Calculate the result of bitwise and, or, XOR
  3. Convert the resulting complement to the original code

The complements of a and b are obtained, and then the complements of the obtained numbers are obtained by bitwise and, or, and then converted into the original code. These steps are very winding. Don't be taken into the ditch. The other two are the same except that the operation rules are different.

be careful
  1. The original inverse complement of integers is the same, but it can not be calculated according to the specification of negative numbers.
  2. The result of bitwise and, bitwise or is also a complement, and finally needs to be converted into the original code.
Examples

No temporary variables are created to realize the exchange of two numbers.

int a = 10;
int b = 20;
printf("a=%d,b=%d\n", a, b);
//1.
a = a + b;
b = a - b;//(a+b)-b = a
a = a - b;//(a+b)-a = b
printf("a=%d,b=%d\n", a, b);
//Spillover risk
//2.
a = a ^ b;
b = a ^ b;//(a ^ b) ^ b = a
a = a ^ b;//(a ^ b) ^ a = b
//Poor readability, only positive numbers are supported
  1. The value of a^b is XOR with a to get b; The value of a^b is XOR with b to get a.
  2. a ^ a = 0 a ^ 0 = a
  3. (a ^ a) ^ b = b (a ^ b) ^ a = b, so it can also be said that XOR supports the commutative law
Use

Give a positive integer. What if you want to know that its (if it is a negative number, it is a complement) lowest bit is 0 and 1?

The positive integer is bitwise and 1. If the result is 1, the lowest bit is 1, otherwise it is 0. For example:

int a = 15;
int b = a & 1;
00000000 00000000 00000000 00001111 - 15 The original inverse complement is the same
00000000 00000000 00000000 00000001 - 1
00000000 00000000 00000000 00000001 - b=1 The original inverse complement is the same

From this example, we can see that if a positive number & 1, the result is 1, the lowest bit is 1, otherwise it is 0. If you use the > > shift right operator, you can get the number of each digit. For example:

int num = 15;
int count = 0;
for (int i = 0; i < 32; i++)
{
    if (((num >> i) & 1) == 1){
        count++;
    }
}
printf("%d\n", count);
Assignment operator
=
//Compound assignor
+=   -=   *=   /=   %=   >>=   <<=  		

Assignment operators don't have much to say. Let's take a look at some wonderful things.

int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//continuous assignment 

How to understand this continuous assignment?

First, y+1 is assigned to x, and then the value of expression x=y+1 is assigned to a.

unary operator
!		//Logical reverse operation
-		//Take negative
+		//Take positive
&   	//Get address
sizeof	//Type length of operand
~		//Bitwise inversion
--		//Front and rear——
++		//Front and rear++
*		//dereference operator 
(type)  //Cast type
Logical reverse operation!

Non zero is true, zero is false, default! 0=1

Take address operator & dereference operator*
int a = 10;
int* p = &a;//*- note that p is a pointer variable, & - note that p stores the address of A
*p = 20;//Dereference accesses the contents of the address in which it is stored
printf("%d\n", *p);

Array name as the first element address problem

int arr[10] = { 0 };
//1.
printf("%p\n", arr + 1);
//2.
printf("%p\n", &arr[0] + 1);
//3.
printf("%p\n", &arr + 1);

Both arr and arr[0] are the addresses of the first element, & arr is the address of the entire array, which is the same when printed. But when they are all + 1, the difference appears. The first two plus 1 are the addresses of the second element, and & arr plus 1 skips the addresses of the whole array.

Broaden a little, * p is a space on the left of = and a value on the right of = is a value.

//1.
int b = *p;//Here * p represents the value
//2.
*p = b;//Here * p represents a space for storing values

Any variable can be understood in this way. If it is placed on the left of =, it represents a space a = 10;, It's an lvalue. On the right is the representative value p = a;, That is, the right value.

Type length operator sizeof

sizeof calculates the size of memory space occupied by variables or types, regardless of what data is stored in memory.

//1.
printf("%d\n", sizeof arr);
//2.
printf("%d\n", strlen(arr));

sizeof strlen() the difference between the two

  1. sizeof is an operator that calculates the space occupied, regardless of the stored data
  2. strlen() is a function to calculate the length of a string. It focuses on the number of characters before \ 0 in the stored data

The () after sizeof is the parenthesis of the expression, not the function call operator. Because sizeof is an operator, it can be omitted.

Example:

int a = 5;
short s = 10;
printf("%d\n", sizeof(s = a + 2));//?
printf("%d\n", s);//?
  1. Assigning int data a+2 to short data s will cause integer truncation or short data.

  2. The expression inside sizeof does not participate in the operation, so the original s is still what it is now. Reason: sizeof's internal operation is processed during precompiling, and the internal expression has long been replaced with numbers during program execution.

Bitwise negation operator~

Invert all its binary bits.

Examples

How to specify one bit of binary bit, change 1 to 0 and 0 to 1?

int a = 13;
//00000000 00000000 00000000 00001101 - 13
//00000000 00000000 00000000 00000010 - 1<<1
//00000000 00000000 00000000 00001111 - 15
int b = a | (1<<1);
printf("%d\n", b);

//00000000 00000000 00000000 00001111 - 15
//11111111 11111111 11111111 11111101 - ~(1<<1)
//00000000 00000000 00000000 00001101 - 13
int c = b & (~(1 << 1));
printf("%d\n", c);

If the binary bit is 0 and you want to change it to 1, press the bit or the previous number 00100... The binary bit is 1. If you want to change it to 0, it is bitwise with the previous number 11011..

++-- Operator

Pre + + -- use before modification, and post + + -- modify before use.

int a = 0;
printf("%d\n", a);
int b = a++;
printf("%d\n", b);
int c = --a;
printf("%d\n", c);

++-- just use it in this way. Don't pursue some useless complex use. No one will use it. The purpose of writing code is not to make people understand. For example:

int a = 0;
int b=(++a)+(a++)+(a++);

Such code will produce different results on different compilers. There is no need to waste time on this.

Cast operator (type)
int a = (int)3.14;
Examples
void test1(int arr[]){
	printf("%d\n", sizeof(arr));//(2)
}
void test2(char ch[]){
	printf("%d\n", sizeof(ch));//(4)
}
int main(){
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%d\n", sizeof(arr));//(1)
	printf("%d\n", sizeof(ch));//(3)
	test1(arr);
	test2(ch);

	return 0;
}
  1. (1) And (3) no problem. The array list is placed in sizeof alone. The size of the whole array is calculated, which are 40 and 10 respectively.
  2. (2) And (4) are array names as function parameters. Although it appears that it is received with an array, it is actually received with a pointer. What is calculated is the size of the pointer. The array name is used as a function parameter. It is impossible to pass the entire array parameter. The compiler automatically demotes it to a pointer to the first address of the element.
Relational operator=
	>	>=   <   <=   !=	==

==And = are different. If you write it wrong, it becomes an assignment.

Logical operator
&&     //Logic and
||	   //Logical or	

Logical operators only focus on true and false, logic and & & are and, logic or | is or.

If the operands on both sides of logic and & & are true, the whole condition is true. If one of the operands on both sides of logic or | is true, the whole condition is true.

Examples
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}

Short circuit operation

  1. Logic and &. When the expression on the left is false, the whole condition is false and no operation is performed.
  2. Logical or 𞓜 when the expression on the left is true, the whole condition is true and no operation is performed.

I = a + + & & + + B & & D + +, the first step a++=0 is false, then the whole expression is false, i=0; i = a++||++b||d + +. In the second step, a + + is true, the whole expression is true, and the following expressions do not operate.

Conditional Operator
exp1 ? exp2 : exp3

If the result of expression exp1 is true, execute exp2 and take the result of exp2 as the result of the whole expression; otherwise, execute exp3 and assign it to the whole expression.

comma expression
exp1, exp2, exp3,...,expN

From left to right, the result of the whole expression is the result of the last expression.

In that case, why should we calculate the previous expression and just calculate the last one?

The previous expression may affect the value of the last expression. For example:

int a = 1,b = 2;
int c = (a>b, a=b+10, a, b=a+1);
Subscript references, function calls, and structure members
   []   ()   .   ->
Subscript reference operator []

arr+i is the address of the element with subscript i in the array.

[] is an operator. Its two operands are array name and subscript respectively. Neither of them is indispensable. For arr[i] can be understood as * (arr+i), in that case, we can write:

arr[i] <=> *(arr+i) <=> *(i+arr) <=> i[arr]
int arr[10] = { 0 };
for (int i = 0; i < 10; i++){
    printf("%p --- %p\n", &i[arr], i+arr);
}

This shows that [] is an operator, and this writing syntax is supported.

Function call operator ()
printf("%u\n", strlen("abc"));

Here, both printf and strlen functions must be carried with (), and they must also be carried without passing parameters, otherwise it will be wrong.

For the function call operator (), you can have one or both operands.

Structure member operator. - >

. structure member name

->Structure pointer - > member name

Structure is used to describe a complex object.

Structure definition

struct Book{
	char name[50];
	char id[15];
	float price;
};

Structure usage

Print(struct Book b1){
	printf("The title of the book is:%s\n", b1.name);
	printf("The price is:%f\n", b1.price);
	printf("Book No.:%s\n", b1.id);
}
int main(){
	struct Book b1 = { "Tan Haoqiang C Language programming",55.5f,"2020322222" };
	Print(b1);
	return 0;
}

Using the structure type struct Book, a structure type variable b is created. The members in b have three name s, IDS and price s.

We can also modify the price later, such as:

b1.price = 100.0f;

Can we change the title or number of the book?

b1.name = "data structure";

Of course not. We can see that the book name and book number id are created through an array. For them B1 Name is the first address of the array. How can you assign an address.

Since it's an address, we can dereference the address and access the array elements ~, let's try again.

*(b1.name) = "data structure";

Of course, it is still wrong. It will display garbled code.

The answer is to use the library function strcpy to assign a value to a string

strcpy(b1.name, "data structure");

Structure address

Pass the variable address, how to use it?

  1. (* structure pointer) member name
Print2(struct Book* pb){
	printf("The title of the book is:%s\n", (*pb).name);
	printf("The price is:%f\n", (*pb).price);
	printf("Book No.:%s\n", (*pb).id);
}
  1. Structure pointer - > member name
Print3(struct Book* pb){
	printf("The title of the book is:%s\n", pb->name);
	printf("The price is:%f\n", pb->price);
	printf("Book No.:%s\n", pb->id);
}

Expression evaluation

When an expression is evaluated, it depends partly on the priority and associativity of its operators and partly on the compiler's own rules. The expression we write must make the compiler logic consistent with our own code logic, otherwise it is useless code. At the same time, some operands in expressions may need type promotion.

Implicit type conversion

In the process of operation, some small byte types will be converted to large byte types before operation. The whole process is completed automatically by the compiler at one time.

integral promotion

For example, short and char will be converted to int before operation. This does not mean that type conversion occurs only when different types of data are operated, but to adapt to CPU4 byte arithmetic units, they will be converted to ordinary integers. This process is called integer promotion. As long as there are operations, there will be integer promotion. For example:

char a=1,b=2,c=3;
...
char d=a+b+c;

For example, first promote the integer a, B and C of character type to ordinary integer, then perform operation, and then put it into d. finally, truncation occurs, only take the last byte and convert it back to character type.

How to improve integer

Integer promotion by sign bit of type. For example:

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

Signed number

  1. Write the binary complement of the variable
  2. Fill with the highest sign bit
  3. The obtained complement is converted into the original code

Unsigned number

Top fill 0

How to get the complement of c? (1) First take c as an int type, then write a 32-bit complement, (2) and then truncate it to the last 8 bits. (3) Finally, fill according to the highest order at this time. If it is 0, fill 0; otherwise, fill 1.

Examples

Example 1

char a = 3;
//00000000 00000000 00000000 00000011 - int a
//00000011 - char a
char b = 127;
//00000000 00000000 00000000 01111111 - int b
//01111111 - char b
char c = a + b;
//00000000 00000000 00000000 00000011 - int a integer lift occurred
//00000000 00000000 00000000 01111111 - int b
//00000000 00000000 00000000 10000010 - int c
//10000001 - char c truncated
printf("%d\n", c);
//11111111111111111111111111111111111111 10000001 - int c - integer lift in complement
//10000000 0000000 01111110 - int c - original code 
  1. Write the 32-bit complement of a and b
  2. Truncate and store in memory
  3. Integer lifting, filling according to the highest position
  4. Perform operations
  5. Perform truncation and integer lifting
  6. Convert the resulting complement back to the original code

For example:

After we get the binary codes of the two variables, we carry out integer promotion on them, and then truncate the results, because they need to be stored in the character variable c. Because the variable c is to be printed in the form of% d, the truncated complements (all stored in memory are complements) are integer promoted and converted into the original code again.

Example 2

char a = 0xb6;
//10110110
short b = 0xb600;
//10110110 00000000
int c = 0xb6000000;
//10110110 00000000 00000000 00000000
if (a == 0xb6)
	//11111111 11111111 11111111 10110110 
	//10000000 00000000 00000000 01001001
	//10000000 00000000 00000000 01001010 - int a
	//00000000 00000000 00000000 10110110 - 0xb6
	printf("a");
if (b == 0xb600)
	//11111111 11111111 10110110 00000000
	//10000000 00000000 01001001 11111111
	//10000000 00000000 01001010 00000000 - int b
	//00000000 00000000 10110110 00000000 - 0xb600
	printf("b");
if (c == 0xb60000)
	//10110110 00000000 00000000 00000000 - int c
	//10110110 00000000 00000000 00000000 - 0xb6000000
	printf("c");
  1. First, we write the binary complement of a, b and c (all positive numbers).
  2. Then it is found that there is an operation (= = is also an operation). As long as there is an operation, the integer must be promoted. After the integer is promoted, unfortunately, the highest bit is 1, which is negative by default.
  3. In this way, after the original inverse complement transformation, it is negative anyway and will not be equal to 0xb6 and 0xb600. Only c itself is the default integer without promotion.

Example 3

char c = 1;
printf("%u\n", sizeof(c));//1
printf("%u\n", sizeof(+c));//4
printf("%u\n", sizeof(-c));//4

When calculating sizeof(c), there is no operation, so no integer promotion occurs. Taking positive and negative is also an operator. When sizeof(± C), the expressions (+ C) and (- C) have integer promotion, so they become four bytes.

Arithmetic conversion

For short and char, the integer needs to be promoted to int. what about floating point and long integer? For these types, it is not called integer promotion, but arithmetic conversion.

The order is from high to low. When the type with low precision is operated with the type with high precision, the low precision will be converted to high precision, and then the operation will be performed with the high-precision data. Example:

int a = 4;
float f = 4.5f;
f = a + f;
printf("%f\n", f);//8.500000

When calculating f, you need to convert a to single precision floating-point type first.

Properties of the operator

There are three factors that affect the evaluation of expressions:

  1. operator precedence
  2. Associativity of operators
  3. Control evaluation order

Which two adjacent operators execute first?

First look at the priority. If the priority is the same, look at the combination.

//Expression 1
a*b+c*d+e*f;
//Expression 2
c + --c;
//Expression 3
int a = 1;
a=(++i)+(++i)+(++i);

Such expressions will produce different results under different compilers, because the standards of each compiler are different.

For such expressions, we know the priority and associativity of operators, but we still can't determine the unique path of expression calculation, so such code is not good. We'd rather write a few more steps to standardize and determine the unique execution path of the expression, rather than pursue excessive simplicity. This is not the purpose of the code.

summary

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 ltd on Sun, 02 Jan 2022 14:02:58 +0200