目录
2. 一维、二维数组和字符串的地址以及指向变量、数组、字符串、函数、结构体的指针变量的 定义。通过指针引用以上各类型数据。
3. 通过结构体构成链表,单向链表的建立,结点数据的输出、删除与插入。
指针
1. 地址与指针变量的概念,地址运算符与间址运算符。
- 地址:
- 地址是指内存中某个位置的唯一标识。
- 在C语言中,可以使用
&
运算符获取变量的地址,即取地址运算符。例如,&variable
获取变量variable
的地址。- 地址是一个无符号整数值,通常以十六进制表示。
示例代码:
#include <stdio.h> int main() { int num = 10; float pi = 3.14159; char ch = 'A'; printf("Address of num: %p\n", &num); printf("Address of pi: %p\n", &pi); printf("Address of ch: %p\n", &ch); return 0; }
输出结果:
Address of num: 0x7ffd3993a12c Address of pi: 0x7ffd3993a130 Address of ch: 0x7ffd3993a131
在上述示例中,我们使用
&
运算符获取变量num
、pi
和ch
的地址,并使用%p
格式说明符打印地址。
- 指针变量:
- 指针是一个存储了内存地址的变量。
- 在C语言中,可以使用指针来间接访问和操作内存中的数据。
- 声明指针变量时,需要指定指针所指向的数据类型。例如,
int* ptr;
声明了一个指向整数的指针变量。- 使用
*
运算符可以访问指针所指向地址的值,即间接寻址运算符。示例代码:
#include <stdio.h> int main() { int num = 10; int* ptr = # printf("Value of num: %d\n", num); printf("Address of num: %p\n", &num); printf("Value of ptr: %p\n", ptr); printf("Value at address stored in ptr: %d\n", *ptr); return 0; }
输出结果:
Value of num: 10 Address of num: 0x7ffd3993a12c Value of ptr: 0x7ffd3993a12c Value at address stored in ptr: 10
在上述示例中,我们声明了一个整型指针变量
ptr
,并将其初始化为变量num
的地址。通过*ptr
,我们可以访问指针ptr
所指向地址的值。地址运算符
&
取得变量的地址,而间址运算符*
则用于访问指针所指向的地址的值。这两个运算符是在处理指针和内存操作时非常常用的工具。
2. 一维、二维数组和字符串的地址以及指向变量、数组、字符串、函数、结构体的指针变量的 定义。通过指针引用以上各类型数据。
- 一维数组和字符串的地址:
- 一维数组是在内存中连续存储的相同类型元素的集合。
- 字符串是由字符组成的一维字符数组,以空字符
\0
结尾。- 一维数组和字符串名本身就代表了它们的起始地址。
示例代码:
#include <stdio.h> int main() { int arr[5] = {1, 2, 3, 4, 5}; char str[] = "Hello"; printf("Address of arr: %p\n", arr); printf("Address of str: %p\n", str); return 0; }
输出结果:
Address of arr: 0x7ffd3993a120 Address of str: 0x7ffd3993a12c
在上述示例中,我们使用数组名
arr
和字符串名str
直接打印它们的地址。
- 二维数组的地址:
- 二维数组是由多个一维数组排列组成的矩阵形式的数组。
- 二维数组的地址表示整个二维数组的起始地址,也就是第一个一维数组的地址。
示例代码:
#include <stdio.h> int main() { int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; printf("Address of matrix: %p\n", matrix); return 0; }
输出结果:
Address of matrix: 0x7ffd3993a120
在上述示例中,我们使用二维数组名
matrix
直接打印它的地址。
- 指向变量、数组、字符串、函数、结构体的指针变量的定义:
- 指针变量用于存储内存地址。
- 可以使用
*
符号定义指针变量,指定指针所指向的类型。- 对于变量和数组,可以使用取地址运算符
&
获取其地址,并将其赋值给指针变量。- 对于字符串,直接将字符串名赋值给指针变量即可。
- 对于函数,可以使用函数名作为指针变量。
- 对于结构体,可以使用结构体类型加上
*
定义指向结构体的指针变量。示例代码:
#include <stdio.h> int main() { int num = 10; int arr[5] = {1, 2, 3, 4, 5}; char str[] = "Hello"; // 指向变量的指针 int *ptr_num = # // 指向数组的指针 int *ptr_arr = arr; // 指向字符串的指针 char *ptr_str = str; printf("Value of num: %d\n", *ptr_num); printf("First element of arr: %d\n", *ptr_arr); printf("String: %s\n", ptr_str); return 0; }
输出结果:
Value of num: 10 First element of arr: 1 String: Hello
在上述示例中,我们定义了指向变量
num
、数组arr
和字符串str
的指针变量ptr_num
、ptr_arr
和ptr_str
。通过解引用操作符*
可以访问这些指针所指向的数据。以上是指针引用不同类型的数据的示例。可以通过指针来访问和修改变量、数组、字符串的值,以及调用函数和操作结构体。指针的灵活性使得在C语言中可以进行复杂的内存操作和数据操作。
3. 用指针作函数参数。
在C语言中,可以使用指针作为函数参数,以便在函数内部对传入的变量进行修改或操作。这样可以实现通过引用传递(Pass by Reference),而不仅仅是值传递(Pass by Value)。以下是使用指针作为函数参数的示例:
#include <stdio.h> // 函数通过指针修改变量的值 void modifyValue(int* ptr) { *ptr = 100; } // 函数通过指针交换两个变量的值 void swap(int* a, int* b) { int temp = *a; *a = *b; *b = temp; } int main() { int num = 5; printf("Before modifyValue: %d\n", num); // 通过指针修改变量的值 modifyValue(&num); printf("After modifyValue: %d\n", num); int a = 10, b = 20; printf("Before swap: a = %d, b = %d\n", a, b); // 通过指针交换两个变量的值 swap(&a, &b); printf("After swap: a = %d, b = %d\n", a, b); return 0; }
输出结果:
Before modifyValue: 5 After modifyValue: 100 Before swap: a = 10, b = 20 After swap: a = 20, b = 10
4. 返回地址值的函数。
在C语言中,可以编写返回地址值的函数,使函数返回值为一个指针,指向某个内存地址。这样可以将函数内部计算或创建的数据结构通过指针返回给调用者使用。以下是一个返回地址值的函数示例:
#include <stdio.h> #include <stdlib.h> // 返回动态分配内存的数组的地址 int* createArray(int size) { int* arr = (int*)malloc(size * sizeof(int)); // 假设对数组进行初始化 for (int i = 0; i < size; i++) { arr[i] = i + 1; } return arr; } int main() { int size = 5; int* ptr = createArray(size); printf("Array elements: "); for (int i = 0; i < size; i++) { printf("%d ", ptr[i]); } printf("\n"); free(ptr); // 释放动态分配的内存 return 0; }
输出结果:
Array elements: 1 2 3 4 5
在上述示例中,我们定义了一个名为
createArray
的函数,它接受一个参数size
来指定要创建的数组大小。函数内部使用malloc
动态分配了一块内存来存储整数数组,并进行了初始化。然后,函数返回这块内存的地址。在
main
函数中,我们调用createArray
函数,并将返回的地址赋值给指针变量ptr
。通过该指针变量,我们可以访问并打印函数内部创建的数组。需要注意的是,由于动态分配的内存使用完后需要手动释放,我们在程序结束前调用了
free
函数来释放通过malloc
分配的内存。这是为了避免内存泄漏问题。通过返回地址值的函数,我们可以方便地将动态创建的数据结构传递给调用者,并且可以灵活地在函数内部进行数据的计算和处理。
5. 指针数组,指向指针的指针。
指针数组和指向指针的指针是C语言中非常有用的概念。
指针数组是一个数组,其元素都是指针。每个指针可以指向不同的数据或对象。可以使用指针数组来管理一组相关的指针。
指向指针的指针是指一个指针变量存储了另一个指针变量的地址。通过指向指针的指针,可以间接访问指针所指向的数据或对象。
以下是指针数组和指向指针的指针的示例:
#include <stdio.h>
int main() {
int num1 = 10, num2 = 20, num3 = 30;
int* ptr1 = &num1;
int* ptr2 = &num2;
int* ptr3 = &num3;
// 定义指针数组
int* ptrArr[] = {ptr1, ptr2, ptr3};
int i;
// 通过指针数组访问不同的指针和对应的值
for (i = 0; i < 3; i++) {
printf("Value at index %d: %d\n", i, *ptrArr[i]);
}
// 定义指向指针的指针
int** doublePtr = &ptr1;
// 通过指向指针的指针访问指针和对应的值
printf("Value pointed by doublePtr: %d\n", **doublePtr);
return 0;
}
输出结果:
Value at index 0: 10
Value at index 1: 20
Value at index 2: 30
Value pointed by doublePtr: 10
在上述示例中,我们定义了三个整型变量 num1
、num2
和 num3
,以及对应的指针 ptr1
、ptr2
和 ptr3
。这些指针存储着相应变量的内存地址。
然后,我们定义了一个指针数组 ptrArr
,其中的元素分别指向 ptr1
、ptr2
和 ptr3
。通过指针数组,我们可以访问每个指针,并使用解引用操作符 *
来获取所指向的值。
接下来,我们定义了一个指向指针的指针 doublePtr
,它存储了 ptr1
的地址。通过指向指针的指针,我们可以间接访问 ptr1
和它所指向的值。
指针数组和指向指针的指针可以在许多情况下很有用,例如在函数中传递多个指针或者处理复杂的数据结构。使用这些概念,可以更加灵活地管理和操作内存中的数据。
结构体(即“结构”)与共同体(即“联合”)
1. 用 typedef 说明一个新类型。
在C语言中,可以使用
typedef
关键字为现有类型定义一个新的类型名称。这样做的好处是可以增强代码的可读性和可维护性。以下是使用typedef
定义新类型的示例:#include <stdio.h> typedef int Age; // 为int类型定义一个新类型Age int main() { Age myAge = 25; printf("My age is: %d\n", myAge); return 0; }
输出结果:
My age is: 25
在上述示例中,我们使用
typedef
关键字将int
类型定义为一个新的类型Age
。然后,我们在main
函数中使用Age
类型来声明一个变量myAge
,并给它赋值为25。最后,我们打印出myAge
的值。通过使用
typedef
,我们可以为任何现有的类型创建一个新的别名,并使用这个别名来代替原有的类型名称。这样使得代码更加易读、直观,并且方便在需要时对类型进行修改和管理。
2. 结构体和共用体类型数据的定义和成员的引用。
在C语言中,结构体(
struct
)和共用体(union
)是用于定义自定义的复合数据类型的关键工具。
- 结构体(
struct
)是一种可以存储多个不同类型的数据成员的数据类型。通过定义结构体,可以将相关的数据组织在一起,形成一个更复杂的数据结构。以下是结构体的定义和成员引用的示例:#include <stdio.h> // 定义一个struct结构体类型 typedef struct { int age; char name[20]; } Person; int main() { // 声明一个Person类型的结构体变量 Person person1; // 设置结构体成员的值 person1.age = 25; strcpy(person1.name, "John"); // 访问结构体成员的值 printf("Name: %s\n", person1.name); printf("Age: %d\n", person1.age); return 0; }
输出结果:
Name: John Age: 25
在上述示例中,我们使用
typedef
为结构体定义了一个新的名称Person
,该结构体包含一个int
类型的age
成员和一个char
数组类型的name
成员。在
main
函数中,我们声明了一个Person
类型的结构体变量person1
,然后通过成员运算符.
设置和访问结构体成员的值。- 共用体(
union
)是一种特殊的数据类型,它允许以相同的内存位置存储不同类型的数据。共用体中的数据成员共享同一块内存,只能同时存储其中的一个成员的值。以下是共用体的定义和成员引用的示例:#include <stdio.h> // 定义一个union共用体类型 typedef union { int intValue; float floatValue; char stringValue[20]; } Data; int main() { // 声明一个Data类型的共用体变量 Data data; // 设置共用体成员的值 data.intValue = 10; printf("intValue: %d\n", data.intValue); data.floatValue = 3.14; printf("floatValue: %.2f\n", data.floatValue); strcpy(data.stringValue, "Hello"); printf("stringValue: %s\n", data.stringValue); return 0; }
输出结果:
intValue: 10 floatValue: 3.14 stringValue: Hello
在上述示例中,我们使用
typedef
为共用体定义了一个新的名称Data
,该共用体包含一个int
类型的intValue
成员、一个float
类型的floatValue
成员和一个char
数组类型的stringValue
成员。在
main
函数中,我们声明了一个Data
类型的共用体变量data
,然后通过成员运算符.
设置和访问共用体成员的值。注意共用体中的数据成员共享同一块内存,改变其中一个成员的值将影响其他成员。通过结构体和共用体,我们可以定义自己的复合数据类型,并灵活地存储和操作不同类型的数据。这样能够更好地组织和管理程序中的数据。
3. 通过结构体构成链表,单向链表的建立,结点数据的输出、删除与插入。
通过结构体构成链表,可以实现单向链表的建立、结点数据的输出、删除和插入等操作。下面是一个示例代码:
#include <stdio.h> #include <stdlib.h> // 定义链表节点的结构体 typedef struct Node { int data; struct Node* next; } Node; // 在链表末尾插入节点 void insertNode(Node** head, int value) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = value; newNode->next = NULL; if (*head == NULL) { // 空链表,将新节点作为头节点 *head = newNode; } else { // 非空链表,遍历到链表末尾再插入新节点 Node* current = *head; while (current->next != NULL) { current = current->next; } current->next = newNode; } } // 删除指定数值的节点 void deleteNode(Node** head, int value) { Node* temp = *head; Node* prev = NULL; // 遍历链表,找到要删除的节点 while (temp != NULL && temp->data != value) { prev = temp; temp = temp->next; } if (temp == NULL) { // 未找到要删除的节点 printf("Node with value %d not found.\n", value); return; } if (prev == NULL) { // 删除头节点 *head = temp->next; } else { // 删除中间或尾部节点 prev->next = temp->next; } free(temp); } // 输出链表中的数据 void printList(Node* head) { Node* current = head; if (current == NULL) { printf("Linked list is empty.\n"); return; } while (current != NULL) { printf("%d ", current->data); current = current->next; } printf("\n"); } int main() { Node* linkedList = NULL; // 在链表末尾插入节点 insertNode(&linkedList, 10); insertNode(&linkedList, 20); insertNode(&linkedList, 30); insertNode(&linkedList, 40); // 输出链表数据 printf("Linked list: "); printList(linkedList); // 删除节点 deleteNode(&linkedList, 20); // 输出链表数据 printf("Linked list after deletion: "); printList(linkedList); return 0; }
输出结果:
Linked list: 10 20 30 40 Linked list after deletion: 10 30 40
在上述示例中,我们定义了一个单向链表的节点结构体
Node
,其中包含数据成员data
和指向下一个节点的指针next
。通过调用
insertNode
函数,可以在链表末尾插入新的节点。函数会根据链表是否为空进行不同的操作,将新节点插入链表。通过调用
deleteNode
函数,可以删除链表中指定数值的节点。函数会遍历链表找到要删除的节点,然后进行删除操作。如果链表中不存在要删除的节点,则会输出相应的错误信息。调用
printList
函数可以输出链表中的数据。函数会遍历链表,依次输出每个节点的数据。在
main
函数中,我们首先创建一个空链表linkedList
。然后使用insertNode
函数插入节点,并使用printList
函数输出链表数据。接着调用deleteNode
函数删除一个节点,并再次使用printList
函数输出链表更新后的数据。通过以上操作,可以实现单向链表的建立、节点数据的输出、删除和插入等功能。