导航:
1. 数据类型 !!!
2. 自定义类型 !!!!
3. 逻辑结构
4. 类型修饰符 !!
5. 杂项 !!
----->x<------------->x<--------------->x<--------------->x<------------->x<-----
char | shot | int | float | double | long | signed | unsigned |
void | struct | union | enum | typedef | if | else | switch |
case | default | while | do | for | goto | break | continue |
auto | register | static | extern | const | volatile | sizeof | return |
xx------------------------------------------------------------------------------------xx
x 常量 和 变量 .
| c语言中 常量有4种基本类型:
| -- #define定义的符号常量
| #define PI 3.14 //定义常量(数值)
| #define NAME "KMIST" //定义常量(字符串)
|
| -- 字符,字符串常量 和 数值常量
| cahr ch = 'K'; // 'K' 是 字符 常量
| char name[6] = "kmist" // "kmist" 是 字符串 常量
| int a = 780; // 780 是 数值 常量
|
| -- 常量变量
| const int age = 20; // age 是 定义的整形常量, 值是20 .
| const 将一个变量修饰成 常量, 意思是说,我不可以去写这个变量,
| 但实际上,它还是变量,可以通过指针的内存溢出,来修改它的值 .
|
| -- 枚举常量
| enum weekday {MON =1, TUE, WED, THU, FRI, SAT, SUN };
|
| 变量 就是在内存中间中声明一段 可读 可写 的数据空间, 变量 的 内存地址 是唯一的.
x x
xx-------------------------------------------------------------------------------------xx
1 数据类型 (限制 占用内存大小 的 关键字)
-- char
硬件最小单位:bit; 软件最小单位 char; [ char(B)=8 bit ]
char [1 byte]= 8bit = 256 种状态.
进制: 十进制 八进制 十六进制 二进制
在硬件描述中 一个char _ _ _ _ _ _ _ _ 8bit 的内存 空间
如果我这样写:
char a = 0x1a;
转换成2进制就是 0001 1010 这样就是对这8个空间为进行赋值.
-- int 编译器能处理的最优处理单位.
32位系统 一个周期能走满 32bit == 4B 就是int 的大小.
16位系统 一个周期能读取 16bit == 2B 就是 shot (int) 的大小.
64位系统 一个周期能读取 64bit == 8B 就是int 的大小.
#所以, int的大小不是固定的,是根据操作系统和编译器所决定 .
什么时候用int? 什么时候用char?
如果用来 描述 数据, 就用int. a.容量大; b.能达到系统的最优处理大小.
如果用来 描述 硬件, 一般使用char.
-- long 至少4B 大小的数据长度.
long的长度最少是4byte.什么意思呢?意义体现在16位的操作系统中. 在16位的系统中,int的大小只有2byte,
2b == 65353 种状态.如果超过了,就要使用 bouble 或者 long .因为long至少保证 4byte的大小.
-- unsigned 和 signed 区别 最高字节 是 符号位 还是 普通的数据
unsigned 无符号 一般用来采集数据
signed 有符号 就是用来定义用于计算的数值
二者在位移运算中体现差异.因为有符号数的第一位是符号位正0 负1.
char a = -1 (0xff) a 在不断左移的过程中, 并不会变成0, 因为 符号位 一直在占位.
unsigned char b = 0xff b 在不断右移过后,会变成 0x00.
-- float 和 double 浮点运算
浮点数 在 内存中的表现形式 跟 整数 是完全不一样的 .
float 4byte
double 8byte 非常浪费内存空间,但是精确.
-- void 这是最神奇的一种数据类型.
因为它可以转换成 任何数据类型,它更多的意义是 声明 标志,而不是 使用 标志 .
void a; a = (int)b;
2 自定义数据类型
----->x<------------->x<--------------->x<--------------->x<------------->x<-----
-- struct !!! 结构体 重点掌握,内存空间的灵魂.
组合的概念.将不同的数据类型,组合在一起.
struct student{ //定义
char name[20];
unsigned int age;
unsigned int id;
};
struct student liming; //申请内存空间.
结构体的顺序是有要求的.因为在内存空间位置不一样.
-- union 共用体 共用同一个起始地址的组合.
结构体的话,每一个 变量的地址 是 接在 上一个变量地址 后面的.
共用体则是不同的变量共同享用同一个起始地址.后一个变量将前一个变量覆盖.
-- enum
略 . 就是一堆数字集合 ?? 反正就是不重要 .
-- typedef
typedef实在在工程中是必须用到的, 也是无时不刻在使用的. 那它存在的意义是什么呢? 它本身的意义又是什么呢?
假设定义 int a = 170; 我们可以知道 a 是 int 类型的 .然后呢?
那如果我们这样写:
typedef int long_t;
long_t a = 170;
这样我们就知道 ,原来 a 指的是长度 .所以 这个关键字的用法就是帮助我们摆脱没有意义的阅读 .
给变量带上有意义的描述, 这就是这个关键字的核心所在 .
在大工程的代码阅读中, 我碰到了无数的变量定义, 全靠这个关键字啊, 不然谁知道你的a b c 是什么东东 .
另外, 自定义的名称一般都是xx_t 这样你一看就知道, 这是一个 typedef. 追溯一下就会知道, 它原本的类型 .
常见的有 uint8_t uint16_t time_t 等等...
自定义类型到这里就结束了. 没什么语法, 就是多读代码. 去适应它 .
不对, 结构体这么重要, 不多解释一下吗 ?因为结构体很重要, 所以到时候会专门在内存空间那里整理出来.
最后吐槽, 真的忙啊. 时间不够怎么办...
3 逻辑结构
----->x<------------->x<--------------->x<--------------->x<------------->x<-----
-- 选择 if-else switch-case-default
-- 循环 while do-while for
while 其实比for重要, 使用的场景也比for要多很多. 可能刚开始学c语言, 都是用for来循环, 真心觉得这是一个坑 .
while 常用于 抽象层次的循环 ,
for 常用于 具体次数上的循环 .
最后要我选,果然还是while 是比较强大的 .
-- continue break goto
真不知道这一部分可以总结什么.emm...
4 类型修饰符
一个变量 a ,其实应该拥有两种属性. 一个是大小,我们用 int 等来描述,另一个,就是空间.
我这个变量 拥有int 这么大的空间,但是这段空间在哪里呢? 这就是类型修饰符做的事.
内存空间有这么一些地方: 堆 栈 寄存器 常量池 等等...
-- auto
默认情况 比如 int a ; char b ,其实都是auto int a ,auto char b;
而auto是默认保存在栈空间的, 这是一段可读可写的空间 .
(auto)int a;
-- register
寄存器修饰符, 将一个变量保存在寄存器中, 这样, cpu读取这个变量的时候, 速度就非常快 .
所以我们可以将频繁使用的变量用register来描述 .
当然, 并不是你把所有变量都register来描述之后, 速度都会变快, 因为 硬件的调度能力是有限的
编译器会尽量地安排 寄存器来存放这些变量, 如果寄存器不够了, 那 变量还是存在在存储器中 .
!!如果,将 a 描述成 寄存器变量的话, '&' 取地址符号是语法错误的.因为编译器已经不把a当成内存变量了.!!!
register int a;
int main(){
register int a;
printf("%d",&a); //语法错误,因为 a是register 字符.无法&.
}
-- static
3种数据:
(1) 函数内部的变量
(2) 函数外部的变量
(3) 函数
-- extern
外部扩展修饰字.
-- const
常量修饰符, 多用来描述只读的变量, 比如字符串. 它的意义是告诉我们,这段数据我不会修改它.
为什么说是, 不能修改的变量呢, 因为说是不允许修改, 但我们还是可以通过指针的内存泄漏,开获取,或者改变这个值.
const int a = 100; a是整型常量变量, 它是一种不安全的常量,实际的属性还是可写的, 所以实际上是不可变的变量.
-- volatile
不优化. 这个关键字, 我们在嵌入式中, 是经常用到的. !!这个算是比较隐含的,我做如下解释:
----------------------------->
int a = 100;
while(a == 100);
led_on();
假设声明一个变量 a 的值是100; 然后其实a的值是跟键盘相关联; 当按下一个按键后, a的值就会发生改变;
当a一直是100的时候, while就一直循环; 当按键按下后, a的值发生改变. 这样就会跳出循环, 执行led_on()这个函数.
那么, 编译器会怎么翻译这段代码呢?
------------------------------>
f1: ldr r0 , [a]
f2: cmp r0 , #100
f3: jmpeq f1
f4: led_on()
这是我们本意, 先将a的值赋给r0, 然后和100比较, 如果相等的话, 就跳转到f1, 直到f3不相等, 执行f4.
但是, f2是在寄存器内操作的, 效率非常快, 相比较, f1要从内存读数据, 是很缓慢的. 而且, 在while这条语句中
并没有对a的值发生改变, cpu想, a的值根本不可能发生改变. 既然不会发生改变, 那就没必要 从内存缓慢地读取一个数据
所以, 如果没有使用 volatile的话, 就会变成
jmpeq f2;
这样就会一直处于死循环 .
所以, volatile 禁止优化修饰符, 常用由硬件触发的变量 .
volatile char key; 这样cpu就会不停地从内存读取key的变化, 而不会忽略它 .
1.5 杂项
-- sizeof 无符号的长类型lu
用来查看 变量 在 内存空间 的 大小 .
-- return
函数返回 .