Detailed explanation of C language operators (learn C series with bug Guo)

Posted by kfresh on Sat, 18 Sep 2021 21:19:49 +0200

Operators and expressions

We are Initial C language Now that we have a general understanding of operators, let's dissect operators in detail today.

Operator

There are many operators in C language, but after roughly classifying, there are the following operators

//Arithmetic operator
+ - * / %
//Shift operators 
<<    >>
//Bitwise operators 
&  |
//Assignment operator
=  += -= *= /= ...
//unary operator 
sizeof() ! ++  -- & * 
//Relational operator
> >= < <= ==
//Logical operator
&& ||
//Conditional Operator 
?:
//comma expression 
 ,
//Other operators
[] ()  ->  .   ...

Arithmetic operator

Arithmetic operators are common,
Addition, subtraction, multiplication, division, remainder.
+- * / operator is the same as ours mathematically.
It is worth noting that
/

int c=10/3; 
   c=10.0/3;
   c=10/3.0; //c the result is 3
   (double)c=10.0/3; //Can calculate small values

In C language, / needs at least one operand to be a floating point number in order to make the result a floating point number, and remember to exist in the floating point number.
%Remainder (modulo) operator
Only the result between two integers can be calculated, and the result is also an integer.

Shift operators

The shift here refers to moving binary bits.
There are shift left and shift right operators.
< < shift left operator
Move bits to the left

We can see that after the binary is shifted to the left, a < < 1 is twice as large as a, so we can know that it is shifted to the left by one bit and expanded by two times.
Shift n bits left and expand 2^n times.
>>Shift right operator
It is conceivable that the shift right operator is also similar to the shift left effect, moving binary bits one bit to the right.
The binary on the right is discarded and the binary bit on the right is not added. It is not the same as adding zero as the shift left operator. It needs to be discussed by case.
Shift is divided into arithmetic shift and logical shift.
Arithmetic shift is to add a bit to the left when moving to the right. You need to see the binary symbol bit to add. Which bit to add is the same as the symbol bit.
Logical shift means that no matter which bit is added to move left or right, 0 is added.
But when we need to move a negative number, it is obvious that the logical shift will change the positive and negative value of the original number.
Therefore, arithmetic shift is used in general compilers.

You can see that the original value of the move right operation has been reduced by 2^n times.
Note: whether overflow will occur after shift should be considered for both left and right shifts.
The shift operation is for moving positive digits,
a> > - 1 this shift error, C language is not defined.

Bitwise operators

Bit operations include & (bitwise AND), | (bitwise OR), ^ (bitwise XOR) ~ (bitwise inversion).
The bit operator, as its name suggests, is an operation on binary bits. There are two operands to operate on binary bits.
Here, our binary bits refer to the complement, because a number is stored in memory in the form of complement.

//   00000000 00000000 00000000 00100010
//   00000000 00000000 00000000 11010110
// & 00000000 00000000 00000000 00000010  
// | 00000000 00000000 00000000 11110110
// ^ 00000000 00000000 00000000 11110100 
Bitwise operators effect
&Both operand bits are true (1) the result is true (1) no is false (0)
|The binary bit of the two operands is false (0) the result is false (0) no is true (1)
^One true (1) one false (0) the result is true (1), otherwise it is false (0)
~Binary bits are reversed bit by bit, 1 becomes 0, 0 becomes 1

Application of bitwise operators

//Try writing this code
include <stdio.h>
int main()
{
    int num1 = 1; //00000000 00000000 00000000 00000001
    int num2 = 2; //00000000 00000000 00000000 00000010
    num1 & num2; // 00000000 00000000 00000000 00000000
    num1 | num2; // 00000000 00000000 00000000 00000011
    num1 ^ num2; // 00000000 00000000 00000000 00000011
    return 0;
}

An interview question

The exchange of two variables is realized without creating new variables.

//Method 1
#include<stdio.h>
int main()
{
 int a=3; // 00000000 00000000 00000000 00000011
 int b=5; // 00000000 00000000 00000000 00000101
 a=a^b;  //  00000000 00000000 00000000 00000110
 b=a^b;  //  00000000 00000000 00000000 00000011
 a=a^b;   // 00000000 00000000 00000000 00000101
}

Interesting code, using ^ bitwise XOR to realize the exchange of two numbers.
^Properties of XOR operators
a^a=0;
a^0=a;
Often use these two properties to solve problems and write excellent code!

//Method 2
#include<stdio.h>
int main()
{
  int a=3;
  int b=5;
  a=a+b;  //a=8
  b=a-b; // b=3
  a=a-b; // a=5
}

Find the number of binary 1 stored in memory as an integer

//Method 1
#include<stdio.h>
int main()
{
 int n=10;
 int count=0;
 while(n)
 {
 	if(n%2==1)
 	{
 		count++;
 	}
 	n>>=1;
 }
 printf("Enter the number of binary bits 1:%d",count);
}

Think about whether there is a problem with the above code
When n is negative?
You can see that the program will continue to loop.
Let's optimize it!

//Method 2
#include<stdio.h>
int main()
{
 	int i=0;
 	int count=0;
 	int num=-3;
 	for(i=0;i<32;i++)
 	{
 		if((num>>i)&1==1)   //Shift and determine whether the last bit is 1
 			count++;
 	}
	return 0;
}

32 cycles are required each time. Can we optimize it again!

//Method 3
#include <stdio.h>
int main()
{
    int num = -1; // 10000000 00000000 00000000 00000001
              //Complement 11111111111111111111111111111111111111111
    int i = 0;
    int count = 0;//count
    while(num)
    {                    
        count++;          
        num = num&(num-1);//Discard last bit 1
    }
    printf("Number of 1 in binary = %d\n",count);
    return 0;
}

The above code is not very magical. Most people can't think of it. This is the charm of the code!

Assignment operator

Assignment operators, we're all familiar with.
We can change a variable to the value you want through the assignment operator!

#include<stdio.h>
int main()
{
 	int weight=180;
 	weight=125;  //Dissatisfaction can change
 	//continuous assignment 
 	int a=13,b=0,c=0;
 	a=b=c=6;
 	//The disadvantage of continuous assignment operation is not easy to debug
}

Compound assignment operator

+= -= *= /= %= ...

You can see many compound assignment operators

   a+=2; ===>   a=a+2;
   a*3;  ===>   a=a*3;
     //Another reason
   ....

Here is the compound assignment operator, which is very simple and convenient to use!

unary operator

//A unary operator is an operator with only one operand!
+   -   !   sizeof()   ++  --  ~  *  (type)

+ -

The + - here are unary operators, not the + - in arithmetic operators!

    a=-5;  //-5 here - refers to the monocular operator!
    b=+5; //+5 + can be omitted!

!
Logical inverse operator

 while(a!=0)   //Here it is! Logical inverse operator
 {
 	count++;   //a is not 0count + +;
 }
 while(!a)
 {
  count++;   //a is 0count + +;
 }

sizeof
I'm surprised that sizeof is an operator!
sizeof is a special operator, not a function!
We know that sizeof can calculate the memory size of a variable and type!

int main()
{
    int a = -10;
    int* p = NULL;
    printf("%d\n", !2);
    printf("%d\n", !0);
    a = -a;
    p = &a;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof(int));
    printf("%d\n", sizeof a);      //Is that all right?
    printf("%d\n", sizeof int);//Is that all right?
	return 0;
}


You can see that when sizeof calculates a type without adding parentheses, an error will be reported. However, when calculating the size of a variable, parentheses can be omitted!
Summary: parentheses cannot be omitted when the sizeof calculation type occupies a large amount of memory. Sizeof (type), sizeof (variable), sizeof variable

Change the error and see the running results!
sizeof and arrays
We know that sizeof can calculate the space size of variables, so we often calculate the number of elements of an array through sizeof!
Formula: sizeof (array) / sizeof (one element of array)

#include <stdio.h>
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;
}

Q:
(1) (2) how much is output from the two places?
(3) (4) how much is output from the two places?

Let's calculate by ourselves first!
Calculation results!
(1)40 (2) 40 (3)10 (4) 10
And running results!

You can see that the running result is not like that!
We're thinking about this result. Why would it be 4?
We obviously pass the array directly to the past, but the memory size calculated by sizeof is not. Do we only pass one address to the past?

We debugged and found that this is the assumption. The array parameter does not pass the whole array parameter, but a pointer!
In x86, that is, 32-bit platform, the memory space occupied by the pointer is 4 bytes.

++ --

 //Pre + + and --:
    #include <stdio.h>
    int main()
    {
        int a = 10;
        int x = ++a;
        //First increment a, and then use a, that is, the value of the expression is the value after the 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--
    #include <stdio.h>
    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;
    }

Summary: pre + +, - self adding operation first, and then use!
Post + +, - use it first and then add it yourself!

Relational operator

>> = < < = = = (judge whether it is equal to)= (judgment is not equal to)

These are basic relational operators!
We are already very common. Let's take a look at the running results of relational operators!
It can be seen that when the judgment result is true, vs uses 1 for true and 0 for false.

Note: when we test whether the results are equal, we use = = instead of the assignment operator =.

Logical operator

Logical operators, logical and &, logical or||

When we want to test the results of two expressions, if we want to satisfy them at the same time, use logic and & & use logic or when we only need to satisfy one of the expression results||
We should distinguish between logical operators and bitwise operators, bitwise AND &, bitwise or |!

#include<stdio.h>
int main()
{
     int a=3;//00000000 00000000 00000000 00000011
	 int b=1;//00000000 00000000 00000000 00000001
	printf("%d\n",a&b);
	printf("%d\n",a|b);
	printf("%d\n",a&&b);
    printf("%d\n",a||b);
			
return 0;
}


Bit operators are quite different from logical operators. One is to operate on the binary of integers, and the other is to judge the result of expressions!
&&Only when the results of two expressions are true at the same time, the result is true!
||Only when the results of two expressions are false at the same time, the result is false!
Characteristics of logical expression!

#include<stdio.h>
int main()
{
 int a=3,b=5,c=6,i=0;
 i=a++&&++b;
 printf("%d %d\n",a,b);
 i=a++||++b;
 printf("%d %d\n",a,b);
return 0;
}

We can see that the logic or the second expression is not executed.
Why!

Summary: logic and & & when the result of the expression is false, the execution will stop, and the following expression!
Logic or | when the result of the expression is true, stop executing the following expression!
This is what we often say about the characteristics of logic short circuit!

Conditional Operator

exp1 ? exp2 : exp3

Conditional operators are usually composed of three expressions! Also called (three eye operator)!
If the result of the exp1 expression is true, execute exp2, otherwise execute exp3
You can see that it is similar to our judgment statement if!

#include<stdio.h>
int main()
{
   int a=5,b=3,max=0;
   //if judgment statement maximizes
	if(a>b)
	{
	 max=a;
	}
	else
	{
	 max=b;
	}
	//Conditional expression
	a>b?max=a:max=b;
return 0;
}

You can see the advantages of conditional expressions, which can greatly simplify the code!

comma expression

exp1,exp2,exp3...expN

Expressions are separated by commas. This is a comma expression.
Expression characteristics

#include<stdio.h>
int main()
{
	int a=2,b=3,c=5;
	int i=(a++,b++,c);
	printf("a=%d b=%d c=%d i=%d",a,b,c,i);
return 0;
}


You can see the expression i=(a++,b++,c); The result i=5 is the value of the last expression C.
Comma expression operation features:
The expression is calculated from left to right. The value of the last expression is the value of the result of the whole comma expression!

Other operators

[] subscript reference operator () function call operator. - > structure member access operator

[] subscript reference operator

Operand: an array name + an index value

int arr[10];//Create array
    arr[9] = 10;//Practical subscript reference operator.
      // The two operands of [] are arr and 9.

Since it is two operands, can two operands exchange positions?

You can see that arr[9] is equivalent to 9[arr]
But we don't mind using 9[arr]

() function call operator

() 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();//Utility () as a function call operator.
        test2("hello bit.");//Utility () as a function call operator.
        return 0;
    }

.: structure. Member name
->: structure pointer - > member name

#include <stdio.h>
struct Stu
{
    char name[10];
    int age;
    char sex[5];
    double score;
};
void set_age1(struct Stu stu)
{
    stu.age = 18;
}
void set_age2(struct Stu* pStu)
{
    pStu->age = 18;//Structure member access
}
int main()
{
    struct Stu stu;
    struct Stu* pStu = &stu;//Structure member access
    
    stu.age = 20;//Structure member access
    set_age1(stu);
    
    pStu->age = 20;//Structure member access
    set_age2(pStu);
    return 0;
}

Topics: C