20160221.CCPP体系详解(0031天)
程序片段(01):01.结构体静态数组.c+02.结构体动态数组.c
内容概要:结构体数组
///01.结构体静态数组.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//01.C语言的结构体特点:
// 1.C语言要求结构体或者共用体至少得有一个成员
// 也就是说C语言并不允许空结构体或者空共用体的出现!
// 2.严格区分采用结构体类型到底定义的是哪种具体变量:
// 普通变量+指针变量+数组变量
struct
{//匿名结构体
int num;
} *pStruct, structArr[10], structVar;
//02.定义结构体的不同分类方式:
// 1.按照声明类型和定义变量的时机不同:
// 声明结构体类型的同时定义结构体变量
// 声明结构体类型之后再定义结构体变量
// 2.按照所属内存空间的不同:
// 栈内存:
// 堆内存:
// 静态区:
//注:结构体变量(普通+指针+数组)
struct MyStruct01
{//标准结构体
int id;
char str[10];
} ms01[10];
//03.结构体的两种常见分类:
// 标准结构体:标准情况
// 匿名结构体:锁定变量变量个数
//注:要想锁定变量个数就不能使用指针变量:
// 因为针对于结构体指针变量可以进行动态内存分配,于是就会
// 导致结构体变量数目的不确定性
//04.结构体初始化:
// 1.位于栈内存的结构体可以采用如同数组一样的静态初始化方式
// 2.可以采用静态默认初始化方式{0}来将整个结构体空间清空为0
// 3.如果涉及到结构体数组的形式,那么最外层{}代表结构体数组整体
// 内层的每一个{}代表一个结构体变量本身
// 4.如果涉及到一维结构体数组的初始化操作,想要省略内部的{}
// 就需要按照结构体变量一个一个按照类型一致,顺序对应的关系
// 完成初始化操作
//05.关于静态初始化:
// 1.针对于所有复合类型:
// 2.最外层{}代表整体:
// 内层{}一般代表单个,也可代表子整体
//注:任何位于栈内存的复合类型采用指针进行指向,都需要进行类型转换
// 任何位于栈内存的复合类型都可以采用{0}进行整体数据清零操作,无论
// 符合类型的嵌套级数-->复合类型快捷静态初始化操作!
int main01(void)
{
struct MyStruct01 ms02[10];
struct MyStruct01 * pMS01 = (struct MyStruct01[]) { 0 };//结构体静态初始化(默认形式)
struct MyStruct01 ms03[] = { {10, "20" },{10, "20" },{10, "20"} };
struct MyStruct01 * pMS02 = (struct MyStruct01[]) { {10, "20"}, { 10,"20" }, { 10, "20" } };
struct MyStruct01 ms04[] = { {0}, {0} };
struct MyStruct01 ms05[] = { 0 };
struct MyStruct01 ms06[][10] = { 0 };//复合类型整体初始化
system("pause");
}
struct CSDN01
{
char name[100];
char pass[100];
char email[100];
} csdnData[10]
= {
{"zdg", "12344321", "zdg@csdn.net"},
{"LaoZheng", "670203313747", "chengming_zheng@163.com"},
{"fstao", "730413", "fstao@tom.cn"}
}, *pCsdnStruct;
//06.关于数组的类型以及指向数组的指针总结:
// 1.所有数组(无论数组维度是多少),该数组的类型都是去掉最高维度数
// 2.所有指向数组(无论数组维度是多少)类型的指针,都是将最高维度变为
// 数组指针类型(*pArr)
//07.成员选择:
// 指针:箭头号
// 对象:点儿号
//注:严格注意运算符优先级特性
int main02(void)
{
pCsdnStruct = (struct CSDN01[]) {
{"zdg", "12344321", "zdg@csdn.net"},
{"LaoZheng", "670203313747", "chengming_zheng@163.com"},
{"fstao", "730413", "fstao@tom.com"}
};
for (int i = 0; i < sizeof(csdnData) / sizeof(*(csdnData + 0)); ++i)
{
//a.b (&a)->b (*p).b;
//char * p = strstr(*(*(csdnData + i)).email, "chengming_zheng@163.com");
//char * p = strstr((&csdnData[i])->email, "chengming_zheng@163.com");
//char * p = strstr((*(pCsdnStruct + i)).email, "chengming_zheng@163.com");
//char * p = strstr((pCsdnStruct + i)->email, "chengming_zheng@163.com");
char * p = strstr((*(pCsdnStruct + i)).email, "chengming_zheng@163.com");
if (p)
{
puts((*(csdnData + i)).pass);
}
}
system("pause");
}
///02.结构体动态数组.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
struct CSDN01
{
int id;
int num;
};
//01.静态开辟和动态回收:
// 1.静态开辟:
// (1).内存尺寸编译指定
// (2).未使用内存分配函数
// (3).使用的是常量决定尺寸
// (4).只能使用数组类型作为类型转换(静态类型转换)
// (5).不可进行手动回收
// 2.动态开辟:
// (1).内存尺寸运行决定
// (2).使用了内存分配函数
// (3).可以使用变量决定尺寸
// (4).只能使用指针类型作为类型转换(动态类型转换)
// (5).可以进行手动回收
//02.内存清零函数:
// 格式:memset(首地址, 数据, 字节数);
// 注意:可以指定任意内存首地址,尤其注意所需参数的特点
// 是地址层面的意义,不具备指针层面的意义
//03.数组数据的两种访问方式:
// 静态访问:中括号
// 动态访问:点儿号
int main03(void)
{
int num;
scanf("%d", &num);
struct CSDN01 * pStruct01 = (struct CSDN01 *)alloca(num * sizeof(struct CSDN01));//栈内存
struct CSDN01 * pStruct02 = (struct CSDN01 *)malloc(num * sizeof(struct CSDN01));//堆内存
//内存清零函数:memset();
memset(pStruct01, 0, sizeof(num * sizeof(struct CSDN01)));
memset(pStruct02, 0, sizeof(num * sizeof(struct CSDN01)));
//遍历动态数组[动态数组可以像静态数组那样的引用方式]
for (int i = 0; i < num; ++i)
{//快速初始化:memset();手动初始化:赋值操作方式
printf("%d, %d\t%d, %d\n", (*(pStruct01 + i)).id = i, (*(pStruct01 + i)).num = i, (*(pStruct02 + i)).id = i, (*(pStruct02 + i)).num = i);
}
for (int i = 0; i < 10; ++i)
{//投票次数
int id = 0;
scanf("%d", &id);
for (int j = 0; j < num; ++j)
{//具体投票
if ((*(pStruct01 + i)).id == id)
{
++(*(pStruct01 + i)).num;
++(*(pStruct02 + i)).num;
break;
}
}
}
for (int i = 0; i < num; ++i)
{
printf("%d, %d\t%d, %d \n", (*(pStruct01 + i)).id, (*(pStruct01 + i)).num, (*(pStruct02 + i)).id, (*(pStruct02 + i)).num);
}
free(pStruct02);
system("pause");
}
struct MyStruct01
{
int id;
char str[10];
};
int main04(void)
{
int num;
scanf("%d", &num);
struct CSDN01 * pCSDNStruct01 = (struct CSDN01 *)malloc(num * sizeof(struct CSDN01));
struct CSDN01 * pCSDNStruct02 = (struct CSDN01[]) { 1, 2, 3, 4 };//静态分配
printf("%d \n", (*(pCSDNStruct02 + 1)).id);
printf("%d \n", (pCSDNStruct02 + 1)->id);
struct CSDN01 csdnx = { 10, 20 };
struct CSDN01 * pCSDNX = &csdnx;
printf("%d \n", (*(pCSDNX)).id);
printf("%d \n", pCSDNX->id);
system("pause");
}
int main05(void)
{
struct CSDN01 csdnData[100];
struct CSDN01 * pCSDNStruct = csdnData;
struct CSDN01 csdnDataX[10][10];
struct CSDN01(*pArr01)[10] = csdnDataX;
struct CSDN01(*pArr02)[10] = (struct CSDN01(*)[10])malloc(50 * sizeof(struct CSDN01));
pArr02[3][4];
system("pause");
}
程序片段(02):结构体大小.c
内容概要:结构体大小
#include <stdio.h>
#include <stdlib.h>
//01.关于结构体内存字节对齐的相关知识:
// 1.明确基本数据类型:
// char short int long float double long long long double
// (通常情况之下:double和long long尺寸一样)
// 2.结构体的内存尺寸一定是大于或者等于所有成员标准尺寸之和
// 3.结构体内存字节准确判定方式:
// (1).确定基本对齐尺寸:
// 编译器指定尺寸+基本数据类型最宽尺寸(两者取最短)
// (2).准确判定内存字节尺寸:
// 方式一:
// 结构体最终尺寸必须可以整除对齐尺寸
// 结构体成员的首地址-结构体的首地址=偏移量(该偏移量必须是当前成员尺寸的整数倍)
// 结构体对齐单元的末尾不足部分,会被默认填充
// 方式二:
// 所有结构体在内存当中的存储形式是矩形方式
// 矩形的宽度为对齐内存尺寸
// 放进矩形的的成员相对位置必须能够整除自身尺寸
// 4.结构体如果只是存在单个成员的情况,根本就不存在结构体内存字节对齐问题:
// 所以此时的结构体尺寸就是结构体当中的第一个成员的尺寸
// 5.结构体内存字节对齐的优点:
// (1).便于快速寻址
// (2).便于节约内存
// 6.结构体内存字节优化方式:
// 1.首先确定基本数据类型最宽的成员:
// (包含结构体多层嵌套都生效的最宽基本成员)
// 2.结构体矩形的形成顺序,务必按照从内到外,从前到
// 后的顺序进行确定
// 3.并且每个结构体矩形成员必须是独立矩形!
// 4.再其次定义基本数据类型最宽的成员
// (按照不同结构体单元进行分类顺序考虑)
// 5.再其次考虑各个结构体当中当中的数组数据类型和数据类型最宽的成员关系
// 数组尺寸和对齐字节数之间的关系
// 6.再逐个按照基本数据类型宽度尺寸递减的方式定义成员
// 注意:每个独立的结构体都是按照矩形形式存储的!
// 这个矩形的宽度就是所有结构体当中数据类型最宽的那个成员
// (嵌套结构体必须保证每个结构体自身先形成矩形,从内到外进行矩形确定)
struct MyStruct01
{//默认顺序
char chr1;//8
double db2;//8
int num3;//8
char chr4;
double db5;//8
};
struct MyStruct02
{//节省顺序
double db2;//8
double db5;//8
int num3;//8
char chr1;
char chr4;
};
struct MyStruct03
{//单成员结构体不存在结构体内存字节对齐问题
char num1;
//double num4;
};
int main01(void)
{
struct MyStruct01 ms01;
struct MyStruct02 ms02;
printf("sizeof(ms01) = %d \n", sizeof(ms01));//32-->默认存储
printf("sizeof(ms02) = %d \n", sizeof(ms02));//24-->节省存储
printf("&ms01 = %p \n", &ms01);
printf("&ms01.chr1 = %p \n", &ms01.db2);
printf("&ms01.db2 = %p \n", &ms01.db2);
printf("&ms01.num3 = %p \n", &ms01.num3);
printf("&ms01.chr4 = %p \n", &ms01.chr4);
printf("&ms01.db5 = %p \n", &ms01.db5);
system("pause");
}
程序片段(03):01.Test.c+02.结构体结构体数组副本机制.c+03.指针与结构体.c
内容概要:结构体成员与指针
///01.Test.c
#include <stdio.h>
#include <stdlib.h>
//01.结构体占用字节统计:
// 1.确定内存字节对齐数:
// 编译器指定+基本数据类型最宽成员(两者取较短那个)
// 注:包括嵌套形式情况下的结构体数据类型最宽成员
// 2.从外往内,从前往后分析结构体嵌套形式:
// 注:任何一个独立的结构体本身一定是按照矩形结构存储的
// 结构体嵌套结构体-->类似于宽度相同的矩形嵌套
// 3.任何结构体成员的的起始位置减去相对矩形左外边的尺寸
// 一定是当前结构体成员字节的整数倍-->如果当前矩形行能够
// 存储下当前结构体成员,则存下,如果当前矩形行存储不下,则
// 换下一个矩形行,当前矩形行的其余位置空置
//02.结构体占用字节优化:
// 1.确定内存字节对齐数:
// 1.编译器指定+基本数据类型最宽成员(两者取较短那个)
// 2.这个内存字节对齐数用于确定每个独立结构体的矩形宽度
// 注:包括嵌套形式情况下的结构体基本数据类型最宽成员!
// 2.从外往内,从前往后进行嵌套结构体的内存字节对齐优化:
// 3.先安置与内存字节对齐数相同的基本数据类型成员
// 4.再考虑结构体类型的成员安置情况
// 5.再考虑数组类型的成员安置情况,这个需要与内存字节对齐数
// 进行综合考虑
// 6.在依次考虑基本数据类型当中从大到小的基本你数据类型安置
struct
{
char chr1;//8--1--7
double db2;//8--8--0
int num3;//8--4--0
char chr4;//0--1--0
short num5;//0--2--1
char chr6;//8--1--7
long long num7;//8--8--0
} anonymity01;//40=25+15
struct
{
long long num7;//8-8-0
double db2;//8-8-0
int num3;//8-4-0
short num5;//0--2--0
char chr1;//0--1--0
char chr4;//0--1--0
char chr6;//8--1-7
} anonymity02;//32=25+7
int main01(void)
{
printf("sizeof(anonymity01) = %d \n", sizeof(anonymity01));
printf("sizeof(anonymity02) = %d \n", sizeof(anonymity02));
system("pause");
}
///02.结构体结构体数组副本机制.c
#include <stdio.h>
#include <stdlib.h>
struct MyStruct01
{
int arr[10];
int len;
};
void change(struct MyStruct01 ms01)
{
printf("change:ms01.arr = %p \n", ms01.arr);
for (int i = 0; i < ms01.len; ++i)
{
*((ms01.arr) + i) = i * 3;
printf("%3d", *((ms01.arr) + i));
}
printf("\n");
}
//01.符合类型都可以使用静态初始化方式:
// 静态初始化方式-->大括号{}
int main02(void)
{
struct MyStruct01 ms01 = { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10 };
change(ms01);
printf("main:ms01.arr = %p \n", ms01.arr);
for (int i = 0; i < ms01.len; ++i)
{
printf("%3d", *(ms01.arr + i));
}
system("pause");
}
//02.函数副本机制:
// 1.函数的形参和返回值都具备副本机制
// 2.只是该副本机制针对于数组类型进行了优化:
// 函数副本机制唯一对数组特殊,采用的是退化指针的解决方案
//03.关于各种取地址符运算的结果:
// 1.对一维数组当中所存储的元素:
// 获取的是列指针
// 2.对一维数组整体(数组名称)
// 获取的行指针
// 3.对任意数组名称执行取地址运算符:
// 获取的都是指向数组的指针(数组指针)
//04.如何确定数组的类型以及指向数组的指针?
// 1.所有数组的类型:
// 去掉数组名+去掉最高维度系数
// 2.指向数组的指针:
// 替换数组名为(*pArr),pArr就是指向数组的指针
// 3.如何确定数组名类型:
// 去掉数组名+替换最高维度(包含中括号以及维度数)为(*),此时就是数组名的变量类型
//注:严格区分数组名的变量类型和常量类型
struct MyStruct01 testReturn()
{
struct MyStruct01 ms01 = { { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10 };
printf("testReturn:ms01.arr = %p;ms01.arr + 1 = %p \n", ms01.arr, ms01.arr + 1);
printf("testReturn:&ms01.arr = %p;&ms01.arr + 1 = %p \n", &ms01.arr, &ms01.arr + 1);
return ms01;
}
//05.函数的返回值不能是数组类型:
// 但可以是指向数组的指针类型
typedef int * intP;//int类型的指针
typedef int intArr[10];//int类型的数组
//int[] returnUnit()//数组类型-->错误
//intArr returnUnit()//数组类型-->错误
intP returnUnit(){}
int main03(void)
{
struct MyStruct01 ms01 = testReturn();
for (int i = 0; i < ms01.len; ++i)
{
printf("%3d", *(ms01.arr + i));
}
printf("\n");
system("pause");
}
struct info
{
char name[10];
int age;
};
struct info infos[3];
struct info infos[];
struct info * pInfo;
void changeInfo(struct info * pInfo)
{//数组名作为函数形参将会退化和数组名相同的变量指针类型
//pInfo = pInfo + 3;
(*(pInfo + 1)).age = 88;
}
//06.通过结构体来描述结构体动态数组:
// 结构体可以用于描述任何数据类型
struct data
{
struct info * pArr;//数组首元素变量指针
int len;
};
int main04(void)
{
struct info infos[3] = { {"fang", 18}, {"hua", 19}, {"lin", 19} };
//infos = infos;
changeInfo(infos);
for (int i = 0; i < 3; ++i)
{
printf("%s, %d \n", (*(infos + i)).name, (*(infos + i)).age);
}
system("pause");
}
///03.指针与结构体.c
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
struct Data01
{
int num;
};
int main05(void)
{
//int intArr[3][4];//intArr:数组名的常量指针形式
int(*intArr)[4] = {0};//intArr:数组名的变量指针形式
//int * [4];//指针数组形式
printf("intArr:%p, intArr+1:%p \n", intArr, intArr + 1);
struct Data01(*pArr)[10] = malloc(90 * sizeof(struct Data01));//动态分配一个标准的二维结构体数组
int k = 0;
for (int i = 0; i < 9; ++i)
{
for (int j = 0; j < 10; ++j)
{
//printf("%3d", pArr[i][j].num = ++k);
printf("%3d", (*(*(pArr + i) + j)).num = ++k);
}
printf("\n");
}
system("pause");
}
int main06(void)
{
struct Data01 **pp;//结构体锯齿数组
pp = (struct Data01 **)malloc(10 * sizeof(struct Data01 *));
int k = 1;
for (int i = 0; i < 10; ++i)
{
*(pp + i) = (struct Data01 *)malloc((i) * sizeof(struct Data01));
for (struct Data01 * pStruct = *(pp + i); pStruct <= *(pp + i) + i; ++pStruct)
{
printf("%3d", (*pStruct).num = k++);
}
printf("\n");
}
system("pause");
}
int main07(void)
{
//struct Data01 * pStruct = (struct Data01 *)alloca(10 * sizeof(struct Data01));//栈内存-->动态分配
struct Data01 * pStruct = (struct Data01 *)malloc(sizeof(struct Data01) * 10);//堆内存-->动态分配
int i = 1;
for (struct Data01 * pTemp = pStruct; pTemp < pStruct + 10; ++pTemp)
{
printf("%p, %d \n", pTemp, (*pTemp).num = i++);
}
system("pause");
}
程序片段(04):位域.c
内容概要:位域
#include <stdio.h>
#include <stdlib.h>
//01.位域:通过限定数据的存储位数,从而实现节省内存占用
// 位域-->指定存储位数-->节省内存|显二进制位
//02.在操作二进制位运算的过程当中,一定要注意到符号位的特点
// 1.位域结构体有其自身的数据存储方式和解析方式
// 2.可以进行数据的分批次存储
struct MyStruct01
{
unsigned int a : 5;//00~31[2^5=32]
unsigned int b : 5;//00~12[2^4=16]
unsigned int c : 16;//0000[2^16]
};//4
int main01(void)
{
printf("sizeof(struct MyStruct01) = %d \n", sizeof(struct MyStruct01));
struct MyStruct01 ms1, *pStruct;
pStruct = &ms1;
ms1.a = 8;//位域-->压缩内存-->操作二进制位-->效率高
ms1.b = 11;
ms1.c = 1999;//存储数据都一样,解析方式不同
printf("%d ,%d, %d \n", ms1.a, ms1.b, ms1.c);
printf("%d, %d ,%d \n", pStruct->a, pStruct->b, pStruct->c);
system("pause");
}
struct Data01
{
unsigned short num1 : 1;
unsigned short num2 : 2;
};//2
//03.位域结构体在进行数据的分批次存储的时候:
// 1.注意有无符号的影响
// 2.注意数据越界的情况:
// 如果出现越界,就只会解析界限以内的数据
int main02(void)
{
printf("sizeof(struct Data01) = %d \n", sizeof(struct Data01));
struct Data01 data01;
data01.num1 = 2;//数据越界2-->二进制-->10-->显示0
printf("%d \n", data01.num1);
system("pause");
}
//04.只有位域才有可能出现按照数据类型进行合并的情况
// 同类型+不越界=>内存压缩
struct Data02
{
unsigned int num1 : 32;
unsigned short num2 : 1;
};//8
int main03(void)
{
printf("%d \n", sizeof(struct Data02));
system("pause");
}
int isit()
{
unsigned short num = 1;//0000 000 0000 0001
return (1 == *(char *)&num);//接触原则
}
int main04(void)
{
short num = 1;
printf("&num = %p \n", &num);//低位在低字节,高位在高字节
printf("%d \n", isit());//0000 0000 0000 0001
//逆序存储:1000 0000 0000 0000
//顺序显示:0000 0000 0000 0001
system("pause");
}
程序片段(05):显示整数.c
内容概要:位域编程实战
#include <stdio.h>
#include <stdlib.h>
//01.关于二进制位与高低直接之间的关系:
// 1.现代手机,电脑:
// 低位在低字节:
// 2.Unix服务器系列:
// 低位在高字节:
//注:关于两种不同存储方式的优缺点:
// 低位在低字节:
// 节省内存
// 低位在高字节:
// 快速检索
//02.调试显示状态:
// 低位在低字节的情况内存结构分析:
// 1000 0000 0000 0000 0000 0000 0000 0000//逆序存储
// 0000 0000 0000 0000 0000 0000 0000 0001//顺序显示
// 解析特点:
// 1000 0000:8个二进制位存储于低字节位置-->节省内存
//注:逆序存储-->实现低位在低字节-->顺序显示(解析方式)
//03.结构体与位域:
// 1.位域所涉及的知识:
// 结构体+位运算
// 2.按照单字节解析二进制位的结构体:
// 特殊结构体:位域
//04.位域特点分析:
// 1.位域的用处:
// (1).内存压缩
// (2).解析数据二进制位
// 2.内存字节对齐特点:
// (1).基于标准结构体法则
// (2).基于内存合并法则
// 相同类型,如果位数相加小于该类型字节数,则压缩存储
// 相同类型,如果位数相加大于该类型字节数,则填位移字
// (3).特殊位域处理:
// 如果位域当中的位域为空,则空置下一个此类型字节数
// 如果位域当中的域名没有,则空置下一个此类型字节数
// 3.快速识别位域:
// 结构体+冒号
// 4.位域组成特点分析:
// BitField:位域结构体名(整体)
// unsigned char:位域结构体成员类型
// chr:域名
// : :位域结构体标识
// 1:位宽
//05.采用位于结构体显示一个数据的二进制存储形式注意事项:
// 1.采用指向位域结构体的指针来存储变量的地址!
// 2.该位于结构体的指针是按照单字节进行内存解析的
// 从低字节开始逐个进行解析
// 3.逆序存储-->顺序显示:
// (1).逆序指针:先打印高字节
// (2).逆序域名:反转存储实质
struct BitField
{
unsigned char chr1 : 1;
unsigned char chr2 : 1;
unsigned char chr3 : 1;
unsigned char chr4 : 1;
unsigned char chr5 : 1;
unsigned char chr6 : 1;
unsigned char chr7 : 1;
unsigned char chr8 : 1;
};//一个字节
int main05(void)
{
printf("sizeof(struct BitField) = %d \n", sizeof(struct BitField));
int data = 1;//原始整数
int bitLen = 4;
struct BitField * pBitField = &data;//将一个整数指针直接赋值给位域结构体变量
//pBitField存储的是位域结构体数组方式存储的
printf("&data = %p \n", &data);
while (bitLen--)//存在-->获位
{//每次解析一个字节-->从高字节开始进行解析-->一直解析到低字节
printf("%d%d%d%d %d%d%d%d \n",
(pBitField + bitLen)->chr8,
(pBitField + bitLen)->chr7,
(pBitField + bitLen)->chr6,
(pBitField + bitLen)->chr5,
(pBitField + bitLen)->chr4,
(pBitField + bitLen)->chr3,
(pBitField + bitLen)->chr2,
(pBitField + bitLen)->chr1);
}
//1000 0000
//0000 0000
//0000 0000
//0000 0000
system("pause");
}
程序片段(06):Union.c
内容概要:Union
#include <stdio.h>
#include <stdlib.h>
//01.结构体和共用体的相同点:
// 1.既不允许空结构体的存在,也不允许空共用体的出现:
// 也就是说结构体或共用体至少必须包含一个成员
// 2.使用结构体类型或者共用体类型的时候都必须包含
// struct关键字
union MyUnion01
{
int num;
double db;
};
//02.共用体内容总结:
// 1.共用体的内存尺寸等于最长的那个成员所占据的内存尺寸
// 最长成员(包含复合类型)
// 2.共用体的所有成员共享同一数据实质(内存形式一致)
// 但是由于共用体的不同成员类型不一致,因此解析结果不一致
//注:共用体的各个成员存储数据存储实质一样,但是解析方式却不一样
// 区分存储实质和解析方式的不同所造成的差异
int main01(void)
{
//printf("sizeof(union MyUnion01) = %d \n", sizeof(union MyUnion01));//结构体内存尺寸等于最宽的那个成员
union MyUnion01 mu01;
mu01.num = 3;
//mu01.db = 111111111123.98776;
printf("%8d, %lf \n", mu01.num, mu01.db);//存储实质一致,但是解析类型不一致
mu01.num = 0;
printf("%8d, %lf \n", mu01.num, mu01.db);
system("pause");
}
程序片段(07):Union.c
内容概要:Union定义方式
#include <stdio.h>
#include <stdlib.h>
union MyUnion01
{//标准形式
int num;
float fl;
} mu, arr[3], *p;
//01.共用体定义方式:
// 1.声明共用体类型的同时定义共用体变量
// 2.先声明共用体类型,再定义共用体变量
// 3.标准共用体和匿名共用体
union
{//匿名形式
int num;
float fl;
} mu1, arr1[3], *p1;
//02.共用体的特点:
// 1.所有成员共用同一个数据
// 2.所有成员公用同一个地址
// 3.只是由于成员的不同类型,因此导致对
// 同一内存地址的同一个数据解析结果不同
//注:所有变量使用同一地址,所有变量使用同一数据
// 不同的成员只是决定不同的数据解析方式
int main01(void)
{
//printf("sizeof(mu1) = %d \n", sizeof(mu1));
union MyUnion01 mu2, arr2[3], *p2;
mu2.num = 100;
printf("%d, %f \n", mu2.num, mu2.fl);
printf("%p, %p \n", &(mu2.num), &(mu2.fl));
system("pause");
}
程序片段(08):对齐.c
内容概要:内存对齐
#include <stdio.h>
#include <stdlib.h>
struct MyStruct01
{
char str[23];
short num;
};
//01.结构体嵌套的情况之下:
// 内存字节对齐数就是所有基本类型成员当中
// 占用内存字节数最宽的那个成员
struct MyStruct02
{
struct MyStruct01 ms01;
char chr;
};
int main01(void)
{
//printf("sizeof(struct MyStruct01) = %d \n", sizeof(struct MyStruct01));
printf("sizeof(struct MyStruct02) = %d \n", sizeof(struct MyStruct02));
system("pause");
}
union MyUnion01
{
char str[13];
int db;
};
//02.共用体的内存尺寸特点:
// 1.必须遵守内存字节对齐方式
// 2.为占用内存尺寸最大的那个成员
// 数组特点+对齐尺寸
int main02(void)
{
printf("sizeof(sruct MyUnion01) = %d \n", sizeof(union MyUnion01));
system("pause");
}
程序片段(09):01.同与不同.c+02.TypeDef.c
内容概要:结构体与共用体的不同
///01.同与不同.c
#include <stdio.h>
#include <stdlib.h>
struct MyStruct01
{
int num;
double db;
};
union MyUnion01
{
int num;
double db;
};
//01.结构体和共用体各个成员的特点:
// 结构体的各个成员内存地址独立,数据之间没有关系
// 共用体的各个成员内存地址相同,数据也是完全相同
//注:共用体的各个成员由于其数据类型的不用,因此对
// 同样一块儿内存数据的解析结果就不一样
// (有无符号+补码解析+阶码解析)
int main01(void)
{
struct MyStruct01 ms01, * ps01;
union MyUnion01 mu01, *pu01;
printf("%p, %p \n", &ms01.num, &ms01.db);//结构体各个成员内存地址不同
printf("%p, %p \n", &mu01.num, &mu01.db);//共用体各个成员内存地址相同
ps01 = &ms01;
pu01 = &mu01;
ms01.num;
(&ms01)->num;
(*&ms01).num;
ps01->num;
ps01->num;
(*ps01).num;
mu01.num;
(&mu01)->num;
(*(&mu01)).num;
system("pause");
}
///02.TypeDef.c
#include <stdio.h>
#include <stdlib.h>
//01.typedef的特点:
// 所有连续定义的变量名称都会成为数据类型的别名
typedef struct MyStruct01
{
int num;
double db;
} ms01, ms02;
typedef struct MyStruct01 MS;
typedef union MyUnion01
{
int num;
double db;
} mu01, mu02;
typedef union MyUnion01 MU;
typedef unsigned int num1, num2, num3, num4;//num1,num2,num3,num4都是数据类型的别名
//02.共用体赋值特点:
// 1.共用体只能对其中一个成员进行初始化赋值操作
// 2.共用体在经过多次赋值操作之后,其最终结果是最后一次赋值结果
//03.结构体和共用体都可以采用指定成员初始化的方式:
// 必须是静态初始化特点才可以采取指定成员初始化的方式
int main02(void)
{
MS my01 = { .num = 10, 10.9 };
ms01 my02 = { 11, 11.9 };
MU myu01 = { 10 };
mu01 myu02 = { .db = 11.9 };
ms02 my3 = { 12, 129 };
system("pause");
}
程序片段(10):PC.c
内容概要:共用体
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct MyStructX
{
double db;
};
union PC
{
struct MyStructX;
int num;
char price[10];
char * p;
} pc01 = { .num = 10000 };
struct MyStruct
{
union PC pc1;
};
int main01(void)
{
union PC pc02;
pc02.p = (char *)malloc(100);
strcpy(pc02.p, "海华装的电脑,价格未知!");
union PC pc03 = pc02;
pc03.p = malloc(100);
strcpy(pc03.p, pc02.p);
printf("%s, %s \n", pc02.p, pc03.p);
free(pc02.p);
printf("%s, %s \n", pc02.p, pc03.p);
system("pause");
}
//01.针对于结构体和共用体而言:
// 1.都存在着浅拷贝和深拷贝问题
// 2.变量名称可以和结构体的名称相同
int main02(void)
{
union PC pc = { 10000 };
union PC arr[3] = { {10000},{10000}, {10000} };
union PC * p = (union PC []){ {10000},{10000},{10000} };
//++pc;
//arr = arr;
pc.num++;
++pc.num;
printf("%d \n", pc.num);
system("pause");
}
//02.采用共用体可以采用多种方式描述同一个事物:
// 注:共用体当中所存储的成员一旦改变,就只能访问最后一个数据
// 同一个地址,同一个数据(指针变量和普通变量存储的内容一致)
int main03(void)
{
union PC pc;
pc.p = (char *)malloc(100);
strcpy(pc.p, "海华装的电脑,价格位置!");
pc.num = 4000;
printf("%s \n", pc.p);
free(pc.p);//严重错误
system("pause");
}
程序片段(11):Data.c
内容概要:数据管理编程
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//01.如何"多行表示"一个"整体存储"的字符串?
// 采用反斜杠"\"操作符作为多行表示字符串的结尾链接状态
char str[1024] = "521276402----hanlei@19940403 \
286738260----WEIPEI559720 \
501223616----feng66532008 \
851400824----shuidongwo520 \
1340382355----huang.512yang \
1061817115----fz62wangyong1983 \
347232860----20080811 \
1028181591----7404850554 \
120539543----0.0123456789 \
754229005----460228214 \
819781633----zmt1993826 \
1319148052----YNU1500621032 \
904972448----zhouxiaowen.520 \
750134133----1292857988 \
438905700----320675 \
379644978----7758521tao \
346083956----devl1017 \
562193724----5361a749 \
286124126----xuqiang1988 \
4008167599----234567 \
937350113----MAWENcxn1020 \
873589635----qqco1341HUA \
853249795----5385939d ";
//02.创建类型为struct info的结构体类型
// 1.用于描述单个用户的信息实体
// 2.单个结构体对应于一个用户的信息
struct info
{
long long QQ;
char pass[16];
};
//03.采用结构体描述动态结构体数组:
// 用户结构体动态数组用于描述多个用户信息实体
//注:基于C语言的所有数据结构都依赖于结构体
struct infos
{
struct info * pInfo;
int len;
} myInfos;
int getRowNum(char * pStr)
{//根据每行字符串所共有的特征进行数据行的统计
int rows = 0;
for (char * pTemp = strstr(pStr, "----"); NULL != pTemp; pTemp = strstr(pTemp + 4, "----"))
{//两面夹击统计法
++rows;
}
return rows;
}
void initMem()
{//采用结构体模拟动态结构体数组
int len = getRowNum(str);
myInfos.pInfo = (struct info *)malloc(len * sizeof(struct info));
myInfos.len = len;
}
void initData(char * pStr)
{
int len = strlen(pStr);
for (char * p = pStr; p < pStr + len; ++p)
{//针对于原始数据的预处理操作
if (' ' == *p)
{
*p = '\0';
}
}
for (char * p = pStr, chr = 0; p < pStr + len; ++p)
{//根据字符串数据进行结构体数据拼装
//printf("%p, %d \n", p, chr);
char * pTmp = (char *)calloc(strlen(p) + 1, sizeof(char));
strcpy(pTmp, p);
//"521276402\0---hanlei@19940403"//pTmp-->pWei+4
char * pWei = strstr(pTmp, "----");
*pWei = '\0';
sscanf(pTmp, "%lld", &(*(myInfos.pInfo + chr)).QQ);
sprintf((*(myInfos.pInfo + chr)).pass, "%s", pWei + strlen("----"));
p += strlen(p) + 1;
}
}
void showAllData()
{
for (int i = 0; i < myInfos.len; ++i)
{
printf("QQ = %lld, pass = %s \n", (*(myInfos.pInfo + i)).QQ, (*(myInfos.pInfo + i)).pass);
}
}
void showDataStatus()
{
printf("还有%d条数据! \n", myInfos.len);
}
void help()
{
printf("1.显示数据 \n");
printf("2.删除数据 \n");
printf("3.插入数据 \n");
printf("4.修改数据 \n");
printf("5.查找数据 \n");
printf("6.显示全部数据 \n");
printf("7.对于QQ排序 \n");
printf("8.清屏 \n");
printf("9.Helop \n");
printf("\n");
}
void deleteData(){}
int main01(void)
{
initMem();
initData(str);
help();
while (1)
{
char chr = getch();//实时获取单个字符
//char chr = '\0';
//getchar(chr);
//putchar(chr);
switch (chr)
{
case '1':
showDataStatus();
break;
case '2':
break;
case '3':
break;
case '4':
break;
case '5':
break;
case '6':
break;
case '7':
break;
case '8':
system("cls");
break;
case '9':
help();
break;
default:
break;
}
}
system("pause");
}