在C/C++实际运用中,指针、数组和结构体这三个部分基本上都是同时使用的,并在不同的函数间调用。今天就来浅析一下三者的使用与联系,适用于有一定基础,但理解不是很清的同学~
目录
(指针和结构体的知识比较重要,就再回顾一下基础,数组的知识单用比较简单,就不赘述)
在 C/C++中,指针通常是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。
定义指针变量的一般形式为:类型说明符 *变量名。?
int *ptr; // 声明一个指向整数的指针变量
char *p1; //表示 p1 是一个指针变量,它的值是某个字符变量的地址。
类型说明符表示指针变量所指向变量的数据类型;*表示这是一个指针变量;变量名表示定义的指针变量名,其值是一个地址。
通常涉及两个运算符:(可以想象自己在一个多层循环嵌套的房间内,指针就是房间外的门牌号)
& 取址运算符:返回变量的内存地址(往外走一圈,指向门牌号,我习惯称引用)
* 取值运算符:访问指针指向的变量(往里走一圈,指向房间内容,我习惯称解引)
#include <stdio.h>
int main()
{
int i = 0x12345678;
int* p = &i;
printf("0x%08x \n", i); //打印结果: 0x12345678
printf("0x%08x \n", &i); //打印结果: 0x004ffed0
printf("%d \n", sizeof(i)); //打印结果: 4
printf("0x%08x \n", p); //打印结果: 0x004ffed0
printf("0x%08x \n", &p); //打印结果: 0x004ffecc
printf("%d \n", sizeof(p)); //打印结果: 8
// 指针变量p指向的对象是变量i
// 1.指向的对象变量i的地址是&i(0x004ffed0)
// 2.指向的对象变量i的类型是int
// 3.指向的对象变量i的内容是0x12345678
// 4.指针变量p本身的地址是&p(0x004ffecc)
// 5.指针变量p本身的类型是int*
// 6.指针变量p本身的内容是&i(0x004ffed0)
return 0;
}
结构体是一种用户自定义的数据类型,它允许将多个不同类型的变量组合在一起,形成一个逻辑上相关的数据单元。结构体在C/C++中非常常用,可以用来表示复杂的数据结构和实体。
结构体的定义形式:
struct Person {
char name[20];
int age;
float height;
};
访问成员通常涉及两个运算符:
下面是成员选择运算符的使用实例。(指向运算符的使用涉及结构体和指针,就放到本文第三节再详细说明)
struct Person p1; // 声明一个结构体变量
strcpy(p1.name, "John"); // 使用strcpy函数将字符串赋给结构体变量的成员name
p1.age = 25; // 赋值给结构体变量的成员age
p1.height = 1.75; // 赋值给结构体变量的成员height
printf("Name: %s, Age: %d, Height: %.2f", p1.name, p1.age, p1.height); // 输出结构体变量的成员值
上述代码的输出如下:
Name: John, Age: 25, Height: 1.75
此外,当使用结构体时,通常同时用typedef关键字,可以通过typedef关键字为结构体定义一个新的类型名称。这样做的好处是可以方便地使用新的类型名称来声明结构体变量,而无需每次都使用struct关键字。
示例如下:
typedef struct {
char name[20];
int age;
} Person;
int main() {
Person p1; // 使用新的类型名称Person声明结构体变量
strcpy(p1.name, "John");
p1.age = 25;
printf("Name: %s, Age: %d", p1.name, p1.age);
return 0;
}
在上例中,通过typedef关键字将匿名结构体定义为Person类型。这样,就可以直接使用Person作为新的类型名称来声明结构体变量p1,而无需每次都使用struct关键字,可以使代码更加简洁和易读,特别是在需要频繁使用结构体类型的情况下。
事实上,数组名本身就是一个指向数组第一个元素的指针。
声明数组指针的形式如下:
int arr[5]; // 声明一个包含5个整数的数组
int *ptr; // 声明一个指向整数的指针变量
ptr = arr; // 将数组名赋给指针变量,即指向数组的第一个元素
这样,ptr指针就指向了该数组,再通过解引运算符*就可以访问数组内容
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 将数组名赋给指针变量
printf("%d ", *ptr); // 输出指针所指向的第一个元素的值
printf("%d", *(ptr + 1)); // 输出指针所指向的第二个元素的值
再看一下,使用指针遍历数组的过程:
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 将数组名赋给指针变量
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i)); // 输出指针所指向的元素的值
}
看下对应的输出:
1 2 3 4 5
?接下来就给把数组指针配合函数来使用了:
void printArray(int *ptr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", *(ptr + i)); // 输出指针所指向的元素的值
}
}
int arr[5] = {1, 2, 3, 4, 5};
printArray(arr, 5); // 将数组名作为参数传递给函数(数组名本身就是一个指针)
?这里的输出结果和上面的是一样的,通过循环打印出指针所指向元素的值。
结构体指针允许通过指针来访问和操作结构体变量的成员。
声明结构体指针的形式:
struct Person {
char name[20];
int age;
};
struct Person p1; // 声明一个结构体变量
struct Person *ptr; // 声明一个指向结构体的指针变量
ptr = &p1; // 将结构体变量的地址赋给指针ptr
接下里,就到了使用1.2中提到的指向运算符->来访问成员:
struct Person {
char name[20];
int age;
};
struct Person p1; // 声明一个结构体变量
struct Person *ptr = &p1; // 声明一个指向结构体的指针,并将结构体变量的地址赋给指针ptr
strcpy(ptr->name, "John"); // 使用指针访问结构体变量的成员name,并赋值
ptr->age = 25; // 使用指针访问结构体变量的成员age,并赋值
printf("Name: %s, Age: %d\r\n", ptr->name, ptr->age); // 使用指针输出结构体变量的成员值
printf("Name: %s, Age: %d", p1.name, p1.age); // 使用指针输出结构体变量的成员值
看下结果:?
Name: John, Age: 25
Name: John, Age: 25
可以发现,两个输出函数打出的内容是一致的;这是因为ptr是指向p1的结构体指针,对结构体指针进行操作,本质上是对结构体变量p1操作,因此自然p1的值也就改变了。
再看一下,动态分配结构体指针的过程:
struct Person {
char name[20];
int age;
};
struct Person *ptr = (struct Person*)malloc(sizeof(struct Person)); // 动态分配一个结构体大小的内存空间
strcpy(ptr->name, "John"); // 使用指针访问结构体变量的成员name,并赋值
ptr->age = 25; // 使用指针访问结构体变量的成员age,并赋值
printf("Name: %s, Age: %d", ptr->name, ptr->age); // 使用指针输出结构体变量的成员值
free(ptr); // 释放动态分配的内存空间
结构体数组即创建一个数组,其中每个元素都是一个结构体。
声明结构体和结构体数组的形式:
struct Person {
char name[20];
int age;
};
struct Person people[5]; // 声明一个包含5个结构体的结构体数组
通常,需要带初始值来初始化结构体数组:
struct Person {
char name[20];
int age;
};
struct Person people[3] = {
{"John", 25},
{"Alice", 30},
{"Bob", 35}
};
访问结构体数组的元素就比较简单,直接使用成员运算符访问即可:
struct Person {
char name[20];
int age;
};
struct Person people[3] = {
{"John", 25},
{"Alice", 30},
{"Bob", 35}
};
printf("Name: %s, Age: %d\n", people[0].name, people[0].age); // 访问结构体数组的第一个元素
printf("Name: %s, Age: %d\n", people[1].name, people[1].age); // 访问结构体数组的第二个元素
printf("Name: %s, Age: %d\n", people[2].name, people[2].age); // 访问结构体数组的第三个元素
当结构体、数组和指针共同使用时,可以实现一些相对更难一点的数据结构和算法。
先简单扩充一下本文第3节中动态分配的例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char name[20];
int age;
};
int main()
{
struct Person *ptr = (struct Person*)malloc(2*sizeof(struct Person)); // 动态分配二个结构体大小的内存空间
strcpy(ptr->name, "John"); // 使用指针访问结构体变量的成员name,并赋值
ptr->age = 25; // 使用指针访问结构体变量的成员age,并赋值
printf("Name: %s, Age: %d\r\n", ptr->name, ptr->age); // 使用指针输出结构体变量的成员值
ptr++; //指针后移
strcpy(ptr->name, "Tom"); // 使用指针访问结构体变量的成员name,并赋值
ptr->age = 20; // 使用指针访问结构体变量的成员age,并赋值
printf("Name: %s, Age: %d", ptr->name, ptr->age); // 使用指针输出结构体变量的成员值
free(ptr); // 释放动态分配的内存空间
return 0;
}
上面代码中的输出结果如下:
Name: John, Age: 25
Name: TOM, Age: 20
下面是一个示例代码,演示了如何使用结构体、数组和指针来实现一个简单的学生信息管理系统:
#include <stdio.h>
#include <stdlib.h>
// 定义学生结构体
typedef struct {
int id;
char name[20];
int age;
} Student;
// 打印学生信息
void printStudent(Student* student) {
printf("学生ID:%d\n", student->id);
printf("学生姓名:%s\n", student->name);
printf("学生年龄:%d\n", student->age);
}
int main() {
int numStudents;
printf("请输入学生人数:");
scanf("%d", &numStudents);
// 动态分配内存来存储学生信息
Student* students = (Student*)malloc(numStudents * sizeof(Student));
// 输入学生信息
for (int i = 0; i < numStudents; i++) {
printf("请输入学生ID:");
scanf("%d", &(students[i].id));
printf("请输入学生姓名:");
scanf("%s", students[i].name);
printf("请输入学生年龄:");
scanf("%d", &(students[i].age));
}
// 输出学生信息
for (int i = 0; i < numStudents; i++) {
printf("第%d个学生信息:\n", i+1);
printStudent(&(students[i]));
}
// 释放内存
free(students);
return 0;
}
持续更新中~点赞收藏越多更新越快啦哈哈?