C语言初阶--操作符详解

目录

操作符分类:

1.算术操作符

+       -       *       /      % ​

2.移位操作符

(1)左移操作符:

(2)右移操作符

3.位操作符

(1)按位与

(2)按位或

(3)按位异或

面试题:

4.赋值操作符

5.单目操作符

5.1 !操作符实例

5.2 _Bool类型

5.3 ++、--操作符

5.4 ()强制转换操作符

5.5 unsigned int 问题:

5.6 &、*操作符 

5.7 sizeof

5.8 sizeof与数组 

5.9 ~按位取反操作符:

6.关系操作符

7.逻辑操作符

&&逻辑与

 ||        逻辑或

8.条件操作符

9.逗号表达式

10.下标引用、函数调用和结构成员


操作符分类:

1.算术操作符

+       -       *       /      % 

1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数(取模只能用于整数)。

2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。

#include<stdio.h>
int main()
{
	//操作符(运算符)
	int ret1 = 10 / 3;
	备注:对于除法操作符来说,两边的操作数都是整数,执行的是整数除法
	如果想计算出小数,除号的两端至少有一个操作数是浮点数
	double ret2 =  10.0 / 3.0;
	printf("%d\n", ret1);

	printf("%lf\n", ret2);
	printf("%.1lf\n", ret2);//%.lf表示保留小数点后一位

	int ret3 = 10 % 3;//取模(取余),计算的是整除之后的余数,取模操作符的两边的操作数只能是整数
	printf("%d\n", ret3);
	return 0;
}

2.移位操作符

首先要提一下2进制整数的表示形式 :原码、反码、补码

其中正数的原、反、补码相同。而负数的反码等于原码按位取反(符号位不变),补码=反码+1;

00000000000000000000000000001010 - 原码

00000000000000000000000000001010 - 反码

00000000000000000000000000001010 - 补码

 int a = 10;//正数

10000000000000000000000000001010 - 原码
11111111111111111111111111110101 - 反码
11111111111111111111111111110110 - 补码

int b=-10;//负数

以下是原码,反码,补码的相互变换(针对的是负数)

(1)左移操作符:

左边抛弃、右边补0

num<<1的结果是移位之后的效果,但num的值是不变的

(2)右移操作符

分两种:

1. 逻辑移位 左边用0填充,右边丢弃

2. 算术移位 左边用原该值的符号位填充,右边丢弃

int a = -1;

原码:10000000000000000000000000000001

反码:11111111111111111111111111111110

补码:11111111111111111111111111111111

int b=a>>1;

逻辑移位左边最高位符号位都是补0,算术移位补原来该数值的符号位。

在计算机中移位是采取算术移位

注意:

对于移位运算符,不要移动负数位,这个是标准未定义的。

int num = 10;

num>>-1;//error

3.位操作符

以下操作符均针对于二进制位进行计算的

& //按位与

| //按位或

^ //按位异或

注:他们的操作数必须是整数

(1)按位与

对应的二进制位有0,则为0,两个同时为1,才为1

注意都是转换成补码后才进行计算的

//& - 按2进制位与
//对应的二进制位有0,则为0,两个同时为1,才为1
int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011 -补码

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

	return 0;
}

(2)按位或

对应的二进制位有1则为1,两个同时为0则为0

//按2进制位或
//对应的二进制位有1则为1,两个同时为0则为0
int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011 -补码
	//
	int c = a | b;
	//00000000000000000000000000000011
	//11111111111111111111111111111011
	//11111111111111111111111111111011
	//
	printf("%d\n", c);

	return 0;
}

(3)按位异或

对应的二进制位:相同为0,相异为1

//^ - 按2进制位异或
//对应的二进制位:相同为0,相异为1

int main()
{
	int a = 3;
	//00000000000000000000000000000011
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011 -补码
	//
	int c = a ^ b;
	//00000000000000000000000000000011
	//11111111111111111111111111111011
	//11111111111111111111111111111000
	//11111111111111111111111111110111
	//10000000000000000000000000001000
	//-8
	printf("%d\n", c);

	return 0;
}

面试题:

不能创建临时变量(第三个变量),实现两个整数的交换

两种方法:

第一种

int main()
{
	int a = 3;
	int b = 5;

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

	return 0;
}
//a=a+b;
//b=a+b-b;
//a=a+b-a;

但是a和b的数值太大会溢出,超出规定范围,有一定风险。

第二种

异或操作符:

1.a^a=0

2.0^a=a

3.异或是支持交换律的

#include<stdio.h>
int main()
{
	int a = 3;
	int b = 5;
	printf("交换前:");
	printf("%d %d\n", a, b);
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("交换后:");
	printf("%d %d\n", a, b);
	return 0;
}

但是异或操作符交换2个变量也有缺点:

1.可读性差

2.效率也不如使用临时变量的方法

3.异或只能对整数的交换

4.赋值操作符

赋值操作符自右向左结合,也可以进行复合赋值,简化代码

int main()

{

 int a = 10;

 a = a + 5;

 a += 5;

}

复合赋值符

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

连续赋值可以写成:

int a = 10;

int x = 0;

int y = 20;

a = x = y+1;//连续赋值

但这样写有缺点:

1.可能会让人产生困惑,到底是先把x值赋给a,再把y+1赋给x。还是先把y+1赋给x,再把x赋给a。(本质上是从右向左赋值)

2.不好调试

x = y+1;

a = x;//这样写更容易调试

5.单目操作符

单目操作符介绍

!           逻辑反操作

-           负值

+           正值

&           取地址

sizeof      操作数的类型长度(以字节为单位)

~           对一个数的二进制按位取反

--          前置、后置--

++          前置、后置++

*           间接访问操作符(解引用操作符)

(类型)       强制类型转换

5.1 !操作符实例

#include<stdio.h>
int main()
{
    //C语言中0表示假,非0表示真
    int flag = 5;
    if (flag)
    {
        printf("haha\n");
    }
    if (!flag)
    {
        printf("hehe\n");
    }
    printf("%d\n", !flag);
    printf("%d\n",flag);

    return 0;
}

C语言中0表示假,非0表示真

5.2 _Bool类型

这里提一下布尔类型,没必要赋值浮点数,负数等待,布尔类型就是为了不让胡乱赋值造的。

//布尔类型是用来表示真假的类型

#include<stdio.h>

#include<stdbool.h>
int main()
{
    _Bool flag = true;//true,false
    if (flag)
    {
        printf("hehe\n");
    }
    else
    {
        printf("hahaha\n");
    }
}

5.3 ++、--操作符

++a//先++,后使用

a++//先使用,后++

--//相同道理

5.4 ()强制转换操作符

int main()
{
    int a= (int)3.14;//强制类型转换,只拿整数部分,小数不要了
    printf("%d", a);
}

//少用
int main()
{
    srand((unsigned int)time());//需要一个time函数的返回值作为参数,但time()函数的返回值是time_t,srand又需要unsigned int类型。
}

5.5 unsigned int 问题:

int main()
{
	unsigned int   num = -10;
	printf("%u\n", num);
	 int num1 = -10;
     printf("%d\n", num1);
	return 0;
}

 理解: unsigned int无符号位将本来数据的最高位符号位变成了有效位,那么数据就变大了。

5.6 &、*操作符 

int main()
{
    int a = 10;
   printf("%p\n", &a);
    int* pa = &a;
    
    char ch = 'w';
    char* pc = &ch;
    //printf("%p\n", &ch);

    char arr[10] = { 0 };
    char* p2 = arr;
    char* p3 = &arr[0];

    char* p = "abcdef";
    printf("%p\n", p);
    printf("%c\n", *p);//打印的是a
    return 0;
}
 

void test1(int *arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);//*(arr+i),arr是首元素地址+i跳过i个整型
	}

}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);

	test1(arr, sz);


	return 0;
}

从int*pa=&a来看,pa是指针变量,类型是int*,&a是取出a在内存空间中开辟的地址,*是解引用操作符,通过a的地址找到a,可以改变a的值。

而arr是首元素地址,+i跳过i个整型。

5.7 sizeof

函数调用的时候,要写()但是sizeof后边的括号可以省略,说明sizeof不是函数

sizeof既是操作符,也是关键字,可以求变量(类型)所占空间的大小

#include<stdio.h>
int main(){
int a = 10;
	short s = 5;
	printf("%d\n", sizeof(s = a + 3));
	printf("%d\n", s);

	return 0;
}

 上图说明sizeof内部的表达式是不计算的。

#include<stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof a);//ok
	printf("%d\n", sizeof(int));//但是不能写出sizeof int
	int arr[10] = {0};
	printf("%d\n", sizeof arr);//ok
	printf("%d\n", sizeof(arr));//ok
	printf("%d\n", sizeof(int[10]));//ok

	
	return 0;
}

 注意:不可写成sizeof 类型 ,不加()是不行的

5.8 sizeof与数组 

#include<stdio.h>
void test1(int arr[])//数组传参,形参可以写成数组,也可以写出指针
{               //即使写成数组,本质上也是指针
	printf("%zd\n", sizeof(arr));
}

void test2(char ch[])
{
	printf("%zd\n", sizeof(ch));
}
int main()
{
	int arr[10] = { 0 };
	char ch[10] = { 0 };
	printf("%zd\n", sizeof(arr));//计算的数组大小
	printf("%zd\n", sizeof(ch));//计算的数组大小
	test1(arr);
	test2(ch);
	return 0;
}

5.9 ~按位取反操作符:

~按位取反

00000000000000000000000000000000
11111111111111111111111111111111 - 补码是全1

int main()
{
	int a = 0;
	printf("%d\n", ~a);//?

	return 0;
}

#include<stdio.h>
int main()
{
	int a = 9;
	//00000000000000000000000000001001
	//00000000000000000000000000010000      1<<4
	//00000000000000000000000000011001
	//
	//把a的二进制中第5位改成1
	a |= (1<<4);
	printf("%d\n", a);

	//把a的二进制中的第5位改回来,变成0
	//00000000000000000000000000011001
	//11111111111111111111111111101111
	//00000000000000000000000000001001

	a &= (~(1 << 4));
	printf("%d\n", a);//9

	return 0;
}

6.关系操作符

>

>=

<

<=

!=   用于测试“不相等”

==      用于测试“相等”

7.逻辑操作符

&&     逻辑与

||        逻辑或

&(按位与)和|(按位或)是针对2进制来说的,而上述两个是针对真假来说的。

1&2----->0

1&&2---->1

1|2----->3

1||2---->1

&&逻辑与

//短路
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;
}

 &&左边为假,右边无需计算(自左向右, 只要有一个操作数为假,后面不用算了)。

 ||        逻辑或

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

 || 左边为真,右边无需计算(自左向右, 只要有一个操作数为真,后面不用算了)。

8.条件操作符

exp1 ? exp2 : exp3

真返回条件2,假返回条件3

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int m = 0;
	m= (a > b ? a : b);
	printf("%d\n", m);
	
	b = (a > 5 ? 3 : -3);
	printf("%d\n", b);
	return 0;
}

9.逗号表达式

逗号表达式,就是用逗号隔开的多个表达式。

逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。

int main()
{
	int a = 1;
	int b = 2;
	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式,返回最后一个值,但前面的会影响后面的变量
	printf("%d", c);
}

 逗号表达式,返回最后一个值,但前面的会影响后面的变量

10.下标引用、函数调用和结构成员

1. [ ] 下标引用操作符 ,操作数:一个数组名 + 一个索引值

2. ( ) 函数调用操作符 接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

int main()
{
    int arr[10] = { 1,2,3,4,5,6 };
    printf("%d\n", arr[4]);//下标引用操作符,操作数是arr,10;
    
    int len = strlen("abcdef");//函数调用操作符,操作数是strlen,"abcdef"
    return 0;
}

3. 访问一个结构的成员

.       结构体.成员名

->    结构体指针->成员名

结构体-自定义类型(聚合类型),把简单的类型集合在一起
生活中有些对象要被描述的话,不能简单的使用单个内置类型(char int short long 、 long long 、 float、 double)

例如:

书:书名,作者,出版社,定价
学生:名字,年龄,学号.....

struct Book
{
	char name[20];
	char author[30];
	int price;
};

void print1(struct Book *b)
{
	printf("《%s》%s %d\n", b->name, b->author, b->price);
	printf("《%s》%s %d\n", (*b).name, (*b).author, (*b).price);
}
int main()
{
	struct Book b1 = { "单人破b","niko",60 };
	struct Book b2 = { "全体白给","simple",80 };
	//结构体变量.成员名
	printf("《%s》 %s %d\n", b1.name, b1.author, b1.price);
	printf("《%s》 %s %d\n", b2.name, b2.author, b2.price);

	print1(&b1);

	//print1(&b1);
	return 0;
}

#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 *stu)
{
	stu->age = 18;
}
int main()
{
	struct Stu stu;
	struct Stu* pStu = &stu;
	stu.age = 20;

	set_age1(stu);
	printf("%d\n", stu.age);
	
	set_age2(&stu);
	printf("%d\n", stu.age);
	// set_age2(stu);


}

这是结构体指针,第一个是传值调用,第二个是传址调用。 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dream_Chaser~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值