[百问网 7Day物联网智能家居实战训练]C语言基础 一
数据类型

以STM32F103C8这一款芯片为例,这是一块32bit的MCU,基本数据类型在此款芯片中的数据长度,
以及在HAL库函数中的定义(stdint.h文件中的定义,采用C99标准)
建议:使用库定义的数据类型,来定义变量或函数。
const 用法
不仅仅是用于定义常量,还可以用于修饰数组、指针、函数参数等。
修饰变量
对变量声明为只读特性,并保护变量值以防被修改
const定义变量的同时还必须对其初始化
const int i = 5;//i具有只读性,不能够被修改;若想对其重新赋值,例如i=10则是错误的用法
const可以放在数据类型的前面或者后面
int const i = 5;
const修饰变量还起到了节约空间的目的,通常编译器并不给普通const只读变量分配空间,而是将它们保存在符号列表中,无需读写内存操作,程序执行效率也会提高。
修饰数组
const int array[5] = {0, 1, 2, 3, 4};
// 或
int const array[5] = {0, 1, 2, 3, 4};
//具有只读性,不可修改,一旦被更改程序会出错
修饰指针
两种形式:
- 用来限定指向空间的值不可修改;
- 限定指针不可修改
int i = 5; int k = 10; int const *p1 = &i; // /* 指针p1,const修饰的是*p1,即p1指向的空间的值不可改变 *p1 = 20;就是错误的用法;但是p1的值是可以改变的,例如p1 = &k;则没有任何问题。 */ int * const p2 = &k; /* *指针p2,const修饰的是p2,即指针本身p2不可更改, 而指针指向空间的值是可以改变的,例如*p2= 15;是没有问题的,而p2 = &i;则是错误的用法。 */
修饰函数参数
const修饰函数参数对参数起限定作用,防止其在函数内部被意外修改,所限定的参数可以是
普通变量也可以是指针变量
void fun(const int x)
{
...
x = 10; // 对 x 的值进行了修改,错误
}
void fun1(const int *p)
{
...
(*p)++; // 对 p 指向空间的值进行了修改,错误
}
作用域与 static 用法
C变量的作用域:
块作用域、函数作用域、函数原型作用域或文件作用域。
局部变量:其有效范围是在被定义的函数内,函数执行完毕后变量即被释放;
全局变量:如果把这个变量定义在函数体外
int k = 0;
void fun3(void)
{
for(k=0; k<10; k++)
{
...
}
}
extern 用法
指明函数或变量定义在其它文件中,提示编译器遇到此函数或者变量的时候到其它模块去寻找其定义,这样被extern声明的函数或变量就可以被本模块或其它模块使用。因而,extern关键字修饰的函数或者变量是一个声明而不是定义
/* example.c */
uint16_t a = 0;
uint16_t max(uint16_t i, uint16_t j)
{
return ((i>j)?i:j);
}
/* main.c */
#include <stdio.h>
extern uint16_t a;
extern uint16_t max(uint16_t i, uint16_t j);
void main(void)
{
printf("a=%d\r\n", a);
printf("Max number between 5 and 9: %d\r\n", max(5, 9));
}
extern关键字还有一个重要的作用,就是如果在C++程序中要引用C语言的文件,则需要用以下格式:
#ifdef __cplusplus
extern "C"{
#endif /* #ifdef __cplusplus */
......
#ifdef __cplusplus
}
#endif /* #ifdef __cplusplus */
volatile 用法
用volatile关键字声明的变量,在每次对其值进行引用的时候都会从原始地址取值。
用来消除编译器的优化:
针对其的任何赋值或者获取值操作都会被执行(而不会被优化)。
三种情景:
1) 修饰硬件寄存器;
2) 修饰中断服务函数中的非自动变量;
3) 在有操作系统的工程中修饰会被多个应用修改的变量;
修饰硬件寄存器
/**
* @brief General Purpose I/O
*/
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
} GPIO_TypeDef;
//__IO的定义是:
#define __IO volatile /*!< Defines 'read / write' permissions */
//定义GPIO是:
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
//GPIOx_BASE的定义
#define GPIOA_BASE (APB2PERIPH_BASE + 0x00000800UL)
在有操作系统的工程中修饰会被多个任务修改的变量
有多个任务在对同一个变量进行赋值
或取值,那么这一类变量也应使用volatile来修饰保证其可见性。所谓可见即:当前任务修改了这一变量的值,
同一时刻,其它任务此变量的值也发生了变化。
struct 用法
灵活的表示多种数据
struct [结构体名]
{
类型标识符 成员名 1;
类型标识符 成员名 2;
.
.
.
类型标识符 成员名 n;
};
//声明
//struct [结构体名] 结构体变量;
示例:
struct students
{
char name[50];
char sex[50];
int age;
float score;
};
int main(void)
{
struct students student;
printf("Name: %s\t",student.name[0]);
printf("Sex: %s\t", student.sex);
printf("Age: %d\t", student.age);
printf("Score: %f\r\n", student.score);
return 0;
}
结构体声明位置:
|
声明位置 |
作用范围 |
|
函数的外部 |
只限于函数内部使用 |
|
函数的内部 |
该声明之后的所有函数都能使用它的标记 |
结构有两层含义:
一层含义是“结构布局”,如上述例子的struct student{…};告诉编译器如何表示数据,但是它并未让编译器为数据分配空间;
另一层含义是:创建一个结构体变量,如上述例子的struct students student;编译器执行这行代码便创建了一个结构体变量student,编译器使用students模板为该变量分配空间:

enum 用法
修饰枚举类型变量的关键字
enum常量是int类型,因此只要能使用int类型的地方就可以使用枚举类型)。枚举类型的目的是提高程序的可读性
enum [枚举类型名]
{
枚举符 1,
枚举符 2
.
.
.
枚举符 n,
};
//例如:
enum color
{
red,
green,
blue,
yellow
};
//enum常量
printf("red=%d, green=%d", red, green);
//enum默认值 常量都被赋予0,1,2等
enum fruit{banana, grape, apple};
//enum赋值
enum levels{low=90, medium=80, high=100};
enum feline{cat, lynx=10, puma, tiger};
//cat=0,lynx、puma、tiger的值分别是10、11、12。
typef def 用法
用typedef可以为某一类型自定义名称
与#define区别:
1) 与#define不同,typedef创建的符号只受限于类型,不能用于值;
2) tyedef由编译器解释,不是预处理器;
3) 在其受限范围内,typedef比#define更灵活;
typedef unsigned char BYTE;
使用 BYTE 来定义变量
BYTE x, y[10];
作用域:取决于typedef定义所在的位置。
|
定义位置 |
作用域 |
|
|
定义在函数中 |
局部作用域 |
受限于定义所在的函数 |
|
函数外面 |
文件作用域 |
用typedef来命名一个结构体类型的时候,可以省略该结构的标签(struct):
typedef struct
{
char name[50];
unsigned int age;
float score;
}student_info;
student_info student={“Bob”, 15, 90.5};
//这样使用typedef定义的类型名会被翻译成:
struct {char name[50]; unsigned int age; float score;}
student = {“Bob”, 15, 90.5};
//tyedef常用于给复杂的类型命名,例如: typedef void (*pFunction)(void);

浙公网安备 33010602011771号