- 第一个C语言程序
- 内存
- 定义与声明
- 变量是什么
- 为什么要定义变量
- 定义变量的本质
- 定义声明
- 关键字 - auto
- 局部与全局变量
- 作用域 vs 生命周期
- auto
- 关键字 - register
- 寄存器本质
- register 修饰变量
- 1、多文件
- 1.1、变量声明
- 1.2、头文件包含内容
- 1.3、函数声明
- 1.4、跨文件
- 2、static
- 1、sizeof
- 2、数据存储
- 2.1、原反补
- 2.2、整形存储的本质
- 数据存储
- 十进制-二进制
- 二进制-十进制
《C语言深度解剖》
32个关键字 - C90标准
第一个C语言程序
#include <stdio.h>// 文本文件 - 可执行程序(二进制文件) - 双击启动该程序
// 生成可执行程序并运行int main()
{printf("hello world!\n");return 0;
}
内存
- 在win中,双击的本质运行程序,是将程序加载到内存中
- 任何程序是在被运行之前必须被加载到内存中
- 程序没有被加载的时候,在硬盘中
- 加载在内存中,因为快
定义与声明
变量是什么
在程序运行时开辟,用来保存数据
为什么要定义变量
因为数据不会立即被处理,需要暂时保存起来
定义变量的本质
所有变量,本质都是在内存中开辟空间,因为程序已经被加载到内存
定义声明
定义:开辟空间
声明:告知
关键字 - auto
局部与全局变量
局部变量:只在本代码块内有效
全局变量:整个程序运行期内,都有效
作用域 vs 生命周期
作用域:该变量的有效区域
生命周期:进开辟,出释放
auto
auto一般用来修饰局部变量
// test
#include <stdio.h>int main()
{for(int i = 0; i < 10; i++){printf("i = %d\n", i)if(1){int j = 1;printf("before: %d\n", j);j++;printf("after: %d\n", j);}}return 0;
}
关键字 - register
寄存器本质
在硬件层面上,提高计算机的运算效率,因为不需要从内存里读取数据了
register 修饰变量
尽量将所修饰的变量,放入CPU寄存区中,从而达到提高效率的目的
采用register的变量:
- 局部的
- 不会被写入的
- 高频被读取的
- 寄存器数量有限,编译器自动判断
register修饰的变量,不能取地址,因为已经在寄存器中了
1、多文件
1.1、变量声明
// test.c
int g_val = 100;// main.c
#include <stdio.h>extern int g_val;int main()
{printf("%d\n", g_val);return 0;
}// extern int g_val = 100; //err
// 申明并没有开辟空间
// =100 赋值或者初始化
// 所有的变量声明的时候,不能设置初始值
1.2、头文件包含内容
1、h基本都是要被多个源文件包含
2、头文件包含内容:
-
C头文件
-
所有的变量的声明
-
#define 类型typedef,struct
3、头文件可能会被重复包含的问题解决方案:
加 #pragma once
1.3、函数声明
// test.h
#include <stdio.h>extern void show();// test.c
void show()
{printf("hello show()!\n");
}
1.4、跨文件
1、全局变量,全局函数 - 都可以跨文件 被访问
2、有一定规模的项目,一定是多文件的,多个文件之间后续一定要进行数据“交互”,如果不能跨文件,“交互”成本较高
2、static
1、修饰全局变量,该变量只能在本文件内被访问,不能被外部其他文件直接访问
2、修饰函数,该函数只能在本文件内被访问,不能被外部其他文件直接访问
3、修饰局部变量,更改局部变量的生命周期,作用域不变
项目维护 提供安全保障
1、sizeof
sizeof - 确定一种类型对应在开辟空间的时候 大小
#include <stdio.h>int main()
{int a = 10;printf("%d\n", sizeof(a)); // 4printf("%d\n", sizeof(int)); // 4printf("%d\n", sizeof a); // 4// printf("%d\n", sizeof int); //err// sizeof 不是函数return 0;
}
-
C中为什么要有类型:
本质对内存进行合理化划分,按需索取
-
类型为什么在C中有这么多种:
应用场景不同,解决应用场景对应的计算方式不同,需要空间的大小也是不同的。本质:用最小成本,解决各种多样化的场景问题。
#include <stdio.h>int main()
{int *p = NULL;int arr[10];int *test[3];printf("%d\n", sizeof(p)); // 4printf("%d\n", sizeof(arr)); // 4*10 printf("%d\n", sizeof(test)); // 4*3return 0;
}
2、数据存储
2.1、原反补
int main()
{int b = -20;// 16+4 16:2^4(10000) 4:2^2(100)// 1000 0000 0000 0000 0000 0000 0001 0100 原码// 1111 1111 1111 1111 1111 1111 1110 1011 反码+1// 1111 1111 1111 1111 1111 1111 1110 1100 补码
}
-
无符号数 - 原反补相同
-
求补码方法:
-
方法一:补码减一得反码 反码按位取反得原码
1111 1111 1111 1111 1111 1111 1110 1100 补码
1111 1111 1111 1111 1111 1111 1110 1011 反码
1000 0000 0000 0000 0000 0000 0001 0100 原码
-
方法二:按规则 加一 取反
计算机硬件完成,原 反 补
可以使用一条硬件电路 完成转化
1111 1111 1111 1111 1111 1111 1110 1100 补码
1000 0000 0000 0000 0000 0000 0001 0011 反码+1
1000 0000 0000 0000 0000 0000 0001 0100 原码
2.2、整形存储的本质
数据存储
unsigned int b = -10;
定义变量并做了初始化
将内容转化成二进制
补码:1111 1111 1111 1111 1111 1111 1111 0110
整形存储的时候,空间是不关心内容的
在将数据保存在空间内的时候,数据已经被转化成二进制
类型决定了如何解释空间内部保存的序列
变量存的过程:字面数据必须先转成补码,再放入空间当中。所以,符号位完全看数据本身是否携带±号,和变量是否有符号无关
变量取的过程:取数据一定要先看本身类型,然后再决定要不要看符号位。如果不需要,直接二进制转成十进制,如果需要,则需要转成原码,然后才能识别。
十进制-二进制
1 -> 2^0
10 ->2^1
100 ->2^2
1000 ->2^3
1后面加n个比特位 就是2^n
如67
67 - > 64+2+1
2^6 2^1 2^0
0000 0000 0000 0000 0000 0000 0100 0011
二进制-十进制
0000 0000 0000 0000 0000 0010 0100 0011
2^9 + 2^6 + 2^1 + 2^0 = 512+64+3 = 579