主题:C语言
时间:2021年1月6日
作者:ybb
参考: C语言
10.3 C语言结构体指针
定义:当一个指针变量指向结构体时,我们就称它为结构体指针(指针变量指向的是结构体)
一般结构:struct 结构体标签名 *变量名
struct stu *pstu=&stu1;
注:结构体变量名和数组名不同,数组名在表达式中会被转换成指针,而结构体变量名不会,它时钟表示的是整个集合本身,要想取得结构体变量的地址,就需要用&
还应该注意,结构体和结构体变量是两个不同的概念:结构体是一种数据类型,是一种创建变量的模板,编译器不会为它分配内存空间,就像 int、float、char 这些关键字本身不占用内存一样;结构体变量才包含实实在在的数据,才需要内存来存储。下面的写法是错误的,不可能去取一个结构体名的地址,也不能将它赋值给其他变量:
struct stu *pstu = &stu;
struct stu *pstu = stu;
获取结构体成员采用:
(*pointer).Member
p->Member
注:结构体成员访问运算符.
结构体指针成员访问运算符
结构体数组指针的使用:
#include<stdio.h>
#include<stdlib.h>
struct stu
{
char *name;
int age;
float score;
}
stus[] = {
{"ybb",24,99.8},{"ld",25,88.9},{"zds",26,99.9}
}, *ps;
int main()
{
int len;
len = sizeof(stus) / sizeof(struct stu);
printf("name\tage\tscore\t\n");
for (ps = stus; ps <stus + len; ps++)//这个语句是什么意思???
{
printf("%s\t%d\t%.2f\t\n", ps->name, ps->age, ps->score);
}
return 0;
}
10.4 CC语言枚举类型(enum的使用)
先用最简单的宏定义实现:define A with B
#include<stdio.h>
#include<stdlib.h>
#define mon 1
#define tue 2
#define wed 3
int main()
{
int day;
scanf_s("%d",&day);
switch (day)
{
default:printf("error");
break;
case mon:
printf("monday");
break;
case tue:
printf("tuesday");
break;
case wed:
{
printf("wednesday");
break;
}
}
return 0;
}
不拿发现,如果数量很多就很麻烦,而且数字的增长是有规律的,可以考虑利用enum实现。
printf()与puts()
scanf()与gets()
那么就来学习一下enum的使用:
enum 枚举类型名 {value1name,value2name} a,b,c;
枚举是一种类型,通过它可以定义枚举变量:
enum week a,b,c;
#include<stdio.h>
#include<stdlib.h>
int main()
{
enum week
{
mon=1,tue,wed
}day;
scanf_s("%d",&day);
switch (day)
{
default:printf("error\n");
break;
case mon:
printf("monday");
break;
case tue:
printf("tuesday");
break;
case wed:
printf("wednesday");
break;
}
return 0;
}
需要注意的两点是:
枚举列表中的 Mon、Tues、Wed 这些标识符的作用范围是全局的(严格来说是 main()
函数内部),不能再定义与它们名字相同的变量。Mon、Tues、Wed 等都是常量,不能对它们赋值,只能将它们的值赋给其他的变量。
宏定义在预处理阶段将名字替换成对应的值;
枚举在编译阶段将名字替换成对应的值;
因此枚举可以理解为发生在编译阶段的宏。
case 关键字后面必须是一个整数,或者是结果为整数的表达式,但不能包含任何变量
谈到C语言最关键的指针、数组、结构体与内存管理,C语言执行的高效性需要对内存管理深入学习,这样才可以掌握C语言的精髓。
10.5 C语言共用体
结构体与共用体的区别:
结构体的各个成员占据不同的内存,当然为了内存对齐加快寻址效率,各个成员占据长度相同的内存空间;
共用体的所有成员占据同一段内存,修改其中的一个会影响其他的成员。
10.6 大端小端及判别方式
大端和小端是指数据在内存中的存储模式,它由 CPU 决定:
-
大端模式(Big-endian)是指将数据的低位(比如 1234 中的 34 就是低位)放在内存的高地址上,而数据的高位(比如 1234 中的 12 就是高位)放在内存的低地址上。这种存储模式有点儿类似于把数据当作字符串顺序处理,地址由小到大增加,而数据从高位往低位存放。
-
小端模式(Little-endian)是指将数据的低位放在内存的低地址上,而数据的高位放在内存的高地址上。这种存储模式将地址的高低和数据的大小结合起来,高地址存放数值较大的部分,低地址存放数值较小的部分,这和我们的思维习惯是一致,比较容易理解。
PC 机上使用的是 X86 结构的 CPU,它是小端模式(正常模式)
很多 ARM、DSP 也是小端模式(部分 ARM 处理器还可以由硬件来选择是大端模式还是小端模式)
51 单片机是大端模式;
10.7 C语言位域
C语言标准规定,位域的宽度不能超过它所依附的数据类型的长度。通俗地讲,成员变量都是有类型的,这个类型限制了成员变量的最大长度,:后面的数字不能超过这个长度
原则:超出部分被直接截去
结构体使用的不熟练,需要加强。
那些是数据类型、那些是变量、如何分开定义、如何在声明的时候就定义,这些都是需要掌握的。
什么是位域:
位域技术就是在成员变量所占用的内存中选出一部分位宽来存储数据。
我们发现位域成员往往不占用完整的字节,有时候也不处于字节的开头位置,因此使用&获取位域成员的地址是没有意义的,C语言也禁止这样做。地址是字节(Byte)的编号,而不是位(Bit)的编号。
无名位域:
无名位域一般用来作填充或者调整成员位置。因为没有名称,无名位域不能使用。
10.8 C语言位运算
C语言的六种位运算符;
区分逻辑运算符与位运算符
区分原码 反码 补码
注:&是根据内存中的二进制位进行运算的,而不是数据的二进制形式。
再强调一遍,&是根据内存中的二进制位进行运算的,而不是数据的二进制形式;其他位运算符也一样。以-9&5为例,-9 的在内存中的存储和 -9 的二进制形式截然不同:
1111 1111 – 1111 1111 – 1111 1111 – 1111 0111 (-9 在内存中的存储)
-0000 0000 – 0000 0000 – 0000 0000 – 0000 1001 (-9 的二进制形式,前面多余的 0 可以抹掉)
- 整数在内存中是如何存储的?
- 小数在内存中是如何存储的?
- 正数和负数在内存中是如何存储的?
- 复数在内存中是如何存储的?
- 数据在内存中的存储方式?
10.9 使用位运算对数据进行加密
通过一次异或运算,生成密文,密文没有可读性,与原文风马牛不相及,这就是加密;
密文再经过一次异或运算,就会还原成原文,这就是解密的过程;
加密和解密需要相同的密钥,如果密钥不对,是无法成功解密的。
如果加密和解密的密钥不同,则称为非对称加密算法。在非对称算法中,加密的密钥称为公钥,解密的密钥称为私钥,只知道公钥是无法解密的,还必须知道私钥。