学习目标:五天学会c语言高级
学习内容:
1. 结构体
2.共用体
1.结构体
结构体其实就是一种特殊的数据类型,像之前我们学过的int类型,char类型,float类型这些都是基本的数据类型,但是在实际问题中只有这些数据类型是不够的,有时候我们需要其中的几种一起来修饰某个变量,例如一个学生的信息就需要学号(字符串),姓名(字符串),年龄(整形)等等,这些数据类型都不同但是他们又是表示一个整体,要存在联系,那么我们就需要一个新的数据类型。
——结构体,他就将不同类型的数据存放在一起,组成一个集合,作为一个整体进行处理。
1.1结构体的定义
//申明一个结构体
struct student
{
char name[MAXTITL];//表示的学生名字;
char sex[MAXAUTL];//表示学生性别;
int score;//表示的学生成绩;
};//注意分号不能少,这也相当于一条语句;
struct student 属于自定义的数据类型。
1.2结构体的大小
如何计算结构体的大小: sizeof(数据类型)
一个结构体的大小取决于它包含的成员变量的大小以及内存对齐规则。在C语言中,结构体的大小通常是其成员变量大小之和,但为了满足特定的内存对齐要求,编译器可能会在结构体的成员之间插入一些填充字节。
32OS:一个结构体的大小,首先先找出结构体内最大的数据类型,如果该数据类型没有超过4字节,那么就按照最大的数据类型进行对齐
Struct a
{
Char a;
Short b;
};--->4字节
如果结构体内的数据类型超过4字节,那么就按照4字节分配
Struct b
{
Char name[29];
Int a;
Double b;
};---->44---->32OS
1.3结构体变量的赋值
要给结构体赋值,你可以使用以下方法:
1.逐个赋值:逐个给结构体的成员赋值。
struct Student {
char name[20];
int age;
float gpa;
};
struct Student s;
strcpy(s.name, "John");
s.age = 20;
s.gpa = 3.5;
2.初始化:在定义结构体变量时,可以在声明的同时进行初始化。
struct Student {
char name[20];
int age;
float gpa;
};
struct Student s = {"John", 20, 3.5};
3.使用指针:如果有一个指向结构体的指针,可以通过指针来访问结构体的成员并赋值。
struct Student {
char name[20];
int age;
float gpa;
};
struct Student s;
struct Student *ptr = &s;
strcpy(ptr->name, "John");
ptr->age = 20;
ptr->gpa = 3.5;
1.4结构体变量的访问
访问结构体成员可以使用点号 .
运算符或箭头 ->
运算符,具体取决于你使用的是结构体变量还是结构体指针。
-
使用点号
.
运算符:当你有一个结构体变量时,可以使用点号来访问其成员。struct Student { char name[20]; int age; float gpa; }; struct Student s; s.age = 20; strcpy(s.name, "John"); s.gpa = 3.5;
2.使用箭头
->
运算符:当你有一个指向结构体的指针时,应使用箭头运算符来访问其成员。struct Student { char name[20]; int age; float gpa; }; struct Student s; struct Student *ptr = &s; ptr->age = 20; strcpy(ptr->name, "John"); ptr->gpa = 3.5;
无论是使用点号还是箭头,都可以用来读取或修改结构体成员的值,具体取决于你的需求和结构体变量的类型(变量还是指针)。
2.共用体
2.1什么是共用体
共用体(Union)是C和C++中的一种复合数据类型,它允许在同一个内存位置存储不同类型的数据。与结构体不同,共用体的成员共享相同的内存位置。这意味着在任何给定时刻,共用体只能容纳其中一个成员的值。
共用体的特点包括:
-
共享内存:所有成员共享相同的内存位置,它们在内存中的起始地址相同。
-
节省内存:共用体可用于节省内存,因为它们只存储其中一个成员的值,而不是存储所有成员的值。
-
不同类型的数据:不同成员可以具有不同的数据类型,因此可以在共用体中存储不同类型的数据。
-
占用最大成员的大小:共用体的大小等于其成员中占用空间最大的成员的大小。
2.2共用体的定义
与结构体比较相似。
union UnionName {
member1Type member1Name;
member2Type member2Name;
// ... 可以有多个成员
};
其中:
UnionName
是共用体的名称。member1Type
、member2Type
等是不同成员的数据类型。member1Name
、member2Name
等是成员的名称。
2.3共用体的大小
共用体(Union)的大小等于它的成员中占用空间最大的那个成员的大小。这是因为共用体的所有成员共享相同的内存位置,因此共用体的大小必须足够大,以容纳其最大的成员。
当共用体内部元素的数据类型没有超过4字节,按照最大的一个数据类型去共用空间,如果超过4字节,先按照4字节分配,然后共用开辟出来的最大的空间
共用体的大小也可能因编译器和平台而异,但它始终等于其最大成员的大小。这是为了确保共用体可以容纳最大的成员,从而保持内存对齐和数据完整性。
2.4共用体大小端问题
共用体(Union)的大小端问题与共用体的定义和成员顺序有关。大小端(Big-Endian 和 Little-Endian)是指计算机在存储多字节数据类型(如整数或浮点数)时,高字节和低字节的存储顺序问题。
在共用体中,成员的存储顺序与它们在共用体定义中的顺序一致。这意味着,如果你在共用体中定义的第一个成员是一个整数,它将被存储在共用体的起始位置。接下来的成员将依次存储在共用体中。
大小端问题主要影响多字节整数的存储方式。在小端系统中,最低有效字节(Low-Order Byte)存储在起始地址,而在大端系统中,最高有效字节(High-Order Byte)存储在起始地址。
3.函数指针
3.1函数指针的定义
return_type (*pointer_name)(parameter_type1, parameter_type2, ...);
其中:
return_type
是函数返回值的类型。pointer_name
是函数指针的名称。parameter_type1, parameter_type2, ...
是函数的参数类型。
3.2 函数指针的使用
#include <stdio.h>
// 定义一个函数
int add(int a, int b) {
return a + b;
}
// 定义另一个函数
int subtract(int a, int b) {
return a - b;
}
int main() {
int (*operation)(int, int); // 声明一个函数指针变量
operation = add; // 将函数指针指向 add 函数
int result1 = operation(5, 3); // 调用 add 函数
operation = subtract; // 将函数指针指向 subtract 函数
int result2 = operation(5, 3); // 调用 subtract 函数
printf("Result1: %d\n", result1);
printf("Result2: %d\n", result2);
return 0;
}
在上述示例中,我们声明了一个函数指针 operation
,它可以指向具有相同参数和返回类型的函数。我们首先将它指向 add
函数,然后调用 add
函数,然后将它指向 subtract
函数,再调用 subtract
函数。
函数返回值的作用
1:函数执行成功之后得到的结果 return dest;
2:表示程序正常退出 return 0;
3:表示程序的退出状态,正数一般表示正常退出return 1;,负数表示出错退出return -1;
4. 枚举
枚举(Enumeration)是一种C语言中的数据类型,用于定义一个有限集合的命名整数常量。枚举可以帮助提高代码的可读性,使程序员能够使用易于理解的符号来表示特定的值,而不必记住相应的数字。
4.1定义方式
enum enum_name {
enumerator1,
enumerator2,
enumerator3,
// ...
};
其中:
enum_name
是枚举类型的名称。enumerator1
,enumerator2
,enumerator3
等是枚举常量的名称,它们代表了整数值。
每个枚举常量都隐式地与一个整数值相关联,第一个常量默认为0,后续常量依次递增。但您可以显式地指定常量的值,如下所示:
enum Days {
Sunday = 1,
Monday = 2,
Tuesday = 3,
Wednesday = 4,
Thursday = 5,
Friday = 6,
Saturday = 7
};
4.2枚举作用
枚举类型在编程中的常见用途包括:
- 用于增加代码的可读性,使代码更具表达性。
- 用于表示有限的状态集合,例如状态机。
- 用于定义选项集合,例如标志位。
- 用于定义命名常量,以避免使用魔法数字。
5.堆区操作
堆区(Heap)是计算机内存的一部分,用于存储程序运行时动态分配的数据。在C语言中,堆区的内存分配和释放是通过函数 malloc
、calloc
、realloc
和 free
来进行的。
以下是堆区的基本操作:
-
分配内存:
malloc(size_t size)
:用于分配指定大小的内存块,并返回指向该内存块的指针。如果分配成功,返回的指针指向未初始化的内存。calloc(size_t num_elements, size_t element_size)
:用于分配指定数量和大小的内存块,并返回指向该内存块的指针。分配后的内存块中的所有字节都被初始化为零。realloc(void *ptr, size_t new_size)
:用于更改已分配内存块的大小,如果需要,还可以移动它。如果分配成功,它返回指向新内存块的指针,原内存块将被释放。
-
释放内存:
free(void *ptr)
:用于释放先前分配的内存块,以便在程序执行期间将其返回给堆区。释放后的指针不再指向有效的内存。
以下是一个示例,演示如何使用 malloc
分配内存和 fr
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr;
int n = 5;
// 分配动态数组内存
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("内存分配失败\n");
return 1;
}
// 初始化数组
for (int i = 0; i < n; i++) {
arr[i] = i * 2;
}
// 打印数组元素
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存
free(arr);
return 0;
}
请注意,在动态分配内存后,务必使用 free
函数来释放内存,以避免内存泄漏。如果不释放内存,程序会继续占用堆区内存,导致内存泄漏问题。