STM32中的C语言知识总结
(1)与、或运算
① “|”:或0则保持不变,或1则置1;
② “&”:与0则清0,与1则保持不变;
举例:
对数据中的某一位进行赋值时,需要先将这一位进行清0,然后再赋值。在清0时,其他位则保持不变,使用“&”即可。然后赋值时用“|”。
另外还有非、异或等
(2)静态变量和静态函数(static)
所有未加static 前缀的全局变量(这里的全局变量指在源文件的开头处,不包含在源文件的任何函数内)和函数都具有全局可见性,其它的源文件也能访问。static声明的函数和变量不能在另一个文件中引用,也就是说,如果加了 static,就会对其它源文件隐藏,无法进行调用。
利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。static 可以用作函数和变量的前缀,对于函数来讲,static 的作用仅限于隐藏,而对于变量,static 还有下面两个作用。
① 存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。在下次使用静态变量时,则不会在去初始化。
② 默认初始化初值为0。在静态存储区共有两种变量存储:全局变量和 static 变量,而且在静态数据区,内存中所有的字节默认值都是0x00
③ 总结:首先static 的最主要功能是隐藏,其次因为 static 变量存放在静态存储区,所以它具备持久性和默认值0。
(3)#ifdef 和 #ifndef
#ifdef和 #endif是一对使用;如果定义了标识符,则编译此段程序。
(4)extern变量申明
对于extern申明变量可以多次,但定义只有一次.
(5)Typedef
typedef用于为现有类型创建一个新的名字,或称为类型别名,用来简化变量的定义。typedef在MDK用得最多的就是定义结构体的类型别名和枚举类型了。
如:typedef struct
{
u16 seq_num;
u16 len;
u8 dev_id[GPRS_PRO_DEVICE_ID];
u16 command_id;
u8 *pro_data;
}gprs_pro_cmd_t;
然后我们就可以直接使用gprs_pro_cmd_t定义一个结构体变量。
(6)结构体
[if !supportLists]① [endif]一般形式:Struct 结构体名{ 成员列表; }变量名列表;
[if !supportLists]② [endif]这样是声明后直接定义,也可以先声明,后定义;
如:
Struct 结构体名字 { 成员列表; };
例如:struct U_TYPE usart1,usart2;
注意:结构体指针成员变量引用方法是通过“->”符号实现;
运用:在我们定义函数的形参时,形参数量较多的情况下,一旦修改就会很麻烦。因此将形参使用结构体进行统一定义,进行组合。
(7)关于函数中结构体的参数传递
在进行传值的时候,用结构体封装有利于函数的传递。
用指向结构体变量的指针作为函数参数。
用结构体变量的引用变量作函数参数
(8)文件的包含问题
#include操作是,若后面带的是<>,则文件在安装路径中找;
若后面带的是“”,则文件在源目录中找。
(9)Volatile 语句
变量前若有加volatile 这个关键字,则每当系统用到这个变量时,则必须重新读取这个变量的值。就会追溯到源地址中存储的数据去取数据,保证数据正确。
这种语句被大量用来描述一个对应于内存映射的输入输出端口,或者寄存器,如IO口的寄存器等。每次到寄存器地址中读取数据。
(10)Enue
相当于变量a好几个可能出现的数值,放入enum中表示同一个类型的值。
在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的。
(11)C存储类
有四种类型:auto、 register、static、extern
① Auto
auto 存储类是所有局部变量默认的存储类,auto 只能用在函数内,即 auto 只能修饰局部变量;
② Register
register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。
register int miles;
寄存器只用于需要快速访问的变量,比如计数器。
③ static和extern在前面已经说过。
④ 总结:
auto 是局部变量的默认存储类, 限定变量只能在函数内部使用;
register 代表了寄存器变量,不在内存中使用;
static是全局变量的默认存储类,表示变量在程序生命周期内可见;
extern 表示全局变量,即对程序内所有文件可见,类似于Java中的public关键字;
(12)C 语言中全局变量、局部变量、静态全局变量、静态局部变量的区别
从作用域看:
1、全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。
2、静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
3、局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
4、静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
从分配内存空间看:
1、全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间。
2、全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
1) 静态变量会被放在程序的静态数据存储区(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2) 变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。
Tips:
A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,全局可见;
D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带"内部存储器"功能的的函数)
E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
(13)inline关键字
inline关键字是用于函数声明或定义,可以把函数指定为内联函数。而且关键字inline必须与函数定义放在一起才能使函数成为内联,仅仅将inline放在函数声明前是不起任何作用的。
内联函数inline必须要定义在头文件中,这与通常的函数定义是不一样的。
(14)C 命令行参数 命令参数_网址
int main( int argc, char *argv[] )
argv[0] 存储程序的名称,argv[1] 是一个指向第一个命令行参数的指针,*argv[n] 是最后一个参数。如果没有提供任何参数,argc 将为 1,否则,如果传递了一个参数,argc 将被设置为 2。
(15)存储空间 堆和栈的区别
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放5、程序代码区—存放函数体的二进制代码。
例子:
#include <stdio.h>
int a = 0;全局初始化区
char *p1;全局未初始化区
void main()
{
int b;栈
char s[] = "abc";栈
char *p2;栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。 strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
注意:i++和++i单独时,没有区别。当作为右值时,i++是先赋值后加,而++i是先加后赋值。
(15)栈空间的分配问题
如:
void fun(int a,int b)
{
int c; //栈
int d; //栈
} //在栈区的存储顺序是c,d,a,b; //分配顺序就是:顺序局部变量、顺序参数
注意:for循环中应该遵循左闭右开的区间规则
(16)C位域
(17)C强制类型转换
强制类型转换是把变量从一种类型转换为另一种数据类型。
记录于2019-08-29 郑州