
刚开始学习时,存在很多疑问,例如如何使用指针,如何匹配结构和指针,例如功能参数的要求,如何实时更新IO端口数据如果您重新学习C语言,则必须学习很长时间才能系统地理解它. 本文将简单地整理出难以记住的知识点.
1,#ifdef和#ifndef
#ifdef标识符A //如果定义了标识符A,则编译程序段1,否则编译程序段2
程序1
#else
程序2
#endif
#ifndef的功能与#ifdef的功能相反,后者在未定义标识符A时编译块1.
2. 全局定义
在软件选项中,有一列,上面填写的变量表示在所有文件中,上面的徽标均已定义.
#ifdef STM32F10X_HD
大容量芯片需要一些变量定义
#end
3. 外部变量声明
可以使用C语言将外部变量放在变量或函数的前面,以指示变量或函数的定义在另一个文件中. 当遇到此变量和函数时,它会提示编译器在其他模块中找到其定义. 这里应该注意,对于外部变量,可以多次声明该变量,但只能声明一次.
外部u16 USART_RX_STA;
此语句声明USART_RX_STA变量已在其他文件中定义,并在此处使用.
下面以一个示例来说明如何使用它.
在Main.c中定义的全局变量id在Main.c中初始化.
Main.c文件
u8 id; //定义只允许一次
main(){
id = 1; printf(“ d%”,id); // id = 1
test();
printf(“ d%”,id); // id = 2
}
但是我们希望在test.c
在changeId(无效)函数中使用变量id. 此时,我们需要声明变量id是在test.c中将无法到达其中的test.c文件.
在下面的test.c中查看代码:
外部u8 id; //声明变量id是在外部定义的,并且声明可以在许多文件中完成
无效测试(void){id = 2;}
声明变量id是在test.c中外部定义的,然后在test.c中使用变量id.
4. Typedef类型别名
Typedef用于为现有类型或类型别名创建新名称,以简化变量的定义. Typedef是MDK中最常用于定义类型别名和枚举结构的类型.
struct _GPIO {__IO uint32_t CRL; __IO uint32_t CRH;…};
定义了GPIO结构,因此我们定义变量的方式是:
struct _GPIO GPIOA; //定义结构变量GPIOA
但这很乏味. 在这里,我们可以为结构定义别名GPIO_TypeDef,以便我们可以通过别处的别名GPIO_TypeDef定义结构变量.
方法如下:

typedef struct {
__ IO uint32_t CRL; __IO uint32_t CRH;…} GPIO_TypeDef;
Typedef为结构定义别名GPIO_TypeDef,
这样,我们可以通过GPIO_TypeDef定义结构变量: GPIO_TypeDef _GPIOA,_GPIOB;
此处的GPIO_TypeDef等效于_GPIO结构.
5. 结构
声明结构类型: 结构结构名称{成员列表;}变量名称列表;例如:
结构U_TYPE {Int BaudRate Int WordLength;} usart1,usart2;
可以在声明结构时定义变量,也可以在声明之后定义变量. 方法是:
结构名称结构变量名;例如: struct U_TYPE usart1,usart2;
引用结构成员变量的方法是: 结构变量名称. 会员名
例如,引用usart1的成员BaudRate,方法是: usart1.BaudRate;
结构指针变量的定义也相同,与其他变量没有区别.
例如: struct U_TYPE * usart3; //定义结构指针变量usart1;
结构指针成员的变量引用方法是通过“->”符号实现的,
例如,要访问usart3结构的指针所指向的结构的成员变量BaudRate,方法是:
Usart3-> BaudRate;
在开发微控制器程序期间,我们经常遇到要初始化的设备,例如串行端口. 它的初始化状态由几个属性确定,例如序列号,波特率,极性和模式. 对于这种情况,当我们没有学习结构时,我们的一般方法是:
void USART_Init(u8 usartx,u32,u32 BaudRate,u8奇偶校验,u8模式);
此方法在某些情况下是有效且理想的. 但是请考虑一下,如果有一天我们想将另一个参数传递给该函数,那么我们必须修改此函数的定义并重新输入字长的输入参数. 但是,如果函数的输入参数随着开发的增加而增加,我们是否必须不断修改函数的定义?这会给我们带来很多开发上的麻烦吗?那么如何解决呢?
因此,如果使用结构,则可以解决此问题. 仅通过更改结构的成员变量,就可以实现更改输入参数而无需更改输入参数的上述目的.
我们可以通过定义结构来组合它们. 这是在MDK中定义的:
typedef struct {uint32_t USART_BaudRate;
uint16_t USART_WordLength;
uint16_t USART_StopBits;
uint16_t USART_Parity;
uint16_t USART_Mode;
uint16_t USART_HardwareFlowControl;} USART_InitTypeDef;
因此,当我们初始化串行端口时,入口参数可以是USART_InitTypeDef类型的变量或指针变量,这是在MDK中完成的: void
USART_Init(USART_TypeDef * USARTx,USART_InitTypeDef * USART_InitStruct);
通过这种方式,我们只需修改结构成员变量并将新的成员变量添加到结构中,而无需修改函数定义即可达到修改入口参数的相同目的.
6. 关于函数中结构的参数传递
在ST库函数中,结构的用法很多. 如第5点所述,使用结构进行封装有助于功能的传递.
以下是摘录的一些解释,很典型.
在ST结构参数传输中,有指针类型和结构地址类型.
(1)使用结构变量名称作为参数.
#include
#include
使用命名空间标准;

struct学生{
字符串名称;
int得分;
};
int main(){
学生一;
无效打印(学生一);
one.name =“千手”;
one.score = 99;
打印(一个);
cout <
cout <
返回0;
}
无效打印(学生一){
cout <
cout << ++ one.score <
}
此方法采用“值传递”方法,将结构变量占用的存储单元的所有存储依次传递给形式参数. 形式参数还在函数调用期间占用存储单元. 这种转移方法具有较大的空间和实际的开销. 如果结构的尺寸很大,则开销非常客观. 而且,由于采用了值传递方法,如果在函数执行过程中改变形式参数的值,则该值不能反映到调用函数中相应的实际参数上c语言入门实例,这往往不能满足使用要求. 因此,这种方法通常较少使用.
(2)使用指向结构变量的指针作为函数参数
#include
#include
使用命名空间标准;
struct学生{
字符串名称;
int得分;
};
int main(){
学生一;
无效打印(学生* p);
one.name =“千手”;
one.score = 99;
学生* p =&one;
打印(p);
cout <
cout <
返回0;
}
无效打印(学生* p){

cout
cout << ++ p->得分<
}
尽管此方法也是值传递的方法,但是这次传递的值是一个指针. 通过更改指针指向的结构变量的值,可以间接更改实际参数的值. 而且,在调用该函数期间,仅创建了一个指针变量,这大大减少了系统开销.
(3)将结构变量的参考变量用作函数参数
#include
#include
使用命名空间标准;
struct学生{
字符串名称;
int得分;
};
int main(){
学生一;
无效打印(学生和一名);
one.name =“千手”;
one.score = 99;
打印(一个);
cout <
cout <
返回0;
}
无效打印(学生和一名){
cout <
cout << ++ one.score <
}
实际参数是结构变量,形式参数是相应结构类型的引用,虚拟和实数组合时通过地址传递,因此执行效率较高. 而且,与作为函数参数的指针相比,它看起来更加直观和易于理解. 因此,将变量作为函数参数引用可以提高效率并保持程序可读性.
7. IMPORT指令
IMPORT指令用于通知编译器要使用的标签在其他源文件中定义,但是必须在当前源文件中引用该标签,并且无论当前源是否存在,标签都将添加到当前文件中文件是指标签源文件的符号表.
有两种构建ST项目的方法,一种是寄存器版本,另一种是固件库版本.
在新过程中,有些功能和文件不需要添加到寄存器版本中.
在注册版本的新项目之后,添加启动文件startup_stm32f10x_hd.s
(堆栈,PC初始化,向量异常地址条目初始化,调用MAIN函数),其中该教程需要注释掉以下几行(绿色部分):
Reset_Handler PROC
导出Reset_Handler [弱]
导入__main
;注册版本代码,因为未使用SystemInit函数,所以请注释掉
;库函数版本代码,建议添加此代码(SystemInit函数必须在外部实现)以初始化stm32时钟等.
; IMPORT SystemInit调用SystemInit函数
![]()
; LDR R0,= SystemInit
; BLX R0
LDR R0,= __ main
BX R0
ENDP
当系统找不到SystemInit函数时,有三种解决方法
①在外部(在任何其他.c文件中)定义函数SystemInit或一个空函数.
②放置
IMPORT SystemInit
LDR R0,= SystemInit
BLX R0
注释或删除这两个句子.
③您可以将库文件system_stm32f10x.c添加到项目中,即可解决该问题.
但是第三个方法更麻烦,因为如果您自己定义一些函数,则可能与system_stm32f10x.c
冲突.
8. 文件包含的问题
#include操作是,如果后跟<>,则在安装路径中找到该文件;
如果后跟“”,则在源目录中找到该文件.
9,易失性声明
如果在变量前添加关键字volatile,则每当系统使用此变量时,都必须重新读取该变量的值.
此类语句用于描述与内存映射或寄存器(例如IO端口寄存器)相对应的大量输入和输出端口.
如下:
int标志= 0;
void car_action()
{
同时(1)
{
如果(flag)car_go();
}
}
void car_stop()
{
flag = 1;
}
在上面的示例中,car_action并未更改标志操作,因此可能只是第一次执行car_action
仅读取标志的值. 第一次读取的值将直接在后续步骤中使用. 实际上,在car_stop中c语言入门实例,flag的值已更改.
在这种情况下,car_action函数的执行结果可能是错误的.
但是,如果在定义中使用了volatile int标志,则每次标识该标志时,都会将其跟踪到存储在源地址中的数据以获取数据,并且程序可以正常执行.
最后,如果您想学习C / C ++编程,则可以通过私人信函编辑器“ 01”获得资料和开发工具以及参加课程的权利!

本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-227599-1.html
直接动动炮肯定不妥