1.右移操作符(>>)
①算术右移
右边丢弃,左边补原符号位
②逻辑右移
右边丢弃,左边补0
左移操作符(<<):左边丢弃,右边补0.
2.位操作符(&、|、^)
和左右移运算符一样,操作数必须是整数
&-按位与:对应二进制位同为1时为1,同为0时为0,相异为0
|-按位或:对应二进制位有一位为1时为1
^-按位异或:对应二进制位相同为0,相异为1
3.逗号表达式
用逗号隔开的表达式,从左向右依次执行,整个表达式的结果是最后一个表达式的结果。
4.隐式类型转换
(1)整形提升
整形提升的意义:表达式的整型运算要在CPU的相应运算器内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU通用寄存器的长度。
因此,即使两个char类型相加时,在CPU执行时实际也要转换成CPU内整型操作数的标准长度。通用CPU是很难直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令),所以表达式中各种长度可能小于int长度的整型值,都必须转换成int或unsigned int,然后才能送入CPU去进行运算。
整行提升是按照变量的数据类型的符号位来提升的。
①负数的整形提升
char c1 = -1;
变量c1中的二进制(补码)中只有8个比特位:11111111
因为char为有符号的char,所以整形提升的时候,高位补充符号位即为1,提升后结果是:
11111111111111111111111111111111
②正数的整形提升
char c2 = 1;
变量c2中的二进制(补码)中只有8个比特位:00000001
因为char为有符号的char,所以整形提升的时候,高位补充符号位即为0,提升后结果是:
00000000000000000000000000000001
(2)算术转换
如果某个操作符的各个操作数的类型属于不同的类型,那么除非其中一个操作数的类型转换成另一个操作数类型,否则操作就无法进行,下面的操作体系称为寻常算术转换。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数类型在以上列表中排名较低,那么首先要先转换成另一个操作数的类型后执行运算。
eg:
```、
float f = 3.14;
int num = f;//隐式转换,会有精度丢失
5.指针
指针类型决定了指针进行解引用操作时,能够访问空间的大小。
野指针:野指针就是指针指向位置不可知的(随机的,不正确的,没有明确限制的)
野指针的成因:
①指针未初始化
int main()
{
int p;//指针未初始化
p = 20;
return 0;
}
②越界访问
int main()
{
int arr[10] = {0};
int *pa = arr;//数组名-首元素地址
int i = 0;
for(i=0; i<12; i++)
{
pa++;//当指针指向超过数组范围时,指针就会成为野指针
}
return 0;
}
③指针指向的空间释放
int test()
{
int a = 10;
return &a;
}
int main()
{
int a = test();//局部变量的空间已经被释放
p = 20;
return 0;
}
如何避免野指针
1.指针初始化;
2.小心指针越界;
3.指针指向空间及时置NULL;
4.指针使用前检查其有效性。
指针的运算:
①指针+-整数
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr)/sizeof(arr[0]);
int pa = arr;//数组名-首元素地址
int i = 0;
for(i=0; i<sz; i++)
{
printf("%d", pa);
pa++;
}
return 0;
}
②指针-指针
a.指针-指针运算得到的是元素个数
b.必须要在同一空间,比如在同一数组中
//求字符串格式
int my_strlen(char str)
{
char start = str;
char end = str;
while (end != '\0')
{
end++;
}
return end - start;
}
int main()
{
char arr[] = "bit";
int len = my_strlen(arr);
printf("%d", len);
return 0;
}
③指针的关系运算
二级指针
int main()
{
int a = 10;
int pa = &a;
int * ppa = &pa;//ppa是二级指针
return 0;
}