C语言从入门到精通 第十章(自定义的数据类型)

一、结构体

1、结构体基本概念

        结构体属于用户自定义的数据类型,允许用户在同一个结构体变量中存储不同的类型的数据。

2、结构体的定义

(1)建立结构体类型的语法:

struct <结构体名> { <结构体成员列表> };

(2)定义结构体类型变量的三种方法:

①先声明结构体类型,再定义该类型的变量。

struct <结构体名> <变量名列表>;

②在声明类型的同时定义变量。

struct <结构体名>

{

        <结构体成员列表>

}<变量名列表>;

③不指定类型名而直接定义结构体类型变量。(使用较少)

struct

{

        <结构体成员列表>

}<变量名列表>;

(3)定义结构体类型时的关键字是struct,不可省略;创建结构体变量时,关键字struct可以省略。

①例1:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>

struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
}s1,s2; //分号不能丢

int main()
{
	Stu s3;  //s1、s2是全局变量,s3是局部变量

	return 0;
}

②例2:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//匿名结构体类型
struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20], *p;  //含有20个结构体类型元素的数组以及指向该结构体类型的指针

int main()
{
	//p = &x;  虽然两个结构体成员相同,但是并不能算作同一类型,即使都是匿名

	return 0;
}

3、结构体变量的初始化和访问

(1)在定义结构体变量时可以对它的成员初始化,初始化列表是用花括号括起来的一些数据(逗号分隔),这些数据依次赋给结构体变量中的各成员。

(2)可以访问结构体变量中成员的值,访问方式为:

<结构体变量名>.<成员名>  或   <结构体指针变量名>-><成员名>

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Stu
{
	char name[20];  //名字
	int age;        //年龄
	char sex[10];   //性别
	char id[15];    //学号
};
int main()
{
	//利用结构体类型创建一个该类型的结构体变量
	struct Stu s = { "张三", 20,"男","20180101" };
	
	printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
	
	struct Stu *ps = &s;
	printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps -> id);
	return 0;
}

4、定义结构体数组

(1)结构体数组的每个元素都是一个结构体类型的数据,它们都分别包括各个成员项。

(2)定义结构体数组的两种形式:

struct <结构体名>

{

        <结构体成员列表>

}<数组名>[<数组长度>];

<结构体名> <数组名>[<数组长度>];

(3)对结构体数组初始化的形式与对普通数组初始化的形式类似,只是每个元素都是结构体常量,用花括号括起,花括号内的是赋给结构体元素各成员的值。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//结构体定义
struct student
{
	//成员列表
	char name[100];
	int age;
	int score;
};


int main() {

	//结构体数组
	struct student arr[3] =
	{
		{"张三",19,90},
		{"李四", 18, 100},
		{"王五",20,80}
	};
	arr[2].age = 21;   //可以修改数组里某个结构体元素的某个成员
	for (int i = 0; i < 3; i++)
	{
		printf("姓名:%s  年龄:%d\n", arr[i].name, arr[i].age);
	}

	return 0;
}

5、指向结构体变量的指针

(1)指针变量的基类型必须与结构体变量的类型相同。

(2)利用操作符“->”可以通过结构体指针访问结构体属性(也可以将结构体指针解引用,再用操作符“.”一样可以访问结构体属性)。

(3)定义结构体指针的两种形式:

struct <结构体名>

{

        <结构体成员列表>

}*<指针名>;

<结构体名> *<指针名>;

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Stu
{
	char name[20];
	int age;
};

void print(struct Stu* ps)
{
	printf("name = %s   age = %d\n", (*ps).name, (*ps).age);
	//使用结构体指针访问指向对象的成员
	printf("name = %s   age = %d\n", ps->name, ps->age);
}
int main()
{
	struct Stu s = { "zhangsan", 20 };
	print(&s);//结构体地址传参
	return 0;
}

6、指向结构体数组的指针

(1)指针变量的基类型必须与结构体变量的类型相同。

(2)可以用指针变量指向结构体数组的元素,和普通数组一样,只是指向结构体数组元素的指针还可以访问结构体元素的成员。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//结构体定义
struct student
{
	//成员列表
	char name[100];
	int age;
	int score;
};

int main() {

	struct student stu[2] = { { "张三",19,90 }, { "张四",14,50 } };
	//int * p = &s ;  
	struct student *p = stu;  //指针指向数组首元素

	printf("姓名:%s  年龄:%d\n", p->name, p->age);
	p++;  //指针指向下一个元素
	printf("姓名:%s  年龄:%d\n", p->name, p->age);

	return 0;
}

7、结构体嵌套结构体

(1)结构体中的成员可以是另一个结构体,在结构体中可以定义另一个结构体作为成员。

(2)需要注意的是,结构体中的成员类型不可以是结构体的本身类型(否则定义该结构体类型的变量时内存将会被无限开辟),不过可以是结构体本身类型的指针(结构体的自引用)。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = { 1, 2 };
struct Stu      //类型声明
{
	char name[15];//名字
	int age;      //年龄
};
struct Stu s = { "zhangsan", 20 };//初始化
struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化
int main()
{
	return 0;
}

8、结构体做函数参数

(1)将结构体作为参数向函数中传递,其中传递方式有值传递和地址传递两种,如果不想修改主函数中的数据就用值传递,反之用地址传递。

(2)函数传参的时候,参数是需要压栈的,如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降,所以结构体传参的时候,尽量传结构体的地址。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s);  //传结构体
	print2(&s); //传地址
	return 0;
}

9、结构体内存对齐

(1)结构体的存储结构并不是和预料中的一样,结构体中有不同类型的成员,比如一个结构体中有两个字符型变量(分别为1成员和2成员)和一个整型变量(3成员,认为它占4字节),那么这个结构体的存储结构应该如下右图所示,这虽然浪费了两个字节的空间,但是却能提升程序的运行效率,这种操作就是内存对齐。

(2)结构体的对齐规则

①第一个成员在与结构体变量偏移量为0的地址处。

②其它成员变量要对齐到某个数字(对齐数)的整数倍的地址处,其中对齐数为编译器默认的一个对齐数与该成员大小的较小值(VS中默认的值为8)。

③结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

④如果是嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

(3)根据结构体的对齐规则,不难得出结构体的大小不仅与其成员类型有关,还与不同类型成员的排序有关,在设计结构体的时候,既要满足对齐,又要节省空间,让占用空间小的成员尽量集中在一起。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct S1
{
	char c1;
	int i;
	char c2;
};

struct S2
{
	char c1;
	char c2;
	int i;
};

struct S3
{
	double d;
	char c;
	int i;
};

struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

int main()
{
	printf("%d\n", sizeof(struct S1));//12
	printf("%d\n", sizeof(struct S2));//8
	printf("%d\n", sizeof(struct S3));//16
	printf("%d\n", sizeof(struct S4));//32
	return 0;
}

(4)编译器有一个默认的对齐数,而这个对齐数实际上可以进行修改,而且这个修改操作还是可逆的,具体见下面的代码。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#pragma pack(8)//设置默认对齐数为8
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(1)//设置默认对齐数为1
struct S2
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
	printf("%d\n", sizeof(struct S1));//12
	printf("%d\n", sizeof(struct S2));//6
	return 0;
}

10、柔性数组

(1)C99中,结构体中的最后一个元素允许是大小未知的数组,这就叫做柔性数组成员,其声明方式如下。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

typedef struct st_type  //声明结构体的同时给它起别名(视个人爱好)
{
	int i;
	int a[0];//柔性数组成员   //如无法编译则可以改为int a[];
}type_a;  //type_a是别名(不是变量名,而是类型名)

int main()
{
	return 0;
}

(2)柔性数组的特点:

①结构体中的柔性数组成员前面必须至少一个其它成员。

②sizeof返回的这种结构体大小不包括柔性数组的内存。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
	typedef struct st_type
	{
		int i;
		int a[0];//柔性数组成员
	}type_a;
	printf("%d\n", sizeof(type_a));//输出的是4
	return 0;
}

(3)包含柔性数组成员的结构体应用malloc ()函数为其进行内存的动态分配,并且分配的内存应该大于结构体的大小,适应柔性数组的预期大小是其次,首先是能保证存储结构体的其它成员,剩余的空间则全部分配给柔性数组。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

typedef struct st_type
{
	int i;
	int a[0];//柔性数组成员
}type_a;

int main()
{
	int i = 0;
	type_a *p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
	//这样柔性数组成员a,相当于获得了100个整型元素的连续空间(不够就用realloc继续开辟)
	p->i = 100;
	for (i = 0; i < 100; i++)
	{
		p->a[i] = i;
	}
	free(p);
	return 0;
}

二、位段

1、位段的声明

(1)位段的声明和结构体是类似的(比如给成员赋值的操作方法是相同的),但是它们又有两点不同:

①位段的成员必须是int、unsigned int、signed int或char(属于整型家族)类型。

②位段的成员名后边有一个冒号和一个数字,数字代表这个成员将占用多少个位(bit)的空间。

(2)举例(Visual Studio C++环境):

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct A
{
	int _a : 2;   //2个bit位,占1字节
	int _b : 5;   //5个bit位,占1字节
	int _c : 10;  //10个bit位,占2字节
	int _d : 30;  //30个bit位,占4字节
};

int main()
{
	printf("%d\n", sizeof(struct A));//8
	return 0;
}

2、位段的内存分配

(1)在VC中,位段在空间上是按照以4个字节(int型成员)或者1个字节(char型成员)为单位的方式来开辟的:

①假如有一个char型成员占用了1个字节的一部分,而这个字节剩下的空间还能容纳下一个char型成员,那么这两个char型成员可以共用1个字节,如果容不下,那么这个字节只属于前一个char型成员,需要为下一个char型成员分配1个新的字节。(甚至连续的最多8个char型成员可以共用1个字节,只要它们占用位数的总和不超过8位)

②对于int型成员,虽然是以4个字节为单位进行空间分配的,但是如果该成员占不满4个字节,则该成员将只占有它所用的字节的整个空间(如上例中的成员_a仅占1个字节),剩下的字节留给下一个成员,如果剩下的字节空间能容纳下一个成员,则暂时不用开辟新的4个字节,如果容不下,则为下一个int型成员分配4个新的字节。(甚至连续的最多4个int型成员可以共用4个字节,只要它们各自占用的位数都不超过8位)

(2)举例(Visual Studio C++环境):

①例1:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{
	struct S s = { 0 };  //占3个字节
	s.a = 10;
	s.b = 12;  //a、b共用1个字节
	s.c = 3;
	s.d = 4;   //c、d各自用1个字节
	printf("%d\n", sizeof(struct S));//3

	return 0;
}

②例2:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

struct A
{
	int _a : 2;   //2个bit位,占1字节
	int _b : 15;  //15个bit位,占2字节
	int _c : 30;  //10个bit位,占4字节
	int _d : 30;  //30个bit位,占4字节
};

int main()
{
	printf("%d\n", sizeof(struct A));//12
	return 0;
}

3、位段的跨平台问题

(1)int位段被当成有符号数还是无符号数是不确定的。

(2)位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。

(3)位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

(4)当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

三、枚举

1、概述

(1)如果一个变量只有几种可能的取值,则可以定义为枚举类型,所谓“枚举”就是把可能的值一一列举出来,变量的值只限于列举出来的值的范围中。

(2)被一一列举出来的值称为枚举元素或枚举常量,它们是用户指定的名字。

2、枚举类型的定义和使用

(1)声明枚举类型的一般形式为:

enum <枚举名>{<枚举元素列表>};

①enum是关键字,指出要定义的是枚举类型。

②枚举名是标识符,即由用户给出的具体枚举类型名。

③枚举元素表包含多个枚举值,它们用逗号分隔开,每个枚举值就是一个枚举常量,它有两种定义形式,如下所示。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
};
enum Color//颜色
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};
//这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值(后面未赋初值的从被赋初值一个开始一个一个递增1)

int main()
{
	//以上定义的 enum Day , enum Sex , enum Color 都是枚举类型
	//{ }中的内容是枚举类型的可能取值,也叫 枚举常量
	return 0;
}

(2)关于enum类型的几点说明:

①一个enum类型实际上是int类型的一个子集,其每一个枚举值代表一个整数。

n个枚举值全部未赋常量值时,它们从前至后分别与整数0、1、…、n-1对应

若第i个枚举值赋常量值为m,则其未赋常量值的后续枚举值分别与整数m+1、m+2、…对应,直到下一个赋了值的枚举值或结束,因此,为枚举值所赋的整型常量值应从前至后递增

④只能拿枚举常量给枚举变量赋值或初始化,才不会出现类型的差异。

⑤定义枚举变量的方式和定义结构体变量的方式类似,只是关键字换为“enum”,这个关键字可以省略。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

enum Color//颜色
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};
enum Color clr = GREEN;

int main()
{
	//clr = 2;
	printf("%d", clr);//2
	printf("%d", BLUE);//4
	return 0;
}

四、联合(共用体)

1、概述

联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,但与结构体不同的是,这些成员共用同一块空间(所以联合也叫共用体)。

2、共用体的声明与变量的定义

(1)共用体的声明形式:

union <共用体名>

{

        <共用体成员列表>

};

(2)共用体变量的定义形式:

union <共用体名>

{

        <共用体成员列表>

}<变量名列表>;

union <共用体名> <变量名列表>;

3、引用共用体变量的方式

(1)引用共用体变量的方式与引用结构体变量的方式类似,都可以使用操作符“.”对成员进行访问,不过虽然访问的是同一段内存空间,但是访问成员的类型不同,于是访问空间的大小以及对存储内容的解读方式也会有所差异(比如访问整型和字符型成员,访问空间的大小会不同;访问整型和浮点型成员,对存储内容的解读方式会不同)。

(2)因为共用体的成员共用同一片内存,所以对其中一个成员进行修改时,基本都会影响到其它成员。

(3)举例:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
	union Un
	{
		int i;
		char c;
	};
	union Un un;
	printf("%p\n", &(un));
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));

	printf("%d\n", &(un.i));
	printf("%d\n", &(un.c));

	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
	printf("%x\n", un.c);
}

4、共用体的特点

(1)共用体的成员是共用同一块内存空间的,这样一个共用体变量的大小至少是最大成员的大小(因为共用体至少得有能力保存最大的那个成员)。

(2)当共用体最大成员的大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
	union Un1
	{
		char c[5];//占五个字节,按照char类型对齐,最大对齐数仍为8
		int i;
	};
	union Un2
	{
		short c[7];//占十四个字节,按照short类型对齐,最大对齐数仍为8
		int i;
	};
	
	printf("%d\n", sizeof(union Un1));//8
	printf("%d\n", sizeof(union Un2));//16
}

五、案例——通讯录

1、main.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

void menu()
{
	printf("***********************************************\n");
	printf("******* 1. add           2. del        ********\n");
	printf("******* 3. search        4. modify     ********\n");
	printf("******* 5. show          6. sort       ********\n");
	printf("******* 0. exit                        ********\n");
	printf("***********************************************\n");
}

int main()
{
	int input = 0;
	Contact contact;
	InitContact(&contact);
	do
	{
		system("cls");
		menu();
		printf("请输入您的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			AddContact(&contact);
			break;
		case 2:
			DelContact(&contact);
			break;
		case 3:
			SearchContact(&contact);
			break;
		case 4:
			ModifyContact(&contact);
			break;
		case 5:
			ShowContact(&contact);
			break;
		case 6:
			SortContact(&contact);
			break;
		case 0:
			printf("正在退出系统!\n");
			break;
		default:
			printf("输入有误!\n");
			break;
		}
		system("pause");
	} while (input);

	return 0;
}

2、contact.h

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME 20
#define MAX_TEL 12
#define MAX_SEX 14
#define MAX_ADDR 30

typedef struct People
{
	char name[MAX_NAME];
	int age;
	char telephone[MAX_TEL];
	char sex[MAX_SEX];
	char addr[MAX_ADDR];
}People;

typedef struct Contact
{
	People data[100];
	int count;
}Contact;

void InitContact(Contact* cp);
void AddContact(Contact* cp);
void DelContact(Contact* cp);
void SearchContact(const Contact* cp);
void ModifyContact(Contact* cp);
void ShowContact(const Contact* cp);
void SortContact(Contact* cp);

3、contact.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "contact.h"

void InitContact(Contact* cp)
{
	assert(cp);
	cp->count = 0;
}

void AddContact(Contact* cp)
{
	assert(cp);
	printf("请输入姓名:");
	scanf("%s", cp->data[cp->count].name);
	printf("请输入年龄:");
	scanf("%d", &(cp->data[cp->count].age));
	printf("请输入性别:");
	scanf("%s", cp->data[cp->count].sex);
	printf("请输入电话号码:");
	scanf("%s", cp->data[cp->count].telephone);
	printf("请输入地址:");
	scanf("%s", cp->data[cp->count].addr);
	cp->count++;
	printf("添加成功!\n");
}

int FindName(const Contact* cp ,const char* name)
{
	assert(cp);
	int i = 0;
	while (i < cp->count)
	{
		if (strcmp(cp->data[i].name, name) == 0)
		{
			return i;
		}
		i++;
	}
	return -1;
}

void DelContact(Contact* cp)
{
	assert(cp);
	char name[MAX_NAME];
	if (cp->count == 0)
	{
		printf("通讯录为空!\n");
		return;
	}
	printf("请输入需要删除联系人的姓名:");
	scanf("%s", name);
	int pos = FindName(cp, name);
	if (pos == -1)
	{
		printf("未找到该联系人!\n");
	}
	else
	{
		while (pos < cp->count)
		{
			cp->data[pos] = cp->data[pos + 1];
			pos++;
		}
		cp->count--;
		printf("删除成功!\n");
	}
}

void SearchContact(const Contact* cp)
{
	assert(cp);
	char name[MAX_NAME];
	if (cp->count == 0)
	{
		printf("通讯录为空!\n");
		return;
	}
	printf("请输入需要查找联系人的姓名:");
	scanf("%s", name);
	int pos = FindName(cp, name);
	if (pos == -1)
	{
		printf("未找到该联系人!\n");
	}
	else
	{
		printf("%-20s\t %-5s\t %-15s\t %-13s\t %-30s\n",
			"姓名", "年龄", "性别", "电话号码", "地址");
		printf("%-20s\t %-5d\t %-15s\t %-13s\t %-30s\n",
			cp->data[pos].name, cp->data[pos].age, cp->data[pos].sex,
			cp->data[pos].telephone, cp->data[pos].addr);
	}
}

void ShowContact(const Contact* cp)
{
	assert(cp);
	if (cp->count == 0)
	{
		printf("通讯录为空!\n");
		return;
	}
	printf("%-20s\t %-5s\t %-15s\t %-13s\t %-30s\n", 
		"姓名", "年龄", "性别", "电话号码", "地址");
	for (int i = 0; i < cp->count; i++)
	{
		printf("%-20s\t %-5d\t %-15s\t %-13s\t %-30s\n",
			cp->data[i].name, cp->data[i].age, cp->data[i].sex,
			cp->data[i].telephone, cp->data[i].addr);
	}
}

void ModifyContact(Contact* cp)
{
	assert(cp);
	char name[MAX_NAME];
	if (cp->count == 0)
	{
		printf("通讯录为空!\n");
		return;
	}
	printf("请输入需要修改联系人的姓名:");
	scanf("%s", name);
	int pos = FindName(cp, name);
	if (pos == -1)
	{
		printf("未找到该联系人!\n");
	}
	else
	{
		printf("请输入姓名:");
		scanf("%s", cp->data[pos].name);
		printf("请输入年龄:");
		scanf("%d", &(cp->data[pos].age));
		printf("请输入性别:");
		scanf("%s", cp->data[pos].sex);
		printf("请输入电话号码:");
		scanf("%s", cp->data[pos].telephone);
		printf("请输入地址:");
		scanf("%s", cp->data[pos].addr);
		printf("修改成功!");
	}
}
int cmpName(const void* p1, const void* p2)
{
	return strcmp(((People*)p1)->name, ((People*)p2)->name);
}
int cmpAge(const void* p1, const void* p2)
{
	return (((People*)p1)->age - ((People*)p2)->age);
}
int cmpSex(const void* p1, const void* p2)
{
	return strcmp(((People*)p1)->sex, ((People*)p2)->sex);
}

void SortContact(Contact* cp)
{
	assert(cp);
	if (cp->count == 0)
	{
		printf("通讯录为空!\n");
		return;
	}
	int(*fun[3])(const void* p1, const void* p2) = { cmpName,cmpAge,cmpSex };
	int i;
	printf("输入0——按姓名排序;输入1——按年龄排序;输入2——按性别排序\n请输入:");
	do
	{
		scanf("%d", &i);
		if (!(i >= 0 && i <= 2))
			printf("输入无效数字!请重新输入:");
	} while (!(i >= 0 && i <= 2));
	qsort(cp->data, cp->count, sizeof(People), fun[i]);
	ShowContact(cp);
}
  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zevalin爱灰灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值