C语言操作符详解

1.操作符分类

算术操作符

移位操作符

位操作符

赋值操作符

单目操作符

关系操作符

逻辑操作符

条件操作符

逗号表达式

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

2.算数操作符

+     -     *      /      %

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

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

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

int a=5/2;//2
int a=5%2;//1
float a=5/2.0;//2.500000
float a=5%2.0;//错误

3. 移位操作符

>>  右移操作符

<<  做移操作符

注:1.移位操作符的操作数要为整数。

2.移动的是二进制数,整数二进制表示有原码、反码、补码。其中存储的是补码。

3.不能移动负位数。int num=10;   num>-1;//错误

其中右移操作符又分为算术右移和逻辑右移。

算术右移:右侧丢弃,左侧补原符号位数,正数补0,负数补1。

逻辑右移:右侧丢弃,左侧补0。

 3.位操作符

&   按位与

|    按位或

^   按位异或

&:有0为零,同为1才为1;

| :   有1为1,同为0才为0;

^: 相同为0,相异为1;

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

结果为:1   7   6

3的二进制00000000000000000000000000000011

5的二进制00000000000000000000000000000101

3&5的二进制00000000000000000000000000000001=1

3|5的二进制00000000000000000000000000000111=7

3^5的二进制00000000000000000000000000000110=6

4.赋值操作符

=

 int a=10;

 int x=0; 

4.1复合赋值符

+=                   a +=10;等价于a = a + 10;

-=                    a -=10;等价于a = a - 10;                   

*=                    a *=10;等价于a = a * 10;

/=                    a /= 10;等价于a = a / 10;

%=                  a %= 10;等价于a = a % 10;

>>=                 a >>= 1;等价于a = a>>1;

<<=                 a <<=1;等价于a = a<<1;

&=                 a & =1;等价于a = a & 1;

|=                 a | =1;等价于a = a | 1;

^=                 a ^ = 1 ;等价于a = a ^ 1;

 例1:

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

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

本代码就和实现a与b的交换,但是当a+b的值大于4的字节,那么结果就会错误.所以采用下面的代码

#include<stdio.h>
int main()
{
	int a = 5;
	int b = 3;
	//a = a + b;//a=8   b=3
	//b = a - b;//a=8   b=5
	//a = a - b;//a=3   b=5
	a = a ^ b;//a 110   b 011
	b = a ^ b;//a 110   b 101   
	a = a ^ b;//a 011   b 101
	printf("a=%d  b=%d\n", a, b);
	return 0;
}

例2: 编写代码实现:求一个整数存储在内存中的二进制中1的个数。

#include<stdio.h>
int main()
{
	int num = 3;
	int count = 0;
	while (num)
	{
		if (num% 2 == 1)
		{
			count++;
		}
		num = num / 2;
	}
	printf("1的个数为%d\n", count);
	return 0;
}

1的个数为3 

看似本代码可以实现计算整数存储在内存中的二进制中1的个数,但是当求的数为负数时,本代码就无法实现该功能.当num为负数时,在if语句判断中,一个负数%一个2一定为负数,不会进入if内部,count不会增加,只会得到0。对代码进行改进:

#include<stdio.h>
int main()
{
	int num = -1;
	int count = 0;
	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (1 == 1 & (num >> i))//if(1==num & (1<<i))
		{
			count++;
		}
	}
	printf("1的个数为%d\n", count);
	return 0;
}

本代码为将num与1相与,只有1与num同时为1时,相与后的结果才会为1 .

1的二进制码————00000000000000000000000000000001 

与1相与来判断最后一位是否为1,然后将num右移或者1左移来判断所有的二进制码位。

本代码可以实现num为负数的情况下的求二进制中1的个数,但是需要循环32次才能实现此功能。对代码进行改进:

#include<stdio.h>
int main()
{
	int num = -1;
	int count = 0;
	int i = 0;
	while(num)
	{
		count++;
		num = num & (num - 1);
	}
	printf("1的个数为%d\n", count);
	return 0;
}

 本代码就可实现当num为0时结束循环。

 5.单目操作符

!           逻辑反操作

-           负值

+          正值

&           取地址

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

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

--          前置、后置--

++          前置、后置++

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

(类型)       强制类型转换

//++和--运算符
//前置++和--
#include <stdio.h>
int main()
{
    int a = 10;
    int x = ++a;
        //先对a进行自增,然后对使用a,也就是表达式的值是a自增之后的值。x为11。
        int y = --a;
    //先对a进行自减,然后对使用a,也就是表达式的值是a自减之后的值。y为10;
    return 0;
}
//后置++和--
#include <stdio.h>
int main()
{
    int a = 10;
    int x = a++;
    //先对a先使用,再增加,这样x的值是10;之后a变成11;
    int y = a--;
    //先对a先使用,再自减,这样y的值是11;之后a变成10;
    return 0;
}
#include <stdio.h>
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);//正确
 printf("%d\n", sizeof int);//错误
 return 0;
}

 6.sizeof()和数组

 

#include <stdio.h>
void test1(int arr[])
{
 printf("%d\n", sizeof(arr));//4或8
}
void test2(char ch[])
{
 printf("%d\n", sizeof(ch));//4或8
}
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;
}

sizeof()计算的是字符长度所占的空间大小,大小为字符。

arr为整形,其中有10个元素。一个整型变量为4个字节,所以sizeof(arr)=40;同理ch为字符型,就为一个字节,所以sizeof(ch)=10。

对函数进行传参时,尽管将arr传过去,但是并不是这个数组的所有元素,而是第一个元素的地址,所以应为一个指针的大小,电脑的位数不同会导致指针的大小不同,32位为4,64位为8。

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

结果为:

4

0

可看出sizeof(s = a + 5)并没有将a + 5的值赋给s,所以sizeof()中不直接参与运算。

7.关系操作符

>

>=

<

<=

!=                用于测试“不相等”

==               用于测试“相等”

注:注意=与==的区分,=为赋值,==为判断是否相等。

8.逻辑操作符 

&&           逻辑与

||              逻辑或

注意与&、|的区分。

&为按位与,是将二进制数列进行相与。

|为按位或,也是讲二进制数列进行相或。

&&(逻辑与)和||(逻辑或),是关注数的本身是否为真,不为零则为真。

1&2    ——————>0

1&&2 ——————>1

1|2    ——————>3

1||2   ——————>1

9.条件操作符 

exp1  ?  exp2  :  exp3

例如:a>b  ?  a  :  b   判断a是否大于b,如果大于就输出a,如果不大于就输出b。

10.逗号表达式

exp1 , exp2 ,  exp3 , …… expN

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

逗号表达式,从左向右依次执行。整个表达式的结果是最后一个表达式的结果。 但是不能直接算最后一个表达式的结果,前面表达式运算可能会改变最后一个表达式的结果。

int a = 1;
int b = 2;
int c = (a > b, a=b+10,a,b=a+1);
printf("%d\n",c);

 当前运算的结果是多少呢?

首先,运算a>b,不产生结果;

其次运算a=b+10,可知此时a应变为12;

再算a,无结果;

最后算b=a+1,这时b应该为13。

为如果直接算b=a+1,结果将为2。

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

11.1 [ ]下标引用操作符

操作数:一个数组名+一个索引值

int arr[10];//创建数组

arr[9] = 10;//实用下标引用操作符。

[ ]的两个操作数是arr和9。

类似于+。1+2 的两个操作数为1和2。

11.2()函数调运操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。

如果没有传递参数,那么就只有函数名一个操作数,故至少有一个操作数。

例如:int num=sun(a,b);操作数为num、a、b;

#include <stdio.h>
void test1()//()不是函数调用操作符,为定义函数的语法规则
{
	printf("hehe\n");
}
void test2(const char* str)
{
	printf("%s\n", str);
}
int main()
{
    //调用函数时才为函数调用操作符
	test1();            //实用()作为函数调用操作符。
	test2("hello bit.");//实用()作为函数调用操作符。
	return 0;
}

11.3访问一个结构的成员 

.        结构体.成员名

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

#include <stdio.h>
struct Stu//定义结构体Stu
{
	//定义结构体中的成员变量,其中name、age、sex都为成员名
	char name[10];
	int age;
	char sex[5];
};
int main()
{
	struct Stu s1 = { "张三",20,"1234" };
	struct Stu* ps = &s1;
	//可以采用结构体指针->成员名
	printf("%s\n", ps->name);
	printf("%d\n", ps->age);
	printf("%s\n", ps->sex);

	//可以运用指针变量
	printf("%s\n", (*ps).name);
	printf("%d\n", (*ps).age);
	printf("%s\n", (*ps).sex);
	//最基础
	printf("%s\n", s1.name);
	printf("%d\n", s1.age);
	printf("%s\n", s1.sex);
	return 0;
}

张三
20
1234
张三
20
1234
张三
20
1234 

12.表达式求值  

表达式求值的顺序一部分是由操作符的优先级和结合性决定。 同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。

12.1隐形类型转换

C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。

char a,b,c;

...

a=b+c;

 b和c的值被提升为普通整型,然后在执行加法运算。

加法运算完成之后,结果将被截断,然后再存储于a中。(将32为二进制区后8位截断,采用字符型方式存储指存储8位二进制码)

整形提升是按照变量的数据类型的符号位来提升的。

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

例1:

int main()
{
	char a = 0xb6;
	short b = 0xb600;
	int c = 0xb6000000;
	if (a == 0xb6)
		printf("a");
	if (b == 0xb600)
		printf("b");
	if (c == 0xb6000000)
		printf("c");
	return 0;
}

a为字符型,8位二进制码。0xb6————1011  0110

b为短整型,16位二进制码。0x600————1011 0110 0000 0000

c为整型,  32位二进制码。0xb6000000————1011 0110 0000 0000 0000 0000 0000 0000

例1中的a,b要进行整形提升,但是c不需要整形提升。

 a,b整形提升之后,变成了负数,所以表达式 a==0xb6 , b==0xb600 的结果是假,但是c不发生整形提升,则表 达式 c==0xb6000000 的结果是真。

结果为:

c

 例2:

int main()
{
	char c = 1;
	printf("%u\n", sizeof(c));//1
	printf("%u\n", sizeof(+c));//4
	printf("%u\n", sizeof(-c));//4
	return 0;
}

例2中的c只要参与表达式运算,就会发生整形提升,

表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节.

表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,

但是 sizeof(c) ,就是1个字节 

12.2算数转换

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换。

long                                                               高

double

double

float

unsigned long int

long int

unsigned int

int                                                                 低

如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运 算。

警告: 但是算术转换要合理,要不然会有一些潜在的问题。 

float f = 3.14;

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

12.3 操作符的属性

复杂表达式的求值有三个影响的因素。

1. 操作符的优先级

2. 操作符的结合性

3. 是否控制求值顺序。

两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。

一些问题表达式

例1:

a*b + c*d + e*f

释:代码1在计算的时候,由于*比+的优先级高,只能保证,*的计算是比+早,但是优先级并不 能决定第三个*比第一个+早执行。

所以表达式的计算机顺序就可能是:

a*b

c*d

a*b + c*d

e*f

a*b + c*d + e*f

或者

a*b

c*d

e*f

a*b + c*d a*b + c*d + e* 

例2: 

int c = 1;

 c  +  - - c;

注释:同上,操作符的优先级只能决定自减--的运算在+的运算的前面,但是我们并没有办法得 知,+操作符的左操作数的获取在右操作数之前还是之后求值,所以结果是不可预测的,是有歧义 的。

先算c 

c=1

- - c

c + - - c

结果为:1 + 0 = 1

或者

- - c //此时c = 0

c + - - c

结果为: 0 + 0 = 0

还有一些在不同的编译器中结果不同,也是因为不能确定运算顺序,导致相同的代码会有不同的结果. 

【基于Python的大麦网自动抢票工具的设计与实现】 随着互联网技术的发展,网络购票已经成为人们生活中不可或缺的一部分。尤其是在文化娱乐领域,如音乐会、演唱会、戏剧等活动中,热门演出的门票往往在开售后瞬间就被抢购一空。为了解决这个问题,本论文探讨了一种基于Python的自动抢票工具的设计与实现,旨在提高购票的成功率,减轻用户手动抢票的压力。 Python作为一种高级编程语言,因其简洁明了的语法和丰富的第三方库,成为了开发自动化工具的理想选择。Python的特性使得开发过程高效且易于维护。本论文深入介绍了Python语言的基础知识,包括数据类型、控制结构、函数以及模块化编程思想,这些都是构建抢票工具的基础。 自动化工具在现代社会中广泛应用,尤其在网络爬虫、自动化测试等领域。在抢票工具的设计中,主要利用了自动化工具的模拟用户行为、数据解析和定时任务等功能。本论文详细阐述了如何使用Python中的Selenium库来模拟浏览器操作,通过识别网页元素、触发事件,实现对大麦网购票流程的自动化控制。同时,还讨论了BeautifulSoup和requests库在抓取和解析网页数据中的应用。 大麦网作为国内知名的票务平台,其网站结构和购票流程对于抢票工具的实现至关重要。论文中介绍了大麦网的基本情况,包括其业务模式、用户界面特点以及购票流程,为工具的设计提供了实际背景。 在系统需求分析部分,功能需求主要集中在自动登录、监控余票、自动下单和异常处理等方面。抢票工具需要能够自动填充用户信息,实时监控目标演出的票务状态,并在有票时立即下单。此外,为了应对可能出现的网络延迟或服务器错误,工具还需要具备一定的错误恢复能力。性能需求则关注工具的响应速度和稳定性,要求在大量用户同时使用时仍能保持高效运行。 在系统设计阶段,论文详细描述了整体架构,包括前端用户界面、后端逻辑处理以及与大麦网交互的部分。在实现过程中,采用了多线程技术以提高并发性,确保在抢票关键环节的快速响应。此外,还引入了异常处理机制,以应对网络故障或程序错误。 测试与优化是确保抢票工具质量的关键步骤。论文中提到了不同场景下的测试策略,如压力测试、功能测试和性能测试,以验证工具的有效性和稳定性。同时,通过对抢票算法的不断优化,提高工具的成功率。 论文讨论了该工具可能带来的社会影响,包括对消费者体验的改善、对黄牛现象的抑制以及可能引发的公平性问题。此外,还提出了未来的研究方向,如增加多平台支持、优化抢票策略以及考虑云服务的集成,以进一步提升抢票工具的实用性。 本论文全面介绍了基于Python的大麦网自动抢票工具的设计与实现,从理论到实践,从需求分析到系统优化,为读者提供了一个完整的开发案例,对于学习Python编程、自动化工具设计以及理解网络购票市场的运作具有重要的参考价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值