学习C、查阅C基本语法好去处:
C 语言教程 | 菜鸟教程
www.runoob.com

嵌入式C的好习惯:
①、常量定义为大写字母形式
②、在指针变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。
数据类型:

各数据类型取值范围:

1、数据的 移位、置1、清0、取反
移位 :??>>x ??右移x位
例如:x=PB_IDR>>2&0x01怎么理解?
PB_IDR>>2是将PB_IDR中的数据右移两位,&0x01是求与,这样x即为PB引脚2的状态
置1: a |=(1<<x); a的第x位置1
清0:a &= ~(1<<x); a的第x位清0
取反:a^=(1<<x); a的第x位取反
2、 结构体 struct
结构体类似于面向对象语言中的属性类的定义,是用户自己定义的数据结构。
结构体定义方法:
①、 struct 结构体名
{
成员列表;
};
struct 结构体名 变量名;
-----------------------------------------------------
②、 struct 结构体名
{
成员列表;
}变量名;
----------------------------------------------------
③、 struct //没有结构体名
{
成员列表;
}变量名; 结构体变量的使用形式:
结构体变量名.成员名结构体指针:
要知道,指针就是地址,那结构体指针就是结构体首地址。
而指针变量是存放地址的容器,结构体指针变量是存放结构体的首地址容器。
struct 结构体名 *结构指针名;访问其中一个结构体元素的成员变量
结构体指针名->成员名等同于
(*结构体指针名).成员名3. extent
变量的声明有两种情况:
1、一种是需要建立存储空间的。例如:int a 在声明的时候就已经建立了存储空间。
2、另一种是不需要建立存储空间的,通过使用extern关键字声明变量名而不定义它。 例如:extern int a 其中变量 a 可以在别的文件中定义的。
extern int i; //声明,不是定义
int i; //声明,也是定义如果需要在一个源文件中引用另外一个源文件中定义的变量,我们只需在引用的文件中将变量加上 extern 关键字的声明即可。
4.常量格式:
整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。
整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
下面列举几个整数常量的实例:
212 /* 合法的 */
215u /* 合法的 */
0xFeeL /* 合法的 */
078 /* 非法的:8 不是八进制的数字 */
032UU /* 非法的:不能重复后缀 */以下是各种类型的整数常量的实例:
85 /* 十进制 */
0213 /* 八进制 */
0x4b /* 十六进制 */
30 /* 整数 */
30u /* 无符号整数 */
30l /* 长整数 */
30ul /* 无符号长整数 */浮点常量
浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。
当使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的。
下面列举几个浮点常量的实例:
3.14159 /* 合法的 */
314159E-5L /* 合法的 */
510E /* 非法的:不完整的指数 */
210f /* 非法的:没有小数或指数 */
.e55 /* 非法的:缺少整数或分数 */转义字符

5. 嵌入C语言 预处理
==预处理==命令可以改变程序设计环境,提高编程效率,
它们并不是 C 语言本身的组成部分,不能直接对 它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理” 。 经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对==预处理==之后的源程序进行==编译==处理,得到可供执行的目标代码。
所有的预处理器命令都是以井号(#)开头。
C 语言提供的预处理功能有三种,分别为==宏定义==、文件包含和条件编译。
//========宏定义=========================
#define MAX 10 // 宏定义
#ifdef // 如果宏已经定义,则返回真
#ifndef //如果宏没有定义,则返回真
#undef //取消已定义的宏
//=======文件包含========================
#include <stdio.h> //系统库中获取 stdio.h
#include "myheader.h" // 从本地目录中获取 myheader.h
//=======条件编译========================
#if //如果给定条件为真,则编译下面代码
#else //#if 的替代方案
#elif //如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif //结束一个 #if……#else 条件编译块
#error //当遇到标准错误时,输出错误消息预处理器运算符
C 预处理器提供了下列的运算符来帮助您创建宏:
①宏延续运算符(\)
一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。例如:
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")②字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。例子如上。
③标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。
#include <stdio.h>
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void)
{
int token34 = 40;
tokenpaster(34);
return 0;
}当上面的代码被编译和执行时,它会产生下列结果:
token34 = 406. #define 宏定义
宏定义只是将一段字符取了另一个名字,在编译器预处理时会将其还原回其原有的样子。
使用创建常量
#define identifier value
例如:
#define LENGTH 10
#define WIDTH 5带参宏定义:
/*带参宏定义*/
#define M(y) y*y+3*y宏定义优点:
宏只占编译时间,函数调用则占用运行时间,每次执行都要载入,所以执行相对宏会较慢。宏定义在编译期间即会使用并替换,而全局变量要到运行时才可以。
所以,适当的时候,我们可以用宏定义来代替一些变量或函数。
例如:
int square(int x) {
return x * x;
}我们可以使用宏重写上面的代码,如下:
#define square(x) ((x) * (x)) 7. const 创建常量
const int Max=100; 则Max++会报错。
8. 位域
位域是为了防止资源空间浪费而在结构体定义属性长度的语法,在结构内声明位域的形式如下:
struct
{
type [member_name] : width ;
};例如:
如果程序的结构中包含多个开关量,只有 TRUE/FALSE 变量,如下:
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status; 这种结构需要 8 字节的内存空间,但在实际上,在每个变量中,我们只存储 0 或 1。在这种情况下,C 语言提供了一种更好的利用内存空间的方式。如果您在结构内使用这样的变量,您可以定义变量的宽度来告诉编译器,您将只使用这些字节。例如,上面的结构可以重写成:
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status; 这样节省了空间。
超出位域一般编译器会有警告,数据只会保留位域宽度内的部分。
9.typedef
C 语言提供了 typedef 关键字,您可以使用它来为类型取一个新的名字。
例如:
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;typedef 与 #define的区别
#define 是 C 指令,用于为各种数据类型定义别名,与 typedef 类似,但是它们有以下几点不同:
- typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
- typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
10.指针
指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。
type *var-name;例如:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */所有实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是一样的,都是一个代表内存地址的长的十六进制数。
把变量地址赋值给指针
ip = &var; /* 在指针变量中存储 var 的地址 */访问指针变量中可用地址的值
printf("Value of *ip variable: %d\n", *ip );NULL 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。
指针运算
C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。
指针数组
可以创建一个指针数组用循环语句指向一个数组
ptr[i] = &var[i]; 也可以用一个指向字符的指针数组来存储一个字符串列表
const char *names[] = {
"Zara Ali",
"Hina Ali",
"Nuha Ali",
"Sara Ali",
};指针的指针
指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。
一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。
int **var;传递指针给函数
C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。
void getSeconds(unsigned long *par)能接受指针作为参数的函数,也能接受数组作为参数
int main ()
{
int balance[5] = {1000, 2, 3, 17, 50};
double avg;
avg = getAverage( balance, 5 ) ;
}
double getAverage(int *arr, int size)
{
/*。。。。*/
}从函数返回指针
C 允许您从函数返回指针。为了做到这点,您必须声明一个返回指针的函数
int * myFunction()
{
}另外,C 语言不支持在调用函数时返回局部变量的地址,除非定义局部变量为static变量。
11.函数指针
函数指针是指向函数的指针变量。函数指针可以像一般函数一样,用于调用函数、传递参数。
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型例如:
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}12.枚举
枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读。
enum 枚举名 {枚举元素1,枚举元素2,……};例如:
enum DAY
{
MON, TUE, WED, THU, FRI, SAT, SUN
};这样 MON即为0 TUE为1.。。。。。。。
也可以自行编号,但真的没必要;
枚举类型的变量
定义完枚举类型后我们应该定义枚举类型的变量
①先定义,再实例
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;②定义的同时实例化
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;③匿名枚举实例化
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;上面的语句就相当于 #define MON 1
13.sizeof
sizeof是c语言中用来求字节运算符
sizeof(基本数据结构)
这里的基本数据类型是指short、int、long、float、double这样的简单内置数据类型。
由于它们的内存大小是和系统相关的,所以在不同的系统下取值可能不同。
sizeof(数组)
u8 talbe[]={0x01,0x02,0xab,.......}
sizeof(table)当sizeof的对象是数组时,返回数组总字节大小,而当对象是指针时,返回指针本身的大小,而不是指示内存空间的大小。
注意:1)当字符数组表示字符串时,其sizeof值将’/0’计算进去。
2)当数组为形参时,其sizeof值相当于指针的sizeof值。
sizeof(指针)
当对象是指针时,返回指针本身的大小,而不是指示内存空间的大小
sizeof(函数)
sizeof也可对一个函数调用求值,其结果是函数返回值类型的大小,函数并不会被调用。
注意:1)不可以对返回值类型为空的函数求值。
2)不可以对函数名求值。
3)对有参数的函数,在用sizeof时,须写上实参表。
sizeof(结构体)
结构体的sizeof涉及到字节对齐问题。为什么需要字节对齐?计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,依次类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
一般不会求解此项(除非为了对比结构体和共用体😎)。
空结构体(不含数据成员)的sizeof值为1。
sizeof(联合体)
结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存;所以整个联合体的sizeof也就是所有成员sizeof中的最大值。
14.typeof
C语言中 typeof 关键字是用来定义变量数据类型的。
15. 联合体(共用体)
联合体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
共用体不是一个集合类型!!!不能存多个数据!!他只是一个类型可变的数据。
为了定义共用体,您必须使用union语句,方式与定义结构类似。union 语句定义了一个新的数据类型,带有多个成员。union 语句的格式如下:
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];例如:
union Data
{
int i;
float f;
char str[20];
} data;共用体占用的内存应足够存储共用体中最大的成员。例如,在上面的实例中,Data 将占用 20 个字节的内存空间,因为在各个成员中,字符串所占用的空间是最大的。下面的实例将显示上面的共用体占用的总内存大小:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
}为了访问共用体的成员,我们使用成员访问运算符(.)。成员访问运算符是共用体变量名称和我们要访问的共用体成员之间的一个句号。您可以使用union关键字来定义共用体类型的变量。
include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}运行结果:
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming我们可以看到共用体的i和f成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是str成员能够完好输出的原因。
16.“->”
->是一个整体,它是用于指向结构体子数据的指针,用来取子数据。
换种说法,如果我们在C语言中定义了一个结构体,然后申明一个指针指向这个结构体,那么我们要用指针取出结构体中的数据,就要用到“->”。
问题中的p=p->next ,意思是将p指向的一个结构体实例中的自数据next赋值给p。

17. #include <??.h> 和 #include "??.h"的区别
#include <stm32f10x.h> 和 #include "stm32f10x.h"的区别是
<>表示去软件根目录中找这个头文件
“” 表示在项目目录下找头文件
18. 宏注释 😀
# if 0
//想被注释的代码
#end if19.stdbool.h
bool 是C++中的关键字,C中不支持
#define true 1
#define false 0
#define bool _Bool
typdef int _Bool
20.static关键字的作用
1.隐藏
一个函数如果加了static,就会对其它源文件隐藏。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。
对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。
2.保持变量内容的持久
存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。
3.默认初始化为0
其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加'\ 0'太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是'\0'。
21.跨文件全局变量
要跨文件(同一个工程中)使用同一个变量(值同步改变),变量声明和定义方法:
例:
首先在aaaa.c定义全局变量:int y,并在aaaa.h中声明:extern int y;
在另外一个xxx.c文件中想要使用上述变量,包含头文件aaaa.h即可使用。