C语言(三)- 结构体、结构体指针、位运算

时间:2024-03-03 07:44:39

一、结构体

1、一般形式

不同类型数据组成的组合型数据结构,即结构体。

结构体类型的一般形式:

1 struct 结构体名{
2 类型名  成员名1;
3 类型名  成员名2;
4 类型名  成员名3;
5 ...... 6 };

举个例子:

 1 #include<stdio.h>
 2 int main(void)
 3 {
 4 struct Date
 5     {
 6        int month;
 7        int day;
 8        int year;
 9     };
10 struct Student
11     {
12        int num;
13        char name[20];
14        char sex;
15        int age;
16        struct Date birthday;    //birthday是一个struct Date结构体类型变量,即birthday使用了strcut Date结构体的数据结构形式
17        char addr[30];
18     };
19 }

2、定义结构体类型变量

 1 struct Date           //声明结构体类型,再定义是此类型的变量
 2     {
 3        int month;
 4        int day;
 5        int year;
 6     };
 7 struct Date birthday1,birthday2;
 8 
 9 或者
10 
11 struct Date            //在声明结构体类型的同时定义是此类型的变量
12     {
13        int month;
14        int day;
15        int year;
16     } birthday1,birthday2;

3、结构体变量初始化和引用

 使用上述  struct Date  结构体来进行说明。

3.1初始化

 1 struct Date                        //声明结构体同时初始化       
 2     {
 3        int month;
 4        int day;
 5        int year;
 6     } birthday1={620,2015};       //char字符型变量使用" "
 7 
 8 
 9 或者
10 
11 struct Date birthday1={620,2015}; //先如上声明结构体,再初始化
12 
13 
14 除此之外还可以对某一单一成员变量初始化:
15 struct Date birthday1={.month=6}; 

3.2成员变量操作

结构体变量名.成员名

如:birthday1.month

结构体变量名.成员名.子成员名

如:student1.birthday1.month

注: 

(1) .  是成员运算符,所有运算符中优先级最高,使用后相当于一个整体,即一个变量。

(2)成员变量及之间可以进行算术运算。

(3)同类的结构体变量可以相互赋值,如:birthday1=birthday2

(4)可以引用结构体成员变量的地址,也可以引用结构体变量的地址。

1 scanf("%d",&birthday1.month);          //输入&birthday1.month的值
2 
3 printf("%d",&birthday1);               //输出结构体变量&birthday1的首地址,即birthday1.month

(5)在scanf函数中,如果结构体成员变量是数组,其本身就代表地址,前面不需要添加取地址符&。 

二、结构体数组

 结构体数组一般形式:

 1 struct  机构体名
 2 {
 3 2 类型名  成员名1;
 4 3 类型名  成员名2;
 5 4 类型名  成员名3;
 6 5 ......
 7 }数组名[ 数组长度 ];
 8 
 9 或者
10 
11 结构体类型  数组名[ 数组长度 ];        //先声明好结构体类型

举例子:

 1 #include<string.h>      //字符数组头文件
 2 #include<stdio.h>
 3 struct Person
 4     {
 5     char name[20];       //参选人名
 6     int count;                //合计票数
 7     }leader[3]={"ergouzi",0,"xiaobai",0,"goudaner",0};
 8 //定义结构体数组并初始化,三个人起始都是0票
 9 
10 
11 或者
12 
13 strcut Person leader[3]={"ergouzi",0,"xiaobai",0,"goudaner",0};

注:结构体数组相对于结构体,简化了结构体变量定义,通过数组一次性定义很多结构体变量。

三、结构体指针

一个变量的地址称为该变量的指针,指针即内存地址。

①指针就是地址,地址就是指针;②地址是内存单元的编号;③指针变量就是存放内存地址的变量。

&:取变量地址运算符,一般后接整型浮点型变量。*:取指针变量所指向的变量的内容的运算符,后接指针变量(即地址)。

1 int x,y;
2 int *p;     //表示p是指向整型量的指针变量,此时没有赋指针变量值,更不存在指向变量的内容。
3 x=10;
4 p=&x;       //将整型变量x的地址赋给了p,此时存在了指向变量的内容
5 y=*p;       //将p所指向的变量的内容赋给了y,   即y=10

结构指针是指向一种结构体类型的指针变量,它是结构体在内存中的首地址。

地址:在内存中每8bit物理长度(即1字节)给的一个编号。在stm32f103zet6中存储器有4GB的地址空间:

4GB的地址空间按照功能划分成8块,以Block 0为例:地址总数为0x2000 0000,换算成十进制为2*(16^7)= 536870912B = 512*1024*1024B =512*1024KB = 512MB

stm32f103zet6为32处理器,寄存器也是32位。8位一个地址,单个寄存器的地址偏移量为4个地址。

地址在C语言编程时,0x0000 0004在编译器来看只是一个十六进制数字,需要地址强制转换(unsigned int *)0x0000 0004。

 以stm32f103zet6中GPIOB寄存器的封装为例讲解结构体指针:

 1 /*
 2 GPIOx(A-G)是片上外设,为了系统化和结构化的封装寄存器,引入了结构体指针
 3  */
 4 /*外设基地址*/
 5 #define PERIPH_BASE                 ((unsign int)0x40000000)
 6 /*总线基地址*/
 7 #define APB1PERIPH_BASE          PERIPH_BASE
 8 #define APB2PERIPH_BASE          ((unsign int)0x00010000)
 9 #define AHBPERIPH_BASE           ((unsign int)0x00020000)
10 /*GPIO外设基地址*/
11 #define GPIOA_BASE                  (APB2PERIPH_BASE + 0x0800)
12 #define GPIOB_BASE                  (APB2PERIPH_BASE + 0x0C00)
13 #define GPIOC_BASE                  (APB2PERIPH_BASE + 0x1000)
14 #define GPIOD_BASE                  (APB2PERIPH_BASE + 0x1400)
15 #define GPIOE_BASE                  (APB2PERIPH_BASE + 0x1800)
16 #define GPIOF_BASE                  (APB2PERIPH_BASE + 0x1C00)
17 #define GPIOG_BASE                  (APB2PERIPH_BASE + 0x2000)
18 /*寄存器基地址,GPIOB为例*/
19 #define GPIOB_CRL                    (GPIOB_BASE + 0x00)
20 #define GPIOB_CRH                    (GPIOB_BASE + 0x04)
21 #define GPIOB_IDR                    (GPIOB_BASE + 0x08)
22 #define GPIOB_ODR                    (GPIOB_BASE + 0x0C)
23 #define GPIOB_BSRR                   (GPIOB_BASE + 0x10)
24 #define GPIOB_BRR                    (GPIOB_BASE + 0x14)
25 #define GPIOB_LCKR                   (GPIOB_BASE + 0x18)
26 /*此时已知寄存器具体地址,可以通过指针&*赋值操作控制寄存器*/
27 /*GPIOx都有一组功能相似的寄存器,只是地址不同却要为每个寄存器定义地址。为了方便访问寄存器,引入结构体封装寄存器*/
28 
29 
30 /*使用结构体封装GPIO寄存器组*/
31 typedef unsigned           int  uint32_t;     //无符号32位变量
32 typedef unsigned short   int  uint16_t;    //无符号16位变量
33 /*GPIO寄存器列表*/
34 typedef struct{
35       uint32_t CRL;          //端口配置低寄存器      地址偏移:0x00
36       uint32_t CRH;         //端口配置高寄存器      地址偏移:0x04
37       uint32_t IDR;          //数据输入寄存器         地址偏移:0x08
38       uint32_t ODR;         //数据输出寄存器         地址偏移:0x0C
39       uint32_t BSRR;       //位设置/清除寄存器      地址偏移:0x10
40       uint32_t BRR;         //端口位清除寄存器       地址偏移:0x14
41       uint16_t LCKR;       //端口配置锁定寄存器     地址偏移:0x18
42 }GPIO_TypeDef;
43 /*以上代码用typedef关键字声明了名为GPIO_TypeDef的结构体,结构体有七个成员变量。C语法,结构体内变量的存储空间是连续的,32位变量占4字节,16变量占2字节,连续排列。
因此,给结构体设置好首地址,那么结构体成员的地址就确定下来,然后以结构体的形式访问寄存器。
*/ 44 45 /*结构体指针访问寄存器*/ 46 /*一般过程*/ 47 GPIO_TypeDef *GPIOx;//定义一个GPIO_TypeDef型结构体指针GPIOx 48 GPIOx = GPIOB_BASE;//GPIOx指针变量,把指针地址设为宏GPIOB_BASE地址 49 GPIOx->CRL = 0xFFFF; 50 uint32_t temp; 51 temp = GPIOx->CRL;//读取GPIOx_CRL寄存器的值到变量temp中 52 53 /*定义好GPIO端口首地址指针*/ 54 #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) 55 #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) 56 #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) 57 #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) 58 #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) 59 #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) 60 #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) 61 #define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) 62 /*使用定义好的宏直接访问GPIOB端口的寄存器*/ 63 GPIOB->BSRR = 0xFFFF; 64 GPIOB->CRL = 0xFFFF; 65 GPIOB->ODR = 0xFFFF; 66 uint32_t temp; 67 temp = GPIOB->IDR;//读取GPIOB_IDR寄存器的值到变量temp中 68 69 70 /*(以下是访问结构体成员的说明)声明了两个变量,一个是指向结构GPIO_TypeDef的结构指针PGPIOB,GPIOB是一个GPIO_TypeDef结构变量*/ 71 struct GPIO_TypeDef *PGPIOB,GPIOB; 72 PGPIOB = &GPIOB; 73 /*结构体变量访问成员变量(以下三者是等价的)*/ 74 GPIOB.ODR = 0xFFFF; 75 /*结构体指针访问成员变量*/ 76 (*PGPIOB).ODR = 0xFFFF;//*优先级低于.,所以加() 77 PGPIOB->ODR = 0xFFFF;

三、位运算(二进制运算)

基本运算符:

&按位与:均为1结果为1,否则为0。

|按位或:有一个为1,结果为1。

^按位异或:二进位相异,结果为1。

~取反:0变1,1变0。

<<左移:<<左边的运算数的各二进制左移若干位(<<右边的数为指定的位移数)

>>右移:>>左边的运算数的各二进制右移若干位(>>右边的数为指定的位移数)

 

 1 /*某一位清零:  a &= ~(1<<x)*,二进制a右侧bitx清零/
 2 
 3 unsigned char a= 0x9f;       //a=1001 1111 b
 4 a &= ~(1<<2);                  //对bit2清零
 5 /*
 6      0000 0100 b                 //b表示二进制数
 7      1111 1011 b
 8      a = (1001 1111 b)&(1111 1011 b)
 9      a = 1001 1011 b
10 */
11 
12 
13 /*连续位清零:a &= ~(y<<x*k)*,y运算数,x一组数的位数,k组数*/
14 a &= ~(3<<2*1); //运算数为3,每组位数为2位,清零第1组
15                            //bit0、bit1为第0组,bit2、bit3为第1组
16 /*
17      0000 1100 b
18      1111 0011 b
19      a = (1001 1111 b)&(1111 0011 b)
20      a = 1001 0011 b
21 */
22 
23 
24 /*连续位赋值*/
25 unsigned char b= 1000 0011b;
26 b |= (1<<2*2);
27 /*
28      0001 0000 b
29      b = (1000 0011 b)|(0001 0000 b)
30      b = 1001 0011 b
31 */
32 
33 
34 /*某一位取反*/
35 //此时b = 1001 0011 b
36 b ^=(1<<6)                      //bit6取反
37 /*
38      0100 0000 b
39      b = (1001 0011 b)^(0100 0000 b)
40      b = 1101 0011 b
41 */

 

7