前言

在学习STM32的过程中,通常需要用C语言进行编程,由于STM32的内存空间是有限的,因此熟悉C语言的数据类型可以帮助开发者更有效地管理内存。

C语言中不同的数据类型在占用内存和处理速度上也有所不同,编译器需要这个变量的类型来确定这个变量占用内存的字节数和这一段内存的解析方法。

正确使用数据类型可以减少编程错误,使得代码更易于调试和维护,尤其是在复杂的嵌入式项目中。


一、基本数据类型

在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。C语言的基本数据类型大致可以分为四大类:

  • 整数型
  • 字符型
  • 浮点型

1.整数型

整数类型主要用于表示整数,没有小数部分的数值。整型数据可分为:基本型、短整型、长整型和无符号型四种。

  • 基本型:以 int 表示。
  • 短整型:以 short 表示。
  • 长整型:以 long 表示。
  • 无符号型:类型说明符为unsigned,需要搭配前面的int、short、long一起使用。
关键字 名称 位数 表示范围
int 整型 32 -2147483648 ~ 2147483647
unsigned int 无符号整型 32 0 ~ 4294967295
short 短整型 16 -32768 ~ 32767
unsigned short 无符号短整型 16 0~65535
long 长整型 32 -2147483648 ~ 2147483647
unsigned long 无符号长整型 32 0 ~ 4294967295
long long 长长整型 64 -(2^64)/2 ~ (2^64)/2-1
unsigned long long 无符号长长整型 64 0 ~ (2^64)-1

注:一个字节(byte)=8位(bit),值范围就是2的几次方,比如char型是一个字节,也就是8位,所以char型变量的取值最大就是2^8=255。

在51单片机中int型占16位,STM32中int型占32位,shout型占26位,Char 型在单片机中通常用来存放整数而不是字符。

2.字符型

字符型常用于表示单个字符或者整个字符串中的某个字符。一个字符型的变量可以表示256种不同的字符,包括小写字母、大写字母、数字、标点符号、空格等。字符型分为字符型和无符号字符型两种。

关键字 名称 位数 表示范围
char 字符型 8 -128 ~ 127
unsigned char 无符号字符型 8 0 ~ 255

字符和整数没有本质的区别,char 变量在内存中存储的是字符对应的 ASCII 码值。
例如:char类型中的字母C,在int类型中表示67,数字67是字母C对应的ASCIl码值。

字符 ASCll码值
空格 32
数字0~9 48~57
大写字母A~Z 65~90
小写字母a~z 97~122
换行符 10
制表符 9

3.浮点型

用于表示实数数值,即具有小数部分的数。和整数不同,浮点型的数值可以是小数、分数、乘方等形式的。浮点型可分为:单精度浮点型和双精度浮点型两种。

  • 单精度浮点型:以 fioat 表示。
  • 双精度浮点型:以 double 表示。
关键字 名称 位数 表示范围
float 单精度浮点型 32 -3.4e38 ~ 3.4e38
double 双精度浮点型 64 -1.7e308 ~ 1.7e308

注:一般来说,使用double可以获得更高的精度,但同时也会消耗更多的内存。由于内存对于程序的性能影响非常大,所以在选择使用哪种类型的浮点数时,需要权衡精度和内存的需求。

C语言中,输出double和float类型时, 默认输出的是6位小数,不足6位,以0补齐,超过6位按四舍五入截断。

二、C语言宏定义

宏在编译前由预处理器替换为其定义的内容,常用于定义常量或者复杂的代码块。通常用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改。

关键字:#define

代码示例如下:

#define PI 3.1415926   //用PI代替3.1415926,新名字在左边,末尾不用加分号
int main() 
{
	int a = PI;  //等效于int a = 3.1415926;
}

三、C语言typedef

将一个比较长的变量类型名换个名字,便于使用。

关键字:typedef

代码示例如下:

typedef unsigned char uint8_t;   //用uint8_t代替unsigned char,新名字在右边,末尾加分号
int main() 
{
	uint8_t a;  //等效于unsigned char a;
}

例如:在STM32的stm32f103_gpio.h中利用typedef对GPIO引脚寄存器地址进行了重命名。如下图所示:
在这里插入图片描述

四、C语言枚举

枚举是 C 语言中的一种基本数据类型,用于定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合,它可以让数据更简洁,更易读。
关键字:enum

如果我们定义一个变量用来储存星期的值,那理论上这个变量只能取1到7的值,如果定义的整形的变量,那这个变量就随意存什么数值都可以,不会受到限制,这时可能出现数据不合法,比如星期8的情况出现。
或者我们需要使用 #define 来为每个整数定义一个别名:

#define MON  1
#define TUE  2
#define WED  3
#define THU  4
#define FRI  5
#define SAT  6
#define SUN  7

所以这个时候,为了让程序更安全,就可以定义一个取值都限制的整型变量,这个变量就是枚举。

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
}day;

注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。

也可以在定义枚举类型时改变枚举元素的值:

enum SEASON 
{
	spring, summer=3, autumn, winter
}season;

没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5

在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。

不过在一些特殊的情况下,枚举类型必须连续是可以实现有条件的遍历。

以下实例使用 for 来遍历枚举的元素:

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
int main()
{
    // 遍历枚举元素
    for (day = MON; day <= SUN; day++) 
    {
        printf("枚举元素:%d \n", day);
    }
}

以上实例输出结果为:

枚举元素:1
枚举元素:2
枚举元素:3
枚举元素:4
枚举元素:5
枚举元素:6
枚举元素:7

五、C语言结构体

结构体定义了一个包含多个成员的新的数据类型,结构体中的数据成员可以是基本数据类型(如 int、float、char 等),也可以是其他结构体类型、指针类型等。

结构体定义由关键字 struct 和结构体名组成,结构体名可以根据需要自行定义。struct 语句的格式如下:

struct tag {
member-list
member-list
member-list

} variable-list ;

tag 是结构体标签。
member-list 是标准的变量定义,比如 int i; 或者 float f;,或者其他有效的变量定义。
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。

结构体变量类型较长,所以通常用typedef更改变量类型名。

typedef struct{     //定义结构体
   char  x;
   int  y;
   float  z;
} StructName;  
//用·引用结构体成员:
StructName.x = 'A';
StructName.y = 66;
StructName.z = 1.23;
//或结构体指针引用:
pStructName->x = 'A';
pStructName->y = 66;
pStructName->z = 1.23;

指针引用:结构体是一种组合数据类型,在函数之间的数据传递中通常用的是地址传递而不是值传递。
和其它类型变量一样,对结构体变量可以在定义时指定初始值。

#include <stdio.h>

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};

int main()
{
    printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}

执行输出结果为:
title : C 语言
author: RUNOOB
subject: 编程语言
book_id: 123456

结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针.
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:

struct B;    //对结构体B进行不完整声明

//结构体A中包含指向结构体B的指针
struct A
{
    struct B *partner;
    //other members;
};

//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
    struct A *partner;
    //other members;
};

使用结构体的整个过程:机构体就是一个数据打包的过程,首先将参数写到结构体的三个变量里,统一打包,京结构体传递到函数里,接着函数里面再将这个结构体拆包出来,读取变量。

六、STM32综合应用实例解析

在STM32中,当我们需要点亮一个LED时,就需要对LED连接的引脚进行初始化(代码如下),其中我们就使用了结构体对GPIO进行配置。

#include "stm32f10x.h" 

void LED_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOA, GPIO_Pin_1 | GPIO_Pin_2);
}

选中"GPIO_InitTypeDef",右键跳转到定义
在这里插入图片描述
我们可以看到GPIO_InitTypeDef这个结构体中有三个成员
在这里插入图片描述
选中"GPIospeed_TypeDef",调转到定义,我们可以看到,这是一个枚举
在这里插入图片描述
我们可以看到,这里运用typedef对枚举进行了重命名,枚举中让GPIO_Speed_10MHz = 1,根据枚举的特性,以此类推我们可以得到GPIO_Speed_2MHz = 2,GPIO_Speed_50MHz = 3.
因此我们可以知道,结构体中的第二个成员GPIO_Speed,只能等于枚举中的其中一个,不可以为其他值。
在这里插入图片描述
当我们在配置时,选择了其中一个,自动也就等于了枚举中对应的值。
在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐