C Language Notes: Operators

content

1. Arithmetic operator

2. Shift operator

2.1 Left shift operator

2.2 Right shift operator

3. Bitwise operators

3.1 A perverted interview question

3.2 Classic interview questions (requires attention)

4. Assignment operator

5. Unary operator

5.2 sizeof and arrays

5.3 ++ and -- operators

6. Relational Operators

7. Logical Operators

8. Conditional operator (ternary operator)

9. Comma Expression

10. Subscript references, function calls, and structure members

10.1 [ ]: Subscript Reference Operator

10.2 ( ): function call operator

10.3 Accessing members of a structure

11. Expression evaluation

11.1 Implicit type conversions

11.2 Arithmetic conversions

11.3 Attributes of Operators


1. Arithmetic operator

+ - * / %

Note:

1. In addition to the % operator , several other operators can act on integers and floating-point numbers.

2. For the / operator If both operands are integers , perform integer division . And as long as there are floating -point numbers, floating-point division is performed .

3. The two operands of the % operator must be integers . Returns the remainder after division .

void test1()
{
	//6.0 % 2.0; //不是整型,err
	int a= 6 / 2;
	int b = 7 / 2;  
	double c = 7 / 2.0;
	printf("a = %d b = %d c = %lf", a, b,c);//a=3 b=3 c=3.500000 
}

2. Shift operator

<< left shift operator

>> right shift operator    

Note: The operands of the shift operator can only be integers.

2.1 Left shift operator

moving bits

Shifting rules: discard on the left, add 0 on the right

void test2()
{
	int a = 7;//a是一个int变量,4个字节,32个bit位 
	int b = a << 1;

	printf("b = %d\n", b); //14
}

2.2 Right shift operator

Shift rules:

First, there are two types of right shift operations:

1. Logical shift : fill with 0 on the left , discard on the right

2. Arithmetic shift : fill the left side with the original sign bit and discard the right side (the original positive number is filled with 0, otherwise it is filled with 1)

Supplement: When integers are stored in memory, they are stored in binary, and there are three representations:

Original code, inverse code, complement code

For -1:

10000000000000000000000000000001 original code

1111111111111111111111111111110 Complement code (sign bit remains unchanged, other bits are inverted bit by bit)

11111111111111111111111111111111's complement (one's complement + 1)

For integers, the computer is stored in two's complement form

for int b = -1;

It can be seen that the binary of -1 is 11111111111111111111111111111111 => complement form

void test3()
{
	int a = 1; 
	int b = -1;
	int c = a >> 1;
	int d = b >> 1;
	//b从11111111111111111111111111111111
	//=> 11111111111111111111111111111111
	printf("c = %d d = %d\n", c, d); //c = 0,d = -1 说明此时编译器用的是算数右移
}

 Warning ⚠: For shift operators, do not shift negative bits, this is undefined by the standard. E.g:

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

3. Bitwise operators

Bit operators are:

& : bitwise AND: the result of 0 is 0, and the result is 1 when all are 1

| : bitwise OR: the result of 1 is 1, and the result is 0 when all are 0

^ : Bitwise exclusive OR: the corresponding binary bits, the same is 0 and the difference is 1

Note: their operands must be integers .

void test4()
{
	int a = 3;
	int b = 5;
	int c = a & b; // & - 按(二进制)位与 - 有0结果就是0,全为1时结果为1
	//a:011 
	//b:101
	//c:001
	int d = a | b; // | - 按(二进制)位或 - 有1结果就是1,全为0时结果为0
	//a:011
	//b:101
	//d:111
	int e = a ^ b; // ^ - 按(二进制)位异或 - 对应的二进制位,相同为0相异为1
	//a:011
	//b:101
	//e:110
	printf("c = %d d = %d e = %d", c, d, e); //c = 1 d = 7 e = 6
}

3.1 A perverted interview question

A temporary variable (the third variable) cannot be created to exchange two numbers.

void test5()
{
	int a = 3;  //011
	int b = 5;  //101

	a = a ^ b;  // a = 110
	b = a ^ b;  // b = 011
	a = a ^ b;  // a = 101

	printf("a = %d b = %d\n", a, b); //a = 5 b =3
}

Conclusion: a ^ b ^ a = b, b ^ a ^ b = a, a ^ a = 0, 0 ^ a = a

(In a^b^a: a and a cancel out to get b, and the conclusion conforms to the "commutative law", that is, a^a^b = b, and the XOR result of two identical numbers is 000)

3.2 Classic interview questions (requires attention)

Write code to achieve: find the number of 1's in binary stored in an integer in memory

//方法一
int count_one1(int n)//该方法存在缺陷,计算负数会出错
{
	int count = 0;
	while (n) //只要不是0 二进制序列中就会有1
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n = n / 2;
	}
	return count;
}

//方法二
//当a与1按位与:
//11111111111111111111111111111111
//00000000000000000000000000000001 
//00000000000000000000000000000001 
//可见:1的最后一位数字与a的最后一位保持一致
//所以判断一次就右移一次a,每次都与1相与 总共右移32次
int count_one2(int n)
{
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if ( ( (n >> i) & 1 ) == 1 )
		{
			count++;
		}
	}
	return count;
	
}

//方法三(最好的办法)
//n = n & (n-1)  假设n = 7
//   111 & 110 = 110
//再来一次:
//n = n & (n-1)
//   110   100 = 100
//结论:n = n & (n-1) 把n的二进制中从右向左的第一个1变成了0
//方法三的具体步骤为 : 在n = 0之前,不停地用n = n & (n-1),执行了几次,则有多少个1
int count_one3(int n)
{
	int count = 0;
	while (n != 0)
	{
		n = n & n - 1;
		count++;
	}
	return count;
}

void test6()
{
	int a = -1; 
	
	int ret = count_one3(a);
	printf("%d\n", ret);
}

Summarize:

1.a & 1, the purpose that can be achieved is: the last digit of 1 is consistent with the last digit of a, so with the right shift operation of a, you can calculate how many 1s exist in binary

2. n = n & (n-1), this method changes the first 1 from right to left in the binary of n to 0, with the loop: before n = 0, keep using n = n & (n-1), if the loop is executed several times, how many 1s are there

4. Assignment operator

void test7()
{
	int a = 0;
	a = 3;   //赋值操作符
}

 The assignment operator can be used consecutively (but not recommended):

void test8()
{
	int a = 10;
	int x = 0;
	int y = 20;
	a = x = y + 1;//连续赋值
	printf("a = %d\n", a); //a = 21
}

do x = y + 1 first, then a = x

Compound assignment operators  : +=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^= These operators can be written as compound effects. Such as:

int x = 10;
x = x+10;
x += 10;//复合赋值
//其他运算符一样的道理。这样写更加简洁。

Exercise: The binary digits of 14 are known to be: 000000000000000000000000000001110

Now let the 0 in the 5th last digit become 1, and then turn it into 0 after implementation

Idea: If you want the i-th bit from right to left to change from 0 to 1 , and the remaining bits remain unchanged, then let the original binary sequence and only the last i-th bit is 1, and the rest of the bits are all 0. The binary sequence phase OR operation is as follows:

Sequence a: 00000000000000000000000000001110

Sequence b: 0000000000000000000000000010000 Sequence b is obtained by shifting 1 to the left by 4 (i-1) units

Phase or: ----------------------------------------------- -------

Sequence c: 00000000000000000000000000011110

And if you want to change from sequence c back to a, that is, the method of changing the penultimate 5th bit from 1 to 0 : let the sequence c and the sequence d except the penultimate 5th bit is 0, and the rest of the bits are all 1 .

Sequence c: 00000000000000000000000000011110

Sequence d: 1 1 11 1 1 1 1111111111111111111101111

Phase and: --------------------------------------------------------- -------

Sequence a: 00000000000000000000000000001110

It is not difficult to see that the sequence d is obtained by the bitwise inversion of the sequence b

So the code is as follows:

void test10()
{
	int a = 14;
	a |= (1 << 4);
	printf("%d\n", a); //30

	a &= (~(1 << 4));

	printf("%d\n", a); //14
}

5. Unary operator

! Logical inverse operation

- negative value

+ positive value

& take address sizeof operand type length in bytes

~ Reverse the binary bitwise of a number

-- Front, Rear--

++ pre, post ++

* Indirect access operator (dereference operator)

(type) cast

void test9()
{
	3 + 5; // +有两个操作数 双目操作符
	//单目操作符:只有一个操作数的操作符
	//!操作符
	int a = 10;
	printf("a = %d\n", a); //10非0 - 真
	printf("!a = %d\n", !a);//0
	
	int flag = 1;
	if (!flag)
	{
		//表示当flag为假的时候执行if语句
	}
	//&与*操作符 取地址与解引用
	int b = 10;
	int* pb = &b; //pb为指针变量,b的地址存在pa中
	*pb = 20; //解引用操作 通过*pb访问或修改b的值
	printf("b = %d\n", b); //20 

	//sizeof操作符 而不是函数! 还可以计算数组总大小
	int c = 10;
	int arr[10] = { 0 };
	printf("sizeof(c) = %d\n", sizeof(c)); //4
	printf("sizeof(int) = %d\n", sizeof(int)); //4
	printf("sizeof(arr) = %d\n", sizeof(arr)); //40
	printf("sizeof(int[10]) = %d\n", sizeof(int[10]) ); //40

	// ~操作符 按位取反
	int d = 0;
	printf("d = %d\n", ~d); //-1
	//00000000000000000000000000000000 //d的补码
	//11111111111111111111111111111111 //按位取反后的补码
	//11111111111111111111111111111110 //反码 (补码-1)
	//10000000000000000000000000000001 //源码 (符号位不变,其他按位取反)
	//-1

	//(强制类型转换)
	double e = 3.14;
	printf("e = %d\n", (int)e); // e = 3

}

5.2 sizeof and arrays

#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;
}

ask:

(1), (2) How much is the output of the two places? Answer: 40 10

(3) and (4) How much are the outputs of the two places? Answer: 4 4 (note)

Note: what is passed into test2 is the pointer of the first element of the ch array, which is 4 or 8 bytes, and should not be understood as the size of the first element of ch 1

5.3 ++ and -- operators

void test11()
{
	int a = 10;
	int b = 10;
	int c = a++; //后置++:先使用再自增1
	int d = ++b; //前置++ 先自增1后使用
	printf("c = %d d = %d a = %d b = %d \n", c, d, a, b);//10 11 11 11

	/*int e = 10;
	int f = e + 1; //1
	f = ++e; //带有副作用 - e变成了12*/

	int g = 1;
	printf("%d\n", g++); // 1
	printf("%d\n", ++g); // 3

}

Example Question: What is the result of the following code?

#include <stdio.h>
int main()
{
	int a, b, c;
	a = 5;
	c = ++a;
	b = ++c, c++, ++a, a++;
	b += a++ + c;
	printf("a = %d b = %d c = %d\n:", a, b, c);// a = 9 b = 23 c = 8
	return 0;
}

6. Relational Operators

>

>=

<

<=

!= is used to test for "not equal"

== is used to test for "equality"

 These relational operators are relatively simple, but we need to be aware of the error-prone use of some operators :

1. In the process of programming, == and = are accidentally written incorrectly, resulting in errors

2. Strings cannot be compared directly with ==, and objects cannot be judged for equality with ==

3. When judging, you can't directly use connection, such as: 1<a<7 but should be a>1 && a<7 (if you use if(1<a<7), when a=8, it will first judge 1<8 ?, is true, continue to judge 1<7?, is true, so the whole expression is true, the content of the if statement will be executed and cause an error)

7. Logical Operators

&& logical AND

|| logical or

Distinguish between logical AND and bitwise AND , and distinguish between logical OR and bitwise OR

void test1()
{
	int a = 2; //010
	int b = 5; //101
	int c = a & b;   //按(2进制)位与:000
 	int d = a && b; //逻辑与:1
	printf("c = %d d = %d\n", c, d);//0 1

	int e = a | b;   //按(2进制)位或:111
	int f = a || b;  //逻辑或:1
	printf("e = %d f = %d\n", e, f);//7 1
}

Interview questions:

void test2()
{
	//程序输出的结果是什么?
	int i = 0, a = 0, b = 2, c = 3, d = 4;
	i = a++ && ++b && d++;       //a=1 b=2 c=3 d=4
	//i = a++||++b||d++;         //a=1 b=3 c=3 d=4
	printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
}

Note: When using && to judge (a && b), when a is already false, the compiler will no longer judge whether b is true or false, because the result must be false

Similarly, when using || to judge (a || b), when b is already false, the compiler will no longer judge whether b is true or false, because the result must be true

8. Conditional operator (ternary operator)

expression1 ? expression2 : expression3

If expression1 is true, the result of the entire expression is the result of expression2, if it is false, the result of the entire expression is the result of expression3

void test3()
{
	int a = 10;
	int b = 20;
	int max = 0;
	max = a > b ? a : b;
	printf("max = %d", max);
}
//上述代码等价于:
void test3()
{
	int a = 10;
	int b = 20;
	int max = 0;
	if (a > b)
		max = a;
	else
		max = b;
	printf("max = %d", max);
}

9. Comma Expression

expression1, expression2, expression3...expressionn

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

Note: The result of the previous expression may affect the last expression, so you can't just look at the last expression and ignore the previous ones

void test4()
{
	//代码1
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);
	int d = 0;
	printf("c = %d", c); //13

	//代码2
	if (a = b + 1, c = a / 2, d > 10);
	//该表达式最终以d>10的真假做判断

	//代码3
	a = get_val();
	count_val(a);
	while (a > 0)
	{
		//业务处理
		a = get_val();
		count_val(a);
	}                   //该代码较为冗余
	
	//如果使用逗号表达式,改写:
		while (a = get_val(), count_val(a), a > 0)
		{
			//业务处理
		}
		//判断条件仍是a>0,但前面的代码依旧会进行,该代码看起来更为简洁
}

10. Subscript references, function calls, and structure members

10.1 [ ]: Subscript Reference Operator

The operands are: 1. Array name 2. Index value

void test5()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	arr[4]; //下标引用操作符
	//系统会把arr[4] ==> *(arr + 4)
	
	printf("%d\n", *(arr + 4)); //5  注:整型指针+1 地址跳过4个字节
	printf("%d\n", arr[4]);     //5
	printf("%d\n", 4[arr]);     //5  说明数组名和索引值可以互换位置
	
}

10.2 ( ): function call operator

int test6()
{
	printf("hehe"); //函数调用操作符,即便没有传参也不能省略( )
}

10.3 Accessing members of a structure

.struct.membername

-> Structure pointer -> member name

void test7()
{
	struct Book
	{
		char name[20];
		int price;
	};
	struct Book b = {"C语言程序设计", 35};
	//结构体变量.成员变量
	printf("书名:%s 定价:%d\n", b.name, b.price);

	struct Book* pb = &b;
	//结构体指针->成员变量
	printf("书名:%s 定价:%d\n", pb->name, pb->price);

}

11. Expression evaluation

The order in which expressions are evaluated is determined in part by the precedence and associativity of operators .

Likewise, the operands of some expressions may need to be converted to other types during evaluation .

11.1 Implicit type conversions

C integer arithmetic operations are always performed with at least the precision of the default integer type.

To achieve this precision, character (char) and short integer (short) operands in expressions are converted to ordinary integer types before use . This conversion is called integer promotion .

The integer operation of the expression should be performed in the corresponding operation device of the CPU. The byte length of the operand of the integer arithmetic unit (ALU) in the CPU is generally the byte length of the int, and it is also the length of the general-purpose register of the CPU.

Therefore, even the addition of two char types is actually converted to the standard length of the integer operand in the CPU when the CPU is executed.

It is difficult for general-purpose CPUs to directly add two 8-bit bytes directly (although there may be such byte addition instructions in machine instructions). Therefore, various integer values ​​whose length may be less than the length of int in the expression must be converted to int or unsigned int before they can be sent to the CPU to perform operations.

How to improve overall?

Integer promotion is promoted according to the sign bit of the data type of the variable

//负数的整形提升
char c1 = -1;
变量c1的二进制位(补码)中只有8个比特位:
1111111
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为1
提升之后的结果是:
11111111111111111111111111111111


//正数的整形提升
char c2 = 1;
变量c2的二进制位(补码)中只有8个比特位:
00000001
因为 char 为有符号的 char
所以整形提升的时候,高位补充符号位,即为0
提升之后的结果是:
00000000000000000000000000000001
//无符号整形提升,高位补0

Examples of shaping lifts:

void test8()
{
	char a = 3;
	char b = 127;
	char c = a + b;
	//a和b要发生整型提升
	//3的二进制:      00000000000000000000000000000011
	//把3存入a中,此时的a:00000011,由于最高位为0,正数
	//所以a整形提升为:00000000000000000000000000000011   -提升后的a

	//127的二进制:    00000000000000000000000001111111
	//存入b中后:      01111111
	//整形提升后:     00000000000000000000000001111111   -提升后的b
	//           +   00000000000000000000000000000011  
	//a+b            00000000000000000000000010000010
	//存入c中:10000010
	//打印%d的c - 又要发生整形提升
	//此时打印的c:    11111111111111111111111110000010   -提升后的c - 补码
	//               11111111111111111111111110000001              - 反码
	//               10000000000000000000000001111110              - 原码 -126

	printf("c = %d\n", c); //-126
}
void test9()
{
	char a = 0xb6;       //10110110         
	short b = 0xb600;    //1011011000000000
	int c = 0xb6000000;  //101101100000000000000000
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");     
	//c
}  
void test10()
{
	char c = 1;
	printf("%u\n", sizeof(c));   //1 
	printf("%u\n", sizeof(+c));  //4 +c是表达式,会发生整形提升(变成int 4字节)
	printf("%u\n", sizeof(-c));  //4 与+c同理
}

11.2 Arithmetic conversions

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

long double

double

float

unsigned long int

long int

unsigned int

int

If the type of an operand is lower in the above list, the operation is performed first by converting to the type of the other operand. (For example: adding int and long int will convert int to long int)

But the arithmetic conversion should be reasonable , otherwise there will be some potential problems:

float f = 3.14;
int num = f;//隐式转换,会有精度丢失

 Example: Find the result of the following code

#include <stdio.h>
int i; //全局变量 - 默认初始化为0
int main()
{
    i--; 
    if (i > sizeof(i))  
    {
        printf(">\n");
    }
    else
    {
        printf("<\n");
    }
    return 0; 
}

The output is: >

Analysis: For the expression i > sizeof(i), i is of type int, and sizeof(i) is of type unsigned int. At this time, arithmetic conversion will occur , i will be converted to unsigned int type , i = -1 before arithmetic conversion, In memory, it is: 1111111111111111111111111111. At this time, the sign bit (highest bit) of the binary sequence is 1, indicating that it is a negative number. After converting to an unsigned integer, the highest bit is also a valid number, and i becomes a very large integer, so sure more than 4

11.3 Attributes of Operators

1. Operator precedence

2. Operator associativity

3. Whether to control the evaluation order

Some problem expressions:

表达式的求值部分由操作符的优先级决定。
表达式1
a*b + c*d + e*f

Note: When code 1 is calculated, since * has a higher priority than +, it can only be guaranteed that the calculation of * is earlier than +, but the priority does not determine that the third * is executed earlier than the first +.

 So the computer order of the expression might be:

a*b

c*d

a*b + c*d

e*f

a*b + c*d + e*f

or:

a*b

c*d

e*f

a*b + c*d

a*b + c*d + e*f

表达式2
c + --c;

Note: Note: The same as above, the priority of the operator can only determine that the operation of the decrement-- is in front of the operation of +, but we have no way to know whether the left operand of the + operator is obtained before the right operand. It is evaluated later, so the result is unpredictable and ambiguous. Assuming c = 1, then the calculation order may be 1 + 0 = 1, or 0 + 0 = 0

//问题代码3
int fun()
{
     static int count = 1;
     return ++count;
}
int main()
{
     int answer;
     answer = fun() - fun() * fun();
     printf( "%d\n", answer);//输出多少?
     return 0;
}

Although the result is the same on most compilers. But in the above code answer = fun() - fun() * fun(); we can only know by the priority of the operators: first multiply, then subtract. , the calling sequence of functions cannot be determined by the precedence of operators.

//问题代码4
#include <stdio.h>
int main()
{
    int i = 1;
    int ret = (++i) + (++i) + (++i);
    printf("%d\n", ret);  //12   4 + 4 + 4
    printf("%d\n", i);    //4
 return 0;  
}

But the output result under linux system is ret = 10(3+3+4), i = 4

Summary: If the expression we write cannot determine the unique calculation path through the properties of the operator, then the expression is problematic.

Guess you like

Origin blog.csdn.net/m0_62934529/article/details/123436088