经过了五一假期的放松,我们重新投入到编程学习大军中,Lets’ go!
操作符详解
算术操作符
+ - * / %
当我们进行除法操作时
int a = 9 / 2; //4
得到的是商,不会得到4.5
这是为什么呢?
这是因为/
旁边都是整数,执行的是整数除法,即使我们把它改成float a = 9 / 2
也没有改变它的本质----还是将整数除法的结果赋给a;如果我们要将表达式的结果正确显示出来,我们可以这样做:
float a = 6 / 5.0;
//或者
float a = 6.0f / 5.0f;
//只要有其中一个数为浮点数,其结果就为浮点数
int a = 7 % 3.0;
printf("%d\n", a);// error
%
两边操作数必须为整数
移位操作符
之前在我的 第二篇C语言初级基础知识中粗略介绍到移位操作符是将二进制位移动的,今天我们来具体讲一讲。
左移右移的操作方法:
- 左移操作符:左边抛弃,右边补0
- 右移操作符:
逻辑移位:左补0,右丢弃
算术移位:左补原符号位值,右丢弃
(当右移一位时)
我们用一个例子来介绍这两种移位方法的区别:
-1以补码形式存放在内存中(正整数原反补码相同)
当补码向右移动一位时,符号位补1
最终a的值与右移后b的值一致,证明该操作使用的是算术右移。
- 大部分情况下,使用的是算术右移
位操作符
& //按位与
| //按位或
^ //按位异或
//皆按二进制位 - 比较对应位
&
的用途
当我们想要改变二进制中某一位数时,使用&
可以方便的让我们修改数字。
修改第五位数为0,可以让第五位数&上0,该数就改变为0;其它位& 1 不变。但是现在又有了一个问题:我们如何得到第二个二进制序列呢?
通过使用按位取反~
和1<<4得到
^
规则:相同为0,相异为1- 操作数皆为整数
用途
我们来看一道笔试题:
交换两个·int型变量的值,不使用第三个变量。
第一种方法:
int main()
{
int a = 3;
int b = 5;
printf("a = %d b = %d\n", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("a = %d b = %d\n", a, b);
return 0;
}
但是,这种方法有缺陷----数字过大溢出
第二种方法:
int main()
{
int a = 3;
int b = 5;
printf("a = %d b = %d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("a = %d b = %d\n", a, b);
return 0;
}
采用异或来进行两个数的交换。通过交换二进制位达到效果,我们可以这样来理解:
1.任意一个变量x与其自身进行异或运算,结果为0,即x^x=0
2.任意一个变量x与0进行异或运算,结果不变,即x^0=x
将第二种方法中的b = a ^ b
看作b = a ^ b ^ b
,因为异或运算原理为:相异为1,相同为0,所以b ^ b
为0 ,0 ^ a
为 a,因此a的值就赋给了b。
练习
下面来做一道练习:
实现:求一个整数存储在内存中的二进制中1的个数
赋值操作符
= += -= *= /= %= >>= <<= &= |= ^=
a = x = y + 1;//连续赋值
单目操作符
! - + & sizeof ~ -- ++ * (类型)
!
的用途
int main()
{
int flag = 5;
if (flag)
printf("hehe\n");
if (!flag)
printf("haha\n");
return 0;
}
sizeof
的用途
int a = 10;
int arr[10] = { 0 };
//计算数组大小
pritnf("%d\n", sizeof(arr));
pritnf("%d\n", sizeof(int [10]));
//计算a大小
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof a);//从这也可以看出sizeof是一个操作符,不是函数
return 0;
}
short s = 5;
int a = 10;
printf("%d\n", sizeof(s = a + 2));
printf("%d\n", s);
return 0;
请你想一想结果是多少?
a+2得到12,将其值赋给s,s并没有改变其类型 - short - 占2个字节。sizeof里面的表达式在编译处进行操作,运行时进行的是printf操作,所以实际上没有改变s的值。
- sizeof括号里面的表达式不参加运算
sizeof与strlen的区别
#include<stdio.h>
int main()
{
char str[] = "hello world";
printf("%d %d\n", sizeof(str), strlen(str));//12 11
return 0;
}
- 计算字符串长度时,sizeof计算\0,strlen不计算\0
- sizeof为操作符;strlen为函数,需要引用头文件
sizeof和数组
void test1(int arr[])
{
printf("%d\n", sizeof(arr));
}
void test2(int ch[])
{
printf("%d\n", sizeof(ch));
}
int main()
{
int arr[10] = { 0 };
int ch[10] = { 0 };
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(ch));
test1(arr);
test2(ch);
return 0;
}
为什么经过了传参,数组的大小就变得不一样了呢?
数组传参,传的是数组首元素的地址,本质上为指针变量。指针变量的大小取决于系统,在32位平台上为4个字节,在64位平台上为8个字节
~
的用途
int main()
{
int a = -1;
//对补码进行按位取反
int b = ~a;
printf("%d", b);//0
return 0;
}
- 不改变a的值
++ --
的用途
x++:后置++,先使用,再++
++x:前置++,先++,后使用
通过下面的代码来复习一下后置和前置的不同:
int main()
{
int a = 10;
int b = a++;
printf("%d\n", a);//11
printf("%d\n", b);//10
int c = 10;
int d = ++a;
printf("%d\n", c);//11
printf("%d\n", d);//11
//x--和--x同理
return 0;
}
下面使用一个不建议使用的例子来考考你:
int a = 1;
int b = (++a) + (++a) + (++a);
printf("%d", b);
结果为12
第一次++a:2
第二次++a:3
第三次++a:4(++a变成4)
最后计算括号外的+号:4+4+4=12
该代码在windows和linuxs环境下结果不同,故不建议采用它作例子
* &
的用途
int main()
{
int a = 10;
printf("%p\n", &a);// & - 取地址操作符
int* pa = &a;
*pa = 20;// * - 解引用操作符 - 间接访问操作符
printf("%d\n", a);//20
return 0;
}
- 要清楚&符号取地址和按位与的不同
(类型)
的应用
int main()
{
int a = (int)3.14;
printf("%d", a);//3
return 0;
}
关系操作符
> < <= >= !=
- 清楚 = 和 == 的不同
逻辑操作符
&&
||
&&
的用途
#include<stdio.h>
int main()
{
int a = 3;
int b = 0;
if (a && b)
{
printf("hehe"); //不输出
}
//if (a || b)
//{
// printf("hehe"); //hehe
//}
return 0;
}
a&&b:只有a和b同时为真时才为真
a | | b:只要有一个为真即为真
曾经有一道笔试题:
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;
}
通过这两段代码的结果,可以知道:
&&:只要第一个表达式为假,后面表达式都不再计算。
| |:只要第一个表达式为真,后面表达式不再计算
条件操作符
? :
用法:
int b = a > 5 ? 3 : -1;
a>b则结果为3,否则为-1;
逗号操作符
int main()
{
int a = 3;
int b = 5;
int c = 0;
int d = (c = 5, a = c + 3, b = a - 4, c += 5);
printf("%d", d); //10
return 0;
}
从左向右依次计算,最后一个表达式结果为整个表达式结果
下标引用,函数调用和结构成员
[ ] - 下标引用操作符
( ) - 函数调用操作符
. -> - 结构成员访问操作符
[ ]
的用途
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
[ ] 的操作数为2个:arr , 10
( )
的用途
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b); //
printf("%d", ret);
return 0;
}
. ->
的用途
struct Book
{
char name[20];
char id[10];
int price;
};
1.
int main()
{
int num = 10;
struct Book b = { "C语言","1234654",55 };
printf("书名:%s\n", b.name);
printf("书号:%s\n", b.id);
printf("定价:%d\n", b.price);
return 0;
}
2.
int main()
{
struct Book * pb = &b ;
printf("书名:%s\n", (*pb).name);
printf("书号:%s\n", (*pb).id);
printf("定价:%d\n", (*pb).price);
return 0;
}
3.
int main()
{
struct Book * pb = &b ;
printf("书名:%s\n", pb->name);
printf("书号:%s\n", pb->id);
printf("定价:%d\n", pb->price);
}
结构体指针->成员名
隐式类型转换
整型提升时,如果操作数类型不同,则它们会被转换为下列层次中出现的最高层次的类型
▲整型提升
整型提升是什么呢?我们通过下面的代码来理解:
#include<stdio.h>
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n", c);
return 0;
}
??!为什么会是-126呢?
此代码便是整型提升的体现。
3的二进制序列为: 00000000000000000000000000000011
存到a - (char类型)中,只存了8个比特位:
00000011
同理,b也一样:
01111111
正数的整型提升:
高位补充0 - 即符号位
负数的整型提升:
高位补充1 - 即符号位
无符号整型的整型提升:
高位补0
补齐后再相加,为:
00000000000000000000000010000010
注意:c为char类型,所以需要截断,即:
10000010 - c
提升后为:
11111111111111111111111110000010 - 补码
打印出来的应该是原码:
11111111111111111111111110000001 - 反码
10000000000000000000000001111110 - 原码 ( -126)
以上,即为整型提升的过程。
示例
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
if (a == 0xb6)
printf("a");
if (b == 0xb600)
printf("b");
if (c == 0xb6000000)//c本身就是整型,不会发生整型提升
printf("c");
return 0;
}
▲算术转换
#include<stdio.h>
int main()
{
int a = 4;
float f = 4.5f;
//a + f;
return 0;
}
计算 a+f (不同类型)时,要将int类型的a转换为另一个要进行操作的相同类型,这就称为寻常算术转换。
操作符优先级
同一优先级的运算符,运算次序由结合方向所决定。
错误代码示范
一.
a * b + c * d + e * f; //error
计算顺序有多种可能性。容易导致错误。
二.
c + --c;
三.
int main()
{
int i = 10;
i = i-- - --i * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
此代码在不同编译器结果不同。
四.
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);
printf("%d\n", ret); //12 - 在linux环境下为10
printf("%d\n", i); //4
return 0;
}
以后在写代码时,要避免写出此类代码。