文章目录
1 进制转化
1.1 二进制0B
逢二进一
1.2 十六进制0X
逢十六进一
1.3 二进制和十六进制转换(8421码)
1位十六进制数 = 4位二进制数,通过8421码计算
二进制数不够4的倍数时补0
- 比如101 1100,写成0101 1100
2 指针
指针就是变量,是用来存放地址的变量,存放在指针中的值都被当做地址来处理
地址是唯一标识一块地址空间的
2.1 * 和&
变量前符号 | 简介 | 详情 |
---|---|---|
什么都不加 | 变量的值 | 根据变量的类型,解析变量所在内存地址中的值 |
& | 变量地址 | 获得变量的内存地址 |
* | 寻址并解析 | 寻找地址等于变量值的内存,根据变量类型解析这块内存中的值 |
int a = 0; // 定义一个int类型变量a
int* point_a; // 定义一个指向 int类型变量 的指针
point_a = &a; //把 变量a 的内存地址 赋值给 point_a
//以下为比喻-------------
Car aodi = ....; // 有一辆奥迪车,它的颜色是...长...宽.........
Car* carNum; // 生产一个车牌
carNum = &aodi; // 把这个车牌挂到车上
2.2 内存中分配
指针的值其实是一个内存地址值,而不是具体值。具体的值保存在变量 a 中。
printf("%d", &a); // a 变量的地址, 0x8004
printf("%d", point_a); // 变量 point_a 的值 0x8004
printf("%d", a); // 变量 a 的值 0
printf("%d", *point_a); // 变量 point_a 值被按照int解析后的值 0
2.3 指针变量和指向关系
用来保存 指针 的变量,就是指针变量。如果指针变量p1保存了变量 num的地址,则就说:p1指向了变量num,也可以说p1指向了num所在的内存块 ,这种指向关系,在图中一般用 箭头表示。
上图中,指针变量p1指向了num所在的内存块 ,即从地址0028FF40开始的4个byte 的内存块。
2.4 常指针及常量指针const关键字
有时候我们希望定义这样一种变量,它的值不能被改变,在整个作用域中都保持固定。
例如,用一个变量来表示班级的最大人数,或者表示缓冲区的大小。
为了满足这一要求,可以使用const关键字对变量加以限定。
将 const 变量称为常量(Constant)。
常量一旦被创建后其值就不能再改变,所以常量必须在定义的同时赋值(初始化),后面的任何赋值行为都将引发错误。
#include <stdio.h>
const int a = 20;
int b = 30;
int y;
int *p;
/*
普通指针不能指向常量,可以指向变量
*/
const int *p1;
int const *p2;
/*
常量指针:指针变量可以改变,不能用这个指针修改指针指向的内存的值。
指针可以指向常量,也可以指向变量
*/
int * const p3 = &b;
int const * const p4 = &a; // 指向常量的常指针。指针指向的值不能被修改(*),指针变量(指向的内存)也不能被修改(p4)
/*
常指针:指针变量的值不能被改变,可以通过常指针修改常指针指向的变量的值,只能指向变量。
定义时候需要初始化,初始化后不能修改,和static定义变量的用法一样
*/
/*
区分常量指针和常指针不能被修改的量的技巧:
const后面的量不能被修改.
例如:
const int *p1; const后面是*p1,指针指向的值,不能被修改
int * const p3 = &b; const后面是p3,指针变量的值(内存地址),不能被修改
int const * const p4 = &a; const后面是*,指针指向的值不能被修改(*);const后面是p4,指针变量的值(内存地址),不能被修改
*/
int main(){
p1 = &a;
/*
&a的结果是一个指针,类型是int*,指向的类型是int,指向的地址是a 的地址
把 变量a 的内存地址 赋值给 p1,p1指向了a
*/
// p = &a; // 报错。原因:普通指针:不能指向常量。
printf("p1: %d\n", p1); // 4210688
y = *p1; // 通过指针p1取出a的值赋值给y
printf("y: %d\n", y); // 20
p1 = &b; // p1指向了b
printf("p1: %d\n", p1); // 4206608
y = *p1; // 通过指针p1取出b的值赋值给y
printf("y: %d\n", y); // 30
// *p1 = 50; //报错。原因:常量指针:不能用这个指针修改指针指向的内存的值。
printf("p3: %d\n", p3);
printf("b初始值: %d\n", b); // 30
*p3 = 50; //p3原本指向b,b初始为30。p3为常指针,常指针可以通过指针修改指向的变量的值,也就是修改b的值
printf("常指针修改了b的值,b: %d\n", b); // 50
printf("p3: %d\n", p3);
return 0;
}
2.5 typedef关键字——函数指针的简化
/*函数指针的简化*/
#include <stdio.h>
/*
typedef关键字的作用:
对现有的类型重命名,不会产生新的类型。
简化复杂的表达式。
typedef关键字的书写规则:
1、用类型定义变量名
2、在定义变量名之前加上typedef关键字
*/
// typedef char INT8;
// INT8 a,b,c; //相当于char a,b,c
// typedef struct{
// /* data */
// }PERSON_S;
// PERSON_S p1, p2;
/*
函数指针的使用形式:
1、直接使用函数指针的定义式来定义函数指针变量
2、定义函数指针数组
3、函数的形参含有函数指针(回调函数)
4、函数的返回值是函数指针
5、函数的返回值和函数的形参都含有函数指针。
*/
//1、直接使用函数指针的定义式来定义函数指针变量
typedef int (*PFUN) (int x);
PFUN p1,p2 = cube;
//2、定义函数指针数组
PFUN a[2] = {square, cube}; // 等价于:int (* a[2]) (int x) = {square, cube};
// 3、函数的形参含有函数指针(回调函数)
int f1(PFUN p, int x){ // 等价于:int f1(int (*PFUN) (int x), int x)
return (*p)(x);
}
// 4、函数的返回值是函数指针
PFUN f2(int x){ // 等价于:int (*f2(int x)) (int x)
return a[x];
}
// 5、函数的返回值和函数的形参都含有函数指针。
PFUN f3(PFUN p){ // 等价于:int (*f3 (int (*PFUN)(int x))) (int x)
return p;
}
int y;
int main(){
int square(int x);
int cube(int x);
// PFUN = square;
// y = (*PFUN)(2);
// printf("y=2*2=%d\n", y);
// PFUN = cube;
// y = (*PFUN)(2);
// printf("y=2*2*2=%d\n", y);
y = (*a[0])(2); //4
y = f1(square, 3); //9
p1 = f2(1);
y = (*p1)(2); //8
}
int square(int x){
return x*x;
}
int cube(int x){
return x*x*x;
}
3 结构体
3.1 c语言的集合数据类型
3.2结构体用法
3.2.1 x作为变量
3.2.2 x作为数据类型
4 位运算
位运算是将数字以二进制形式进行计算的运算符。
与其他运算符不同,C语言中位运算,顾名思义,是以数值的二进制位为单位进行操作的,包含<<(左移)、>>(右移)、~(按位取反)、&(按位与)、|(按位或)、^(按位异或) 共六种运算符。
C语言的六种位运算:
位运算 | 功能 |
---|---|
左移运算符 << | 向左(高位)移位,右侧补0;本质是乘以2的N次方 |
右移运算符 >> | 向右(低位)移位,左侧补0;本质是除以2的N次方 |
按位取反 ~ | 如名,即0变1,1变0 |
按位与 & | 相对应的两个位都为1则为1,反之为0 |
按位或 | | 相对应的两个位至少有一个为1即为1,反之为0 |
按位异或 ^ | 相对应的两个位相同为0,相异(不同)为1 |
4.1 六种位运算
4.1.1 按位与(&)
有0为0,全是1为1。
按位与运算符的作用:
-
清零
我们可以对某一个数与0进行按位与运算,由于两个位都为1才为1,因此最终全部位都变为0,起到清零的作用 -
取指定位
如某些存储场景下,“第13位表示xxxx“”,我们需要取出13位,则可以让原数值与数字7进行按位与运算,得到的结果即是原数值的1~3位的值。 -
判断奇偶
可以发现,数字的奇偶取决于二进制位的最低一位是1还是0,因此只需要与1按位与运算,判断是1是0即可得知奇偶。
4.1.2 按位或(|)
有1为1
按位或运算符的作用:
对一个数字的指定位,置为1。
如“某个数字的第七位”表示开关,原先是0,需要改为1的状态,即可以将这个数字与64按位或,即可得到第七位变为1,其余位的值依旧不变。
4.1.3 按位异或(^)
相同为0,不同为1
异或运算符的作用
-
指定位数的翻转
如想对某个数字的低4位进行翻转,则可以将这个数字与15(二进制为00001111)进行按位异或运算,既可以将原数字的低四位进行翻转,即高四位不变,低四位0变1,1变0 -
与0异或还是原值
大家可以自行实验,一个数字与0进行异或,结果还是原值 -
交换两个数字
除了之前我们学习交换两个数字需要第三个变量做中介之外,如今可以通过异或运算进行,代码如下:
#include<stdio.h>
int swap(int *a,int *b)
{
if (*a!=*b)
{
*a=*a^*b;
*b=*b^*a;
*a=*a^*b;
}
return 0;
}
int main()
{
int a=5;
int b=5;
swap(&a,&b);
printf("a=%d b=%d\n",a,b); //a为3,b为5
return 0;
}
4.1.4 按位取反(~)
01互换
4.1.5 左移运算(<<)
对应十进制,左移几位,乘几次2。
左移N位的本质是乘以2的N次方。
4.1.6 右移运算(>>)
对应十进制,右移几位,除以几次2。
右移N位的本质是除以2的N次方。
4.2 交换律和结合律
& | ^满足交换律和结合律
4.3 例题
输入5个数,两对相同,找出那个不同的数?
采用位异或(^)运算和他的结合律。
a ^ a = 0;
a ^ 0 = a;
#include <stdio.h>
int main(){
int n[5];
printf("输入5个数字:\n");
for(int i=0; i<5; i++){
scanf("%d", &n[i]);
}
int ans = 0;
for(int j=0; j<5; j++){
ans = ans ^ n[j];
}
printf("落单的数字为:%d", ans);
}
5 单片机的地址和状态
软件 = 地址 + 状态 → 硬件
5.1 STM32F103X8-B数据手册
储存器图
所有地址的起始地址:0X0000 0000
所有地址容量的最大值:0XFFFF FFFF
5.2 STM32F10XXX
29页