操作符详解
1.操作符
分类:
算数操作符
移位操作符
位操作符
赋值操作符
单目操作符
关系操作符
逻辑操作符
条件操作符
逗号表达式
下表引用,函数调用和结构成员
1.0算数操作符
+ - * / %
1.除了%操作符外,其他的几个操作符可以作用于整数和浮点数。
2.对于/操作符如果两个操作数都为整数,执行整数除法,而只要有浮点数执行的就是浮点数除法。
3.%操作符的两个操作数必须为整数,返回的是整除之后的余数。
#include<stdio.h>
int main()
{
int a = 5 / 2;//商2余1
//int a = 5 / 2.0;
//int a=8%2;
printf("a=%d\n",a);
return 0;
}
1.1移位操作符
1.<<左移操作符规则:左边抛弃,右边补0
#include<stdio.h>
int main()
{
int a = 5;
int b = a << 1;
//00000000 00000000 00000000 00000101
printf("%d\n,b");
return 0;
}
2 >>右移操作符:
算术右移:右边丢弃,左边补原符号位
逻辑右移:右边丢弃,左边补0
#include<stdio.h>
int main()
{
int a = 16;
//>>-右移操作符
//00000000 00000000 00000000 00010000
int b=a >> 1;
return 0;
}
#include<stdio.h>
int main()
{
int a = -1;
//整数的二进制表示有:原码,反码,补码
//存储到内存的是补码
//10000000 00000000 00000000 00000001-原码
//11111111 11111111 11111111 11111110-反码
//11111111 11111111 11111111 11111111-补码
int b = a >> 1;
printf("%d\n", b);
return 0;
}
3.对于移位操作符,不要移动负数位,这个是标准未定义的。
1.2位操作符
1.位操作符有:
& 按位与
| 按位或
^按位异或
他们的操作数必须是整数
2.代码:
按位与:
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
//00000000 00000000 00000000 00000011
//00000000 00000000 00000000 00000101
//00000000 00000000 00000000 00000001
int c = a&b;
printf("%d\n",c);
return 0;
}
按位或:
#include<stdio.h>
int main()
{
//|按位或
int a = 3;
int b = 5;
//00000000 00000000 00000000 00000011
//00000000 00000000 00000000 00000101
//00000000 00000000 00000000 00000111
int c = a | b;
printf("%d\n", c);
return 0;
}
按位异或:
#include<stdio.h>
int main()
{
//^按位异或
//按二进制位异或
//相同为0,相异为1
int a = 3;
int b = 5;
//00000000 00000000 00000000 00000011
//00000000 00000000 00000000 00000101
//00000000 00000000 00000000 00000110
int c = a ^ b;
printf("%d\n", c);
return 0;
}
3.应用:
不能创建临时变量(第三个变量),实现两个数的交换:
#include<stdio.h>
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;
}
编写代码,求一个整数存储在内存中的二进制的个数:
方法一:
#include<stdio.h>
//这种方法不能计算负数
int main()
{
int num = 10;
int count = 0;
while (num)
{
if (num % 2 == 1)
count++;
num = num / 2;
}
printf("二进制中1的个数=%d\n", count);
return 0;
}
方法二:
#include<stdio.h>
int main()
{
int num = 0;
int count = 0;
scanf_s("%d", &num);
int i = 0;
for (i = 0; i < 32; i++)
{
if(1==((num >> i)&&1))
{
count ++;
}
}
printf("%d\n", count);
return 0;
}
1.3赋值操作符
1.赋值操作符:
#include<stdio.h>
int main()
{
int weight = 120;//体重
weight = 89;//重新赋值
double salary = 1000.0;
salary = 2000.0;//使用赋值操作符赋值
//赋值操作符可以连续赋值
int a = 10;
int x = 0;
int y = 20;
a = x = y + 1;//连续赋值
return 0;
}
2.复合赋值符:
+= -= *= /= %= >>= <<= &= |= ^=
#include<stdio.h>
int main()
{
int a = 10;
a = a + 2;
a += 2;//复合赋值符
a=a >> 1;
a >>= 1;
a = a & 1;
a &= 1;
return 0;
}
1.4单目操作符
1.单目操作符的意思是只有一个操作数。
2.单目操作符有:
3.代码如下:
#include<stdio.h>
int main()
{
1.逻辑反操作
int a = 0;
if (!a)
{
printf("哈哈\n");
}
int a = 10;
printf("%d\n", !a);
2.-负值
int a = 5;
a = -a;
3.&-取地址操作符
int a = 10;
int*p=&a;
*p;//解引用操作符
4.sizeof-求变量,类型所占内存空间的大小,单位是字节
int a = 10;
char c = 'r';
char* p = &c;
int arr[10] = { 0 };
printf("%d\n", sizeof(a));//4
printf("%d\n", sizeof(c));//1
printf("%d\n", sizeof(p));//4
printf("%d\n", sizeof(arr));//40
5.~-对一个数的二进制按位取反
00000000 00000000 00000000 00000000
11111111 11111111 11111111 11111111-补码
11111111 11111111 11111111 11111111-反码
10000000 00000000 00000000 00000001-原码
int a = 0;
printf("%d\n", ~a);//-1
int a=11;
a = a | (1 << 2);
printf("%d\n", a);
a = a&(~(1 << 2));
printf("%d\n", a);
6.++前置和后置
int a = 10;
printf("%d\n", ++a);//前置++,先++,后使用
printf("%d\n", a++);//后置++,先使用,再++
7.*间接访问操作符
8.(类型)-强制类型转化
int a = (int)3.14;
return 0;
}
4.练习:
sizeof和数组:
#include<stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//4
}
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));//40
printf("%d\n", sizeof(ch));//10
test1(arr);
test2(ch);
return 0;
}
1.5关系操作符
> >= < <= !=(用于测试不相等) ==(用于测试相等)
1.6逻辑操作符
1.逻辑操作符有
&&逻辑与 ||逻辑或
include<stdio.h>
int main()
{
int a = 3;
int b = 5;
int c = a && b;
//int c=a ||b;
printf("%d\n", c);
return 0;
}
2.逻辑与和或的特点:
#include<stdio.h>
int main()
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b &&d++;
printf(" a=%d\n b=%d\n c=%d\n d=%d\n", a, b, c, d);//1 2 3 4
return 0;
}
1.7条件操作符
1.exp1?exp2:exp3
2.练习:
求两个数的较大值:
#include<stdio.h>
int main()
{
int a =10;
int b =20;
int max = 0;
max = (a > b ? a : b);
printf("%d\n", max);
return 0;
}
1.8逗号表达式
1.exp1,exp2,exp3,…expn
逗号表达式,就是用逗号隔开的多个表达式,逗号表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, a, b=a + 1);
printf("%d\n", c);//13
return 0;
}
1.9下标引用,函数调用和结构成员
1.[]下标引用操作符
操作数:一个数组名+索引值
int arr[10];//创建数组
arr[9]=10;//使用下标引用操作符
[]的两个操作数是arr和9;
2.()函数引用操作符,接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
#include<stdio.h>
int get_max(a, b)
{
return(a > b ? a : b);
}
int main()
{
int a = 10;
int b = 20;
int max = get_max(a, b);
printf("max=%d\n", max);
return 0;
}
3.结构体访问操作符.:
.:结构体.成员名
->:结构体指针->成员名
#include<stdio.h>
//创建一个结构体类型-struct stu
struct stu
{
//成员变量
char name[20];
int age;
char id[20];
};
int main()
{
//使用sturuct stu类型创建了一个学生对象s1,并初始化
struct stu s1 = { "张三",20,"2019234" };
struct stu* ps=&s1;
printf("%s\n", s1.name);
printf("%d\n",ps->age);
return 0;
}
2表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定,也有些表澳大使的操作数在求值的过程中可能需要转化为其他类型。
2.1隐形类型转化
1.c的整型算数运算总是至少以缺省整形类型的精度来进行的。
为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转化为普通整型,这种转化称为整型提升。
2.整型提升的意义:
表达式的整形运算要在CPU的相应运算器内执行,CPU内整型运算器的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要先转化为CPU内整型操作数的标准长度。
通用CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整形值,都必须先转化为int或者unsigned int,然后才能送入CPU去执行运算。
3.如何进行整型提升呢?
有符号整形提升的时,高位补充符号位
无符号数整形提升时,高位补充0.
整形提升是按照变量的数据类型的符号位来提升的
4.代码如下:
#include<stdio.h>
int main()
{
char a = 3;
//00000011-a
//00000000 00000000 00000000 00000011整形提升
//01111111-b
//00000000 00000000 00000000 01111111整形提升
//00000000 00000000 00000000 10000010
//10000010-c
//11111111 11111111 11111111 10000010整形提升-补码
//11111111 11111111 11111111 10000001-反码
//10000000 00000000 00000000 01111110-原码 -126
char b = 127;
char c = a + b;
printf("%d\n", c);
return 0;
}
2.2算术转化
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转化为另一个操作数的类型,否则操作就无法进行。下面的层次体系称为寻常算术转化。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转化为另一个操作数的类型后执行运算。
2.3操作符的属性
复杂表达式的求值有三个影响的因素:
1.操作符的优先级。
2.操作符的结合性。
3.是否控制求值顺序。
2.4应用
1.求一个整数存储在内存中1的个数:
方法一:
#include<stdio.h>
int count_bit_one(unsigned int n)
{
int count = 0;
while (n)
{
if (n % 2 == 1)
{
count++;
}
n = n / 2;
}
return count;
}
int main()
{
int a = 0;
scanf_s("%d", &a);
int count=count_bit_one(a);
printf("count=%d\n", count);
return 0;
}
方法二:
#include<stdio.h>
int count_bit_one(int n)
{
int count = 0;
while (n)
{
{
n = n&(n - 1);
count++;
}
}
return count;
}
int main()
{
int a = 0;
scanf_s("%d", &a);
int count=count_bit_one(a);
printf("count=%d\n", count);
return 0;
}
2.求二进制不同位的个数,两个int整数m和n的二进制表达中,有多少个位(bit)不同:
#include<stdio.h>
int get_diff_bit(int m, int n)
{
int tmp = m^n;
int count = 0;
while (tmp)
{
tmp = tmp&(tmp - 1);
count++;
}
return count++;
}
int main()
{
int m = 0;
int n = 0;
scanf_s("%d%d", &m, &n);
int count = get_diff_bit(m, n);
printf("count=%d\n", count);
return 0;
}
3.获取一个整数二进制序列中所有的偶数位和奇数位,分别打印二进制序列:
#include<stdio.h>
void print(int m )
{
int i = 0;
printf("奇数位:\n");
for (i = 30; i >= 0; i -= 2)
{
printf("%d",(m >> i) & 1);
}
printf("\n");
printf("偶数位:\n");
for (i = 31; i >= 1; i-=2)
{
printf("%d", (m >> i) & 1);
}
printf("\n");
}
int main()
{
int m = 0;
scanf_s("%d", &m);
print(m);
return 0;
}
4.写一个函数打印arr数组内容,不使用数组下标,使用指针,arr是一个整形一维数组:
#include<stdio.h>
void print(int *p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr,sz );
return 0;