结构体指针
介绍
在C语言中,结构体指针是一种非常有用的工具,它允许我们通过指针来操作结构体,尤其是在处理动态内存分配或函数参数传递时。以下是关于C语言结构体指针的详细讲解。
定义结构体指针
首先,我们需要定义一个结构体。例如,定义一个表示学生信息的结构体:
#include <stdio.h>
struct Student {
char name[50];
int age;
float gpa;
};
然后,我们可以定义一个指向该结构体的指针:
struct Student *studentPtr;
初始化结构体指针
指向现有结构体变量
我们可以让结构体指针指向一个已有的结构体变量:
struct Student student1 = {"Alice", 20, 3.5};
struct Student *studentPtr = &student1;
动态分配内存
我们也可以使用动态内存分配来初始化结构体指针:
struct Student *studentPtr = (struct Student *)malloc(sizeof(struct Student));
if (studentPtr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
访问结构体成员
使用结构体指针访问成员
当我们有一个结构体指针时,可以使用箭头操作符 ->
来访问结构体成员:
studentPtr->age = 21;
printf("Name: %s, Age: %d, GPA: %.2f\n", studentPtr->name, studentPtr->age, studentPtr->gpa);
通过指针和解引用
我们也可以使用指针和解引用操作符 *
组合来访问结构体成员:
(*studentPtr).age = 22;
printf("Name: %s, Age: %d, GPA: %.2f\n", (*studentPtr).name, (*studentPtr).age, (*studentPtr).gpa);
不过,使用箭头操作符 ->
更加简洁和常见。
结构体指针作为函数参数
将结构体指针作为函数参数,可以高效地传递结构体数据,避免大结构体的拷贝。以下是一个示例:
void printStudent(struct Student *student) {
printf("Name: %s, Age: %d, GPA: %.2f\n", student->name, student->age, student->gpa);
}
int main() {
struct Student student1 = {"Alice", 20, 3.5};
printStudent(&student1);
struct Student *studentPtr = (struct Student *)malloc(sizeof(struct Student));
if (studentPtr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
strcpy(studentPtr->name, "Bob");
studentPtr->age = 21;
studentPtr->gpa = 3.7;
printStudent(studentPtr);
free(studentPtr);
return 0;
}
使用结构体指针构建链表
结构体指针在构建复杂数据结构(如链表)时非常有用。以下是一个简单的单链表示例:
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点
struct Node {
int data;
struct Node *next;
};
// 创建新节点
struct Node* createNode(int data) {
struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 打印链表
void printList(struct Node *head) {
struct Node *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
struct Node *head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
printList(head);
// 释放内存
struct Node *current = head;
struct Node *next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
return 0;
}
高级用法
当然,这里有更多关于C语言结构体指针的高级用法和注意事项。
多级指针(指向指针的指针)
有时候,我们可能需要使用指向指针的指针,尤其是在处理动态二维数组或在函数中修改指针变量时。以下是一个简单的示例:
#include <stdio.h>
#include <stdlib.h>
struct Student {
char name[50];
int age;
float gpa;
};
void allocateStudent(struct Student **studentPtr) {
*studentPtr = (struct Student *)malloc(sizeof(struct Student));
if (*studentPtr == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
}
int main() {
struct Student *studentPtr = NULL;
allocateStudent(&studentPtr);
strcpy(studentPtr->name, "Alice");
studentPtr->age = 20;
studentPtr->gpa = 3.5;
printf("Name: %s, Age: %d, GPA: %.2f\n", studentPtr->name, studentPtr->age, studentPtr->gpa);
free(studentPtr);
return 0;
}
在这个例子中,allocateStudent
函数使用了双重指针(struct Student **
)来分配内存并修改指针变量。
动态二维数组
使用结构体指针可以创建动态二维数组。例如,一个表示学生成绩的二维数组:
#include <stdio.h>
#include <stdlib.h>
struct Grade {
int studentId;
float score;
};
int main() {
int rows = 3;
int cols = 2;
// 分配行指针数组
struct Grade **grades = (struct Grade **)malloc(rows * sizeof(struct Grade *));
if (grades == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 分配每行的列
for (int i = 0; i < rows; i++) {
grades[i] = (struct Grade *)malloc(cols * sizeof(struct Grade));
if (grades[i] == NULL) {
printf("Memory allocation failed\n");
return 1;
}
}
// 初始化数据
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
grades[i][j].studentId = i * cols + j;
grades[i][j].score = (i + 1) * (j + 1) * 10.0;
}
}
// 输出数据
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("Student ID: %d, Score: %.2f\n", grades[i][j].studentId, grades[i][j].score);
}
}
// 释放内存
for (int i = 0; i < rows; i++) {
free(grades[i]);
}
free(grades);
return 0;
}
结构体指针与链表操作
链表是一种常见的数据结构,使用结构体指针实现链表操作是一个非常好的练习。以下是一些基本的链表操作示例,包括插入和删除节点:
插入节点
void insertAtBeginning(struct Node **head, int data) {
struct Node *newNode = createNode(data);
newNode->next = *head;
*head = newNode;
}
void insertAtEnd(struct Node **head, int data) {
struct Node *newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
return;
}
struct Node *current = *head;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
}
删除节点
void deleteNode(struct Node **head, int key) {
struct Node *temp = *head, *prev = NULL;
// 如果头节点持有要删除的值
if (temp != NULL && temp->data == key) {
*head = temp->next;
free(temp);
return;
}
// 搜索要删除的节点
while (temp != NULL && temp->data != key) {
prev = temp;
temp = temp->next;
}
// 如果没有找到该值
if (temp == NULL) return;
// 解除链接并释放内存
prev->next = temp->next;
free(temp);
}
高级链表操作
链表反转
反转链表是一个常见的操作,以下是反转单链表的代码示例:
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node *next;
};
struct Node* createNode(int data) {
struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
void reverseList(struct Node **head) {
struct Node *prev = NULL;
struct Node *current = *head;
struct Node *next = NULL;
while (current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}
*head = prev;
}
void printList(struct Node *head) {
struct Node *current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
int main() {
struct Node *head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
printf("Original List:\n");
printList(head);
reverseList(&head);
printf("Reversed List:\n");
printList(head);
// 释放内存
struct Node *current = head;
struct Node *next;
while (current != NULL) {
next = current->next;
free(current);
current = next;
}
return 0;
}
链表合并
合并两个有序链表是一个常见操作,以下是合并两个有序链表的代码示例:
struct Node* mergeLists(struct Node *l1, struct Node *l2) {
if (l1 == NULL) return l2;
if (l2 == NULL) return l1;
struct Node *head = NULL;
if (l1->data < l2->data) {
head = l1;
l1 = l1->next;
} else {
head = l2;
l2 = l2->next;
}
struct Node *current = head;
while (l1 != NULL && l2 != NULL) {
if (l1->data < l2->data) {
current->next = l1;
l1 = l1->next;
} else {
current->next = l2;
l2 = l2->next;
}
current = current->next;
}
if (l1 != NULL) {
current->next = l1;
} else {
current->next = l2;
}
return head;
}
深拷贝结构体
有时候我们需要对结构体进行深拷贝,即复制结构体及其所有嵌套的内容。以下是一个深拷贝简单结构体的示例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Address {
char street[100];
char city[50];
char state[50];
int zip;
};
struct Student {
char name[50];
int age;
float gpa;
struct Address *address;
};
struct Student* deepCopyStudent(const struct Student *original) {
struct Student *copy = (struct Student *)malloc(sizeof(struct Student));
if (copy == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
strcpy(copy->name, original->name);
copy->age = original->age;
copy->gpa = original->gpa;
copy->address = (struct Address *)malloc(sizeof(struct Address));
if (copy->address == NULL) {
printf("Memory allocation failed\n");
free(copy);
exit(1);
}
memcpy(copy->address, original->address, sizeof(struct Address));
return copy;
}
int main() {
struct Address address = {"123 Maple St", "Springfield", "IL", 62701};
struct Student student1 = {"Alice", 20, 3.5, &address};
struct Student *studentCopy = deepCopyStudent(&student1);
// 修改原始数据以验证深拷贝
strcpy(student1.address->city, "Changed City");
printf("Original Student: %s, %s\n", student1