C语言中本人初次学习C语言,以下是对C语言中的操作符学习的相关知识点总结。
操作符的概述:
在C语言之中有很多的操作符,例如:算数运算符,移位操作符,位操作符等,这些操作符赋予了该语言极大地灵活性,能够让我们更好地去操作相关的数据。
算数操作符:
常见的算数运算符有:+ - * / %
需要注意的点:
1. 除了 % 操作符之外,其他的几个操作符可以作用于整数和浮点数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。
3. % 操作符的两个操作数必须为整数。返回的是整除之后的余数。
移位操作符
<< 左移操作符
>> 右移操作符
注:移位操作符的操作数只能够是整数
对于移位运算符,不要移动负数位,这个是标准未定义的
int num = 10;
num>>-1;//error
左移操作符 << :
移位的规则:左边抛弃,右边补零。
例如:
int num = 10;
num << 1;
//那么10在计算机内存中存储二进制的正数补码就是
//00000000000000000000000000001010
//在进行了移位之后存储的数据就在理论上变为
//00000000000000000000000000010100
右移操作符 >> :
移位规则:跟左移运算符有一些不同
右移运算分为两种:
1. 逻辑移位 左边用0填充,右边丢弃
2. 算术移位 左边用原该值的符号位填充,右边丢弃
同样举例:
int num = -1;
//在计算机内存中存储的是-1的补码
//11111111111111111111111111111111
算术右移:
左边使用原来的值的符号位进行填充 (负数在计算机中的符号位计算为1)
//11111111111111111111111111111111(1)
逻辑右移:
//左边补零
//01111111111111111111111111111111(1)
#include <stdio.h>
int main(void)
{
int a = -1;
int b = a >> 1;
printf("%d\n",b);
return 0;
}
上面的代码为例子,输出的结果为-1,说明使用的是算术移位。我们可以来对其进行细致的探讨。
-1的原码10000000000000000000000000000001 //使用‘%d’输出的数据
-1的反码11111111111111111111111111111110
-1的补码11111111111111111111111111111111 //计算机中存储的数据补码 = 反码 +1
位操作符
& 按位与
| 按位或
^ 按位异或
//他们的操作数都必须是整数
简单地小例子:
#include <stdio.h>
int main(void)
{
int num1 = 2;//00000010
int num2 = 5;//00000101
printf("%d\n", num1 & num2);//00000000
printf("%d\n", num1 | num2);//00000111
printf("%d\n", num1 ^ num2);//00000111
return 0;
}
分享一道面试题:
不能创建临时变量(第三个变量),实现两个数的交换。
#include <stdio.h>
int main(void)
{
int a = 3;
int b = 5;
//加减法,可能会溢出
a = a + b;
b = a - b;
a = a - b;
//异或
a = a ^ b;
b = a ^ b;
a = a ^ b;
return 0;
}
上面提供了两种解决方案:
第一种方案使用的是加减法,但是有一个问题,当变量a和b中存放的数很大的时候,可能就会产生溢出的现象。
第二种方案使用的是异或的方案,可以很好地解决上述的问题。
最后的一道练习题:
不能创建临时变量(第三个变量),实现两个数的交换。
首先,可以想到的是,这个求解方法与求解水仙花数的方法可以类似,使用
#include <stdio.h>
int main(void)
{
int num = 0;
int count = 0;
scanf("%d",&num);
//统计num的补码中二进制位的个数
while (num)
{
if (num % 2 == 1)
{
count++;
num = num / 2;
}
}
printf("该数的二进制数中共有%d个1\n",count);
return 0;
}
每次当我们使用num将其 % 2 时,其最后一位就可以被提取出来,再与1进行比较,得到结果。
还可以提供一种更加优化的方案:
#include <stdio.h>
int main(void)
{
int num = 0;
int count = 0;
scanf("%d",&num);
for (int i = 0; i < 32; i++)
{
if (1 == ((num >> i) & 1))
count++;
}
printf("该数的二进制数中共有%d个1\n",count);
return 0;
}
每次讲所求的数与1进行与操作,若最后一位为1则与出的结果为1,否则最后一位为0,然后在对原数进行右移操作。
赋值操作符
在编写程序中,使用的比较多的就是赋值操作。可以使用赋值操作符进行连续的赋值操作。
a = x = y + 1;
//这样的写法很简单,但是不易于理解,写成下面的方式更易于理解
x = y + 1;
a = x;
赋值操作符中还要一些符合的操作符,例如:
+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=
单目操作符
单目操作符介绍
! | 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
-- | 前置、后置-- |
++ | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
sizeof
关于sizeof进行一个简单地分享:
1.sizeof中的表达式不会真实的计算出来。
#include <stdio.h>
int main(void)
{
short s = 0;
int a = 10;
printf("%d\n",sizeof(short)); //2
printf("%d\n",sizeof(s = a + 5)); //2
printf("%d\n",s); //0
//sizeof中的表达式不会实际运行
return 0;
}
2.sizeof求解数组所占空间时,区分在主函数中,还是传入自己编写的函数所占空间两者的区别
#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;
}
在主函数中,sizeof求解的是整个数组所占空间的大小;在自己所写的函数中求解的是首元素的地址的大小。
++和--
++a表示先进行自增操作,然后再使用a变量
a++表示先使用a变量,然后再进行自增操作
#include <stdio.h>
int main(void)
{
int num1 = 0;
int num2 = 0;
printf("num1 = %d\n", ++num1);//1
printf("num2 = %d\n",num2++); //0
return 0;
}
关系操作符
> >= < <= != ==
注意在使用时不要把==与=写错
逻辑操作符
&& 逻辑与
|| 逻辑或
要区分&&与&的区别:
&&操作符运算之后返回的是1或者0;
&操作符是对于位进行操作
在分享一道题目:
#include <stdio.h>
int main(void)
{
int i = 0, a = 0, b = 2, c = 3, d = 4;
i = a++ && ++b && d++;
i = a++ || ++b || d++;
printf(" a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);
return 0;
//当逻辑运算符 && 左边已经判断为假之后后面的就不会再运算
//当逻辑运算符 || 左边已经判断为真之后后面的就不会再运算
}
在此,需要的注意的是逻辑与和逻辑或这两个字符串都是控制求值顺序的。
&&运算符,当第一个条件判断为0时,后面的都不会再继续进行运算。另一个也是同样的判断操作。
条件操作符
exp1 ? exp2 : exp3
解释:当 exp1判断为真时,返回exp2表达式的结果,反之返回exp3的结果。
有了这个操作符我们可以更轻松的实现两个数最大值的比较。
逗号表达式
exp1, exp2, exp3, …expN
逗号表达式,就是使用逗号隔开多个表达式,从左向右依次执行,结果是最后一个表达式的结果。
//逗号表达式从左向右依次执行整个表达式的结果是最后一个表达式的结果
int a = 1;
int b = 2;
int c = (a > b, a = b + 10, b = a + 1);
上述返回的就是b最终运算得到的值。
使用逗号表达式也可以让我们的代码更加简洁:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main(void)
{
a = get_val();
count_vel(a);
while (a > 0)
{
a = get_vel();
count_vel();
}
//使用逗号表达式进行改写
while (a = get_vel(), count_val(a), a>0)
{
;
}
return 0;
}
下标引用、函数调用和结构成员
下标引用操作符 [ ]
//操作数+索引值
int arr[10] = { 0 };
arr[7] = 8;//*(arr+7)
7[arr] = 9;//同样可以正常赋值
arr[7]的实际意义就是*(arr+7)
函数调用操作符 ( )
接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
访问一个结构成员
. 结构体.成员名
-> 结构体指针->成员名
隐式类型转换
整形提升的简要介绍
C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
整形提升的意义:
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长 度。
通用CPU(general-purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。
例如:
char a, b, c;
a = b + c;
简单的说就是,因为char类型的数据只占1个字节大小的空间,因此我们需要在运算时把它提升至int类型的长度,即四个字节,再进行运算。
b和c的值被提升为普通整型,然后再执行加法运算。
加法运算完成之后,结果将被截断,然后再存储于a中。
如何进行整形提升
//无符号数的整形提升
直接高位补零
//正数的整形提升
char c = 1;
变量c中存储的补码是8个bit位的
//00000001
char类型是有符号的,因此高位补充符号位
最终的结果为://00000000000000000000000000000001
//负数的整形提升
char c = -1;
-1显示的原码为
//10000001 - 原码
//111111110 - 反码
//111111111 - 补码 = 反码 + 1
变量c中存储的补码是8个bit位的
//11111111
char类型是有符号的,因此高位补充符号位
最终的结果为://111111111111111111111111111111111
一个具体的例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
//整形提升
//无符号数和有符号数
int main(void)
{
char a = 5;
//00000000000000000000000000000101 a char类型字符截取8个bit位
//00000101
char b = 126;
//00000000000000000000000000111110 b char类型字符截取8个bit位
//01111110
//整形提升并相加
//00000000000000000000000000000101
//00000000000000000000000001111110
//00000000000000000000000010000011
//10000011 c char类型字符截取8个bit位
//整形提升
//11111111111111111111111110000011 补码 = 反码 +1
//11111111111111111111111110000010 反码
//10000000000000000000000001111101 原码
char c = a + b;//存储的是补码
printf("%d\n", c);//输出原码 -125
return 0;
}
另一个例子:
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;
}
算术算换
如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类 型,否则操作就无法进行。下面的层次体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
操作符的属性
有很多的操作符属性,在网上那个可以查找到详细的表格。
要注意的是有很多的表达式,其运算顺序并不明确,例如:
a*b + c*d + e*f
#include <stdio.h>
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
有些运算顺序在不同的编译器中结果不同。
到此为止,是本人学习C语言的操作符的全部内容,非常欢迎大佬前来指正。