[C语言]结构体进阶与枚举联合

目录

结构体进阶与枚举联合::

                                        结构体进阶:

                                          结构体类型的声明

                                          结构的自引用

                                          结构体变量的定义和初始化

                                          结构体内存对齐

                                          结构体传参

                                          结构体实现位段

                                        枚举:

                                          枚举类型的定义

                                          枚举的优点

                                          枚举的使用

                                        联合:

                                           联合类型的定义

                                           联合的特点

                                           联合大小的计算

                                        C语言编程训练(牛客网)

                                           1.字符串旋转结果

                                           2.BC96-有序序列判断

                                           3.BC106-上三角矩阵判定

                                           4.BC107—矩阵转置

                                           5.BC115-小乐乐与欧几里得

                                            

                                        

                                                         

结构体进阶与枚举联合::

结构体进阶:

结构体类型的声明

结构体概述:

有时候我们需要将不同类型的数据组合成一个有机的整体,如:一个学生有学号、姓名、性别、年龄、地址等属性.显然单独定义以上变量比较繁琐,数据不便于管理,C语言中给出了另一种构造数据类型——结构体.

[C语言]结构体进阶与枚举联合_第1张图片

 结构体的声明:

struct tag
{
    member-list;
}variable-list;
//例如:
struct Stu1
{
    char name[20];
    int age;
    char sex[5];
    char ID[20];
};
struct Stu2
{
	char name[20];
	int age;
}s1,s2;   
//s1和s2是struct Stu类型的全局变量

结构体的特殊声明:匿名结构体类型

[C语言]结构体进阶与枚举联合_第2张图片

struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}a[20],* p;
int main()
{
	p = &x;  //err 编译器认为匿名结构体是两个完全不同的两个结构体
	return 0;
}

[C语言]结构体进阶与枚举联合_第3张图片

结构的自引用

//错误写法:
struct Node
{
	int date;
	struct Node next;
};//这种写法无法计算结点大小:即sizeof(struct Node)无法计算
//正确写法:
struct Node
{
	int date;
	struct Node* next;
};
//结构体的自引用只能包含结构体成员对应的指针
//错误写法:
typedef struct
{
	int data;
	Node* next;
}Node;//typedef只能对已然存在的数据类型进行重命名 
//不要把匿名结构体类型和自引用相结合
//正确写法:
typedef struct Node
{
	int data;
	struct Node* next;
}Node;

[C语言]结构体进阶与枚举联合_第4张图片

[C语言]结构体进阶与枚举联合_第5张图片

结构体变量的定义和初始化

定义结构体变量的方式:

1.先声明结构体类型,再定义结构体变量.

2.在声明类型的同时定义变量.

3.直接定义结构体类型变量(无变量名). 

[C语言]结构体进阶与枚举联合_第6张图片

结构体类型和结构体变量的关系:

结构体类型:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元.

结构体变量:系统根据结构体类型(内部成员状况)为之分配空间.

//结构体嵌套结构体
struct score
{
	int n;
	char ch;
};
struct Stu
{
	char name[20];
	int age;
	struct score s;
};
int main()
{
	struct Stu s1 = { "张三",20,{100,'q'} };
	printf("%s %d %d %c\n", s1.name, s1.age, s1.s.n, s1.s.ch);
	return 0;
}

[C语言]结构体进阶与枚举联合_第7张图片

[C语言]结构体进阶与枚举联合_第8张图片

结构体成员的使用:

#include
#include
//结构体类型的定义
struct stu
{
	char name[50];
	int age;
};
int main()
{
	struct stu s1;
	//如果是普通变量,通过点运算符操作结构体成员
	strcpy(s1.name, "lihua");
	s1.age = 18;
	printf("s1.name = %s  s1.age = %d\n", s1.name, s1.age);
	//如果是指针变量,通过->操作结构体成员
	strcpy((&s1)->name, "hongfei");
	(&s1)->age = 22;
	printf("(&s1)->name = %s  (&s1)->age = %d\n", (&s1)->name, (&s1)->age);
	return 0;
}

[C语言]结构体进阶与枚举联合_第9张图片

结构体数组:

//统计学生成绩
struct stu
{
	int num;
	char name[20];
	char sex[10];
	float score;
};
int main()
{
	//定义一个含有5个元素的结构体数组并将其初始化
	struct stu element[5] =
	{
		{ 101, "Li ping", "男", 45},
		{ 102, "Zhang ping", "男", 62.5 },
		{ 103, "He fang", "女", 92.5},
		{ 104, "Cheng ling", "女", 87},
		{ 105, "Wang ming", "男", 58 }
	};
	int i = 0;
	int count = 0;
	float average, sum = 0.0f;
	for (i = 0; i < 5; i++)
	{
		sum += element[i].score;	//计算总分
		if (element[i].score < 60)
		{
			count++;		//统计不及格人的分数
		}
	}
	printf("sum = %f\n", sum);//打印总分数
	average = sum / 5;					//计算平均分数
	printf("平均成绩:average = %f\n", average); //打印平均分
	printf("不及格人数:count = %d\n", count);//打印不及格人数

	for (i = 0; i < 5; i++)
	{
		printf(" name=%s  score=%f\n", element[i].name, element[i].score);
	}
	return 0;
}

[C语言]结构体进阶与枚举联合_第10张图片

结构体嵌套结构体:

struct person
{
	char name[20];
	char sex[10];
};
struct stu
{
	int ID;
	struct person info;
};
int main()
{
	struct stu s[2] = { 1,{"zhangming","男"},2,{"lili","女"} };
	int i = 0;
	for (i = 0; i < 2; i++)
	{
		printf("ID = %d  info.name = %s  info.sex = %s\n",
			    s[i].ID, s[i].info.name, s[i].info.sex);
	}
	return  0;
}

[C语言]结构体进阶与枚举联合_第11张图片

堆区结构体变量

//结构体类型的定义
struct stu
{
	char name[50];
	int age;
};
int main()
{
	struct stu* p = (struct stu*)malloc(sizeof(struct stu));
	if (p == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	strcpy(p->name, "lihua");
	p->age = 22;
	printf("p->name = %s  p->age = %d\n", p->name, p->age);
	printf("(*p).name = %s  (*p).age = %d\n", (*p).name, (*p).age);
	free(p);
	p = NULL;
	return 0;
}

[C语言]结构体进阶与枚举联合_第12张图片

结构体嵌套一级指针

//结构体类型的定义
struct stu
{
	char* name;
	int age;
};
int main()
{
	struct stu* p = (struct stu*)malloc(sizeof(struct stu));
	if (p == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	p->name = (char*)malloc(strlen("lihua") + 1);
	if (p->name == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	strcpy(p->name, "lihua");
	p->age = 22;
	printf("p->name = %s, p->age = %d\n", p->name, p->age);
	printf("(*p).name = %s, (*p).age = %d\n", (*p).name, (*p).age);
	free(p->name);
	p->name = NULL;
	free(p);
	p = NULL;
	return 0;
}

[C语言]结构体进阶与枚举联合_第13张图片

结构体内存对齐

计算结构体的大小与介绍offsetof:

//计算下列结构体所占字节数
struct S1
{
	char c1;
	int i;
	char c2;
};//12byte
struct S2
{
	char c1;
	char c2;
	int i;
};//8byte
struct S3
{
	double d;
	char c;
	int i;
};//16byte
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};//32byte

[C语言]结构体进阶与枚举联合_第14张图片

[C语言]结构体进阶与枚举联合_第15张图片

[C语言]结构体进阶与枚举联合_第16张图片

[C语言]结构体进阶与枚举联合_第17张图片

结构体内存对齐的规则:

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

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处.

   对齐数 = 编译器默认的一个对齐数与该成员大小的较小值.

   VS中的默认对齐数为8..

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

4.如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处 ,结构体的总大小就是所

有的最大对齐数(含嵌套结构体的对齐数)的整数倍.

结构体内存对齐存在的原因:

1.平台原因:不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则会抛出硬件异常.

2.性能原因:数据结构(尤其是栈)应该尽可能的在自然边界上对齐,处理器需要两次进行内存访问,而对其的内存只需要进行一次内存访问.

[C语言]结构体进阶与枚举联合_第18张图片

总体来说:结构体的内存对齐是拿空间换取时间的做法.

我们在设计结构体的时候既满足结构体对齐又节省空间的方法:让占用空间小的成员尽量集中到一起.

修改默认对齐数:

#pragma back(4)
struct S1
{
	int i;
	double d;
};
#pragma back()
int main()
{
	printf("%d\n", sizeof(struct S1));
	return 0;
}

[C语言]结构体进阶与枚举联合_第19张图片

[C语言]结构体进阶与枚举联合_第20张图片

结构体传参

struct S
{
	int data[1000];
	int num;
};
void print1(struct S ss)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ss.data[i]);
	}
	printf("%d\n", ss.num);
}
void print2(const struct S* ps)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("%d\n", ps->num);
}
int main()
{
	struct S s = { {1,2,3},100 };
	print1(s);//传值调用
	print2(&s);//传址调用
	return 0;
}

[C语言]结构体进阶与枚举联合_第21张图片

结构体实现位段

位段的定义:

位段的声明和结构体是类似的,不过有两个不同:

1.位段的成员必须是int、unsigned int、signed int、char.

2.位段的成员名后边有一个冒号和一个数字.

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};//8byte
//位段是用来节省空间的
//注:一般情况下位段的成员是同一类型的

位段的内存分配:

1.位段的成员可以是int、unsigned int、signed int 或char类型.

2.位段的空间是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的.

3.位段涉及很多不确定性因素,位段是不跨平台的,注意可移植的程序避免使用位段.

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};//3byte
//char只涉及到一个字节 因此不考虑大小端的问题
int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

[C语言]结构体进阶与枚举联合_第22张图片 位段的跨平台问题:

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

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

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

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

总结:跟结构体相比,位段可以达到同样的效果,但是可以很好的节省空间,但是也存在不跨平台的问题.
位段的应用:

[C语言]结构体进阶与枚举联合_第23张图片

枚举:

枚举类型的定义

枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内.

enum 枚举名
{
    枚举值列表
};

枚举的性质:

在枚举值表中应列出所有可用值,也称为枚举元素.

枚举值是常量,不能在程序中用赋值语句再对它赋值.

枚举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2...

enum Day
{
	//枚举常量
	Mon=1,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
int main()
{
	enum Day d = Fri;
	printf("%d\n", Mon);
	printf("%d\n", Tues);
	printf("%d\n", Wed);
	return 0;
}

[C语言]结构体进阶与枚举联合_第24张图片

枚举的优点

1.增加代码的可读性和维护性.

2.和#define定义的标识符相比,枚举有类型检查,更加严谨.

3.防止了命名污染(封装).

4.与#define相比,便于调试.

5.使用方便,一次可以定义多个常量.

枚举的使用

//枚举一般配合switch使用
enum Color
{
	red,
    blue,
    green,
    pink,
    yellow,
    black,
    white
};
int main()
{
	int value = 0;
	scanf("%d", &value);
	enum Color color;
	switch (value)
	{
	case red:
		printf("红色\n");
		break;
	case blue:
		printf("蓝色\n");
		break;
	case green:
		printf("绿色\n");
		break;
	case pink:
		printf("粉色\n");
		break;
	case yellow:
		printf("黄色\n");
		break;
	default:
		printf("输入错误\n");
		break;
	}
	return 0;
}

[C语言]结构体进阶与枚举联合_第25张图片

联合:

联合类型的定义

union Un
{
	int a;
	char c;
};
int main()
{
	union Un u;
	printf("%p\n", &u);
	printf("%p\n", &(u.a));
	printf("%p\n", &(u.c));
	return 0;
}

[C语言]结构体进阶与枚举联合_第26张图片

union Un
{
	int a;
	char c;
};
int main()
{
	union Un u;
	u.a = 0x11223344;
	u.c = 0x00;
	return 0;
}

[C语言]结构体进阶与枚举联合_第27张图片

联合的特点

联合的成员是共用一块空间的,这样一个联合变量的大小,至少是最大成员的大小.

//判断当前机器是大端还是小端
int check_sys()
{
	union 
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}
int main()
{
	//01 00 00 00 -小端
	//00 00 00 01 -大端
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

[C语言]结构体进阶与枚举联合_第28张图片

[C语言]结构体进阶与枚举联合_第29张图片

[C语言]结构体进阶与枚举联合_第30张图片

联合大小的计算

联合的大小至少是最大成员的大小,当最大成员不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍.

[C语言]结构体进阶与枚举联合_第31张图片

C语言编程训练(牛客网)

1.字符串旋转结果

[C语言]结构体进阶与枚举联合_第32张图片

#include
//方法1
int IsLeftMove(char arr1[], char arr2[])
{
	int len = strlen(arr1);
	int i = 0;
	for (i = 1; i <= len; i++)
	{
		int j = 0;
		char tmp = arr1[0];
		for (j = 0; j < len - 1; j++)
		{
			arr1[j] = arr1[j + 1];
		}
		arr1[len - 1] = tmp;
		if (strcmp(arr1, arr2) == 0)
		{
			return 1;
		}
	}
	return 0;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "cdefab";
	int ret = IsLeftMove(arr1, arr2);
	if (ret == 1)
	{
		printf("OK\n");
	}
	else
	{
		printf("No\n");
	}
	return 0;
}
//方法2:使用库函数
int IsLeftMove(char arr1[], char arr2[])
{
	int len1 = strlen(arr1);
	int len2 = strlen(arr2);
	if (len1 != len2)
	{
		return 0;
	}
	strncat(arr1, arr1, len1);
	char* ret = strstr(arr1, arr2);
	if (ret == NULL)
	{
		return 0;
	}
	else
	{
		return 1;
	}
}
int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "cdefab";
	int ret = IsLeftMove(arr1, arr2);
	if (ret == 1)
	{
		printf("OK\n");
	}
	else
	{
		printf("No\n");
	}
	return 0;
}

[C语言]结构体进阶与枚举联合_第33张图片

2.BC96-有序序列判断

[C语言]结构体进阶与枚举联合_第34张图片

#include 
int main()
{
    int n = 0;
    scanf("%d", &n);
    int arr[50] = { 0 };
    int i = 0;
    int flag1 = 0;//假设原数组为升序
    int flag2 = 0;//假设原数组为降序
    for (i = 0; i < n; i++)
    {
        scanf("%d", &arr[i]);
    }
    for (i = 0; i < n - 1; i++)
    {
        if (arr[i] > arr[i + 1])
        {
            flag1 = 1;
        }
        else if (arr[i] < arr[i + 1])
        {
            flag2 = 1;
        }
        else
        {
            flag1 = 0;
            flag2 = 0;
        }
    }
    if (flag1 + flag2 == 0 || flag1 + flag2 == 1)
    {
        printf("sorted\n");
    }
    else
    {
        printf("unsorted\n");
    }
    return 0;
}

3.BC106-上三角矩阵判定

[C语言]结构体进阶与枚举联合_第35张图片

#include 
int main()
{
    int n = 0;
    scanf("%d", &n);
    int arr[10][10] = { 0 };
    int i = 0;
    int j = 0;
    int flag = 1;
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
        {
            scanf("%d", &arr[i][j]);
        }
    }
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < i; j++)
        {
            if (arr[i][j] != 0)
            {
                flag = 0;
                goto again;
            }
        }
    }
again:
    if (flag == 0)
    {
        printf("NO\n");
    }
    else
    {
        printf("YES\n");
    }
    return 0;
}

4.BC107—矩阵转置

[C语言]结构体进阶与枚举联合_第36张图片

#include 
int main()
{
    int n = 0;
    int m = 0;
    scanf("%d %d", &n, &m);
    int arr[10][10] = { 0 };
    int i = 0;
    int j = 0;
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < m; j++)
        {
            scanf("%d", &arr[i][j]);
        }
    }
    for (j = 0; j < m; j++)
    {
        for (i = 0; i < n; i++)
        {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
    return 0;
}

5.BC115-小乐乐与欧几里得 

[C语言]结构体进阶与枚举联合_第37张图片                                                                   

int main()
{
    int n = 0;
    int m = 0;
    while (scanf("%d %d", &n, &m) == 2)
    {
        int min = n < m ? n : m;
        int max = n > m ? n : m;
        int i = min;
        int j = max;
        while (1)
        {
            if (n % i == 0 && m % i == 0)
            {
                break;
            }
            i--;
        }
        //i就是最大公约数
        while (1)
        {
             if (j % n == 0 && j % m == 0)
             {
                    break;
             }
             j++;
        }
        //j就是最小公倍数
        printf("%d\n", i + j);
    }
    return 0;
}
//方法2:
int main()
{
	long n = 0;
	long m = 0;
	while (scanf("%d %d", &n, &m) == 2)
	{
		long i = n;
		long j = m;
		long ret = 0;
		while ((ret = i % j) != 0) 
		{
			i = j;
			j = ret;
		}
		printf("%ld", j + (n * m) / j);
	}
	return 0;
}

   

你可能感兴趣的:(c++,c语言)