表达式求值
表达式求值的顺序一部分是由操作符的优先级和结合性决定。 同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
一、隐式类型转换(整型提升 算数转换)
1、整型提升
C的整型算术运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升。
1.2、整型提升的意义
表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度 一般就是int的字节长度,同时也是CPU的通用寄存器的长度。 因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度。 通用CPU(general - purpose CPU)是难以直接实现两个8比特字节直接相加运算(虽然机器指令 中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转 换为int或unsigned int,然后才能送入CPU去执行运算。
1.3、如何进行整体提升呢?
整形提升是按照变量的数据类型的符号位来提升的 - 高位补充符号位 负数的整形提升, 高位补充符号位补1 正数的整形提升, 高位补充符号位补0 无符号整形提升,高位补0
#include <stdio.h>
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n", c);
return 0;
}
1.4、整形提升的例子
1.4.1、只有char和short会整型提升成int
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;
}
1.4.2、参与运算
int main()
{
char c = 1;
printf("%u\n", sizeof(c));
printf("%u\n", sizeof(+c));
printf("%u\n", sizeof(-c));
return 0;
}
2、算数转换
long double double float unsigned long int long int unsigned int int
向上转换 排名较低的,首先要转换为另外一个操作数的类型后执行运算。
int main()
{
int a = 4;
float f = 4.5f;
float r = a + f;
return 0;
}
3、操作符的属性
3.1、复杂表达式的求值有三个影响的因素
- 操作符的优先级
- 操作符的结合性
- 是否控制求值顺序
3.1.1、优先级 结合性
两个相邻的操作符优先级不同的情况下,取决于优先级 两个相邻的操作符优先级相同时,取决于他们的结合性 // int c = a + b * 3 优先级 先算* 再算+ // int c = a + b + 3 优先级相同 看结合性 L-R 从左向右 先算a+b再算+3
3.1.2、是否控制求值顺序
// && 逻辑与(左边为假 右边就不用算了) // || 逻辑或(左边为真,右边也不用算了) // ?: 条件操作符(表达式1为真 2算3不算) // , 逗号表达式(真正结果是最后一个表达式)
3.2、问题表达式
表达式1 //a* b + c * d + e * f
所以表达式的计算机顺序就可能是: 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 * f
表达式2 //c + --c; 操作符的优先级只能决定自减–的运算在+的运算的前面,但是我们并没有办法得知, + 操作符的左操作数的获取在右操作数之前还是之后求值
代码3 非法表达式
int main()
{
int i = 10;
i = i-- - --i * (i = -3) * i++ + ++i;
printf("i = %d\n", i);
return 0;
}
代码4
int main()
{
int a = 1;
int d = 0;
d = (++a) + (++a) + (++a);
printf("%d\n", d);
return 0;
}

代码5
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();
printf("%d\n", answer);
return 0;
}
静态局部变量 出函数范围不销毁 VS -10
总结:
我们写出的表达式如果不能通过操作符的属性确定唯一的计算路径,那这个表达式就是存在问题的。
指针
1、指针是什么
在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向 (points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以 说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址 的内存单元
- 地址指向了一个确定的内存空间,所以地址形象地被称为指针
- int* pa = &a; pa是用来存放地址(指针),所以pa是指针变量
总结: 指针是个变量,用来存放内存单眼的地址。(存放在指针中的值都被当成地址处理)。
1.1、一个单元1个字节
问题: 一个小的单元到底是多大?(1个字节) 如何编址?
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的是产生一个电信号正电/负电(1或者0) 产生的二进制序列有2^32 这样的二进制序列,如果作为内存单元的编号,2^32次方个地址,能够管理2的32次方个内存单元 //bit //byte //kb //mb //gb //tb //pb… 2^32bit -> 4,294,967,296 ->/8/1034/1024/1024 ->0.5gb
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储, 所以一个指针变量的大小就应该是4个字节。 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
2、指针和指针类型
2.1、指针类型的意义
32位 指针变量大小都是4字节 为什么还要有这么多指针类型?
① 解引用
指针类型的意义1: 指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存大的大小) char* 指针解引用访问1个字节 int* 指针解引用访问4个字节
int main()
{
int a = 0x11223344;
char* pc = &a;
*pc = 0;
return 0;
}
② + -整数
指针类型的意义2: 指针类型决定了,指针+ -整数的时候的步长(指针±整数的时候,跳过了几个字节) int* 指针+1 跳过了4个字节 char* 指针+1 跳过了1个字节
例:把每个整形里放1
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = 1;
}
return 0;
}
每个字节里放1
int main()
{
int arr[10] = { 0 };
char* pa = arr;
int i = 0;
for (i = 0; i < 10; i++)
{
*(pa + i) = 1;
}
return 0;
}
总结:
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。
3、野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
3.1、野指针成因
① 指针未初始化
int main()
{
int* p;
*p = 20;
return 0;
}
② 指针越界访问
int main()
{
int arr[10] = {0};
int i = 0;
int* p = arr;
for (i = 0; i <= 10; i++)
{
*p = i;
p++;
}
return 0;
}
③ 指针指向的空间释放
int* test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
内存中的栈区是从高地址到低地址使用,高地址使用完再使用低地址
3.2、如何规避野指针
- 初始化指针
int main() {
int a = 10;
int* p = &a;
int* p2 = NULL;
}
-
小心指针越界 -
指针指向空间释放即置成NULL -
避免返回局部变量的地址(避免指针指向的空间被释放) -
指针使用之前检查有效性
int main()
{
int* p2 = NULL;
*p2 = 100;
if (p2 != NULL)
{
*p2 = 100;
}
return 0;
}
|