对于C语言指针这一部分内容仅供参考可能会有出错的地方,也可能会有不足之处,希望大家可以积极地指出,会认真的汲取经验。
指针是 C 语言的核心特性之一,也是 C 语言的难点所在。掌握指针能够帮助我们更高效地操作内存,实现复杂的数据结构和算法。
一、指针的基本概念
指针是一个变量,其值为另一个变量的内存地址。通过指针,我们可以间接访问和修改内存中的数据。
1.1 指针变量的定义与初始化
指针变量的定义格式:数据类型 *指针名;
int a = 10; // 定义普通变量a
int *p; // 定义int类型的指针p
p = &a; // 将a的地址赋值给指针p,&是取地址运算符
int *q = &a; // 定义的同时初始化
1.2 指针的解引用
通过*运算符可以访问指针所指向的变量的值,这个过程称为解引用:
#include <stdio.h>
int main() {
int num = 25;
int *p = # // p指向num的地址
printf("num的值: %d\n", num);
printf("num的地址: %p\n", &num);
printf("指针p的值: %p\n", p); // 指针p存储的是num的地址
printf("p指向的值: %d\n", *p); // 通过*解引用获取值
*p = 30; // 通过指针修改所指向变量的值
printf("修改后num的值: %d\n", num);
return 0;
}
这段代码的输出会显示:
- num 的地址和指针 p 的值相同
- 通过
*p可以获取和修改 num 的值
二、指针与数组
数组名本质上是数组首元素的地址,这使得指针和数组有着紧密的联系。
2.1 指针访问数组元素
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // 数组名就是首元素地址,等价于&arr[0]
// 两种访问方式等价
printf("arr[0] = %d, *p = %d\n", arr[0], *p);
printf("arr[1] = %d, *(p+1) = %d\n", arr[1], *(p+1));
// 使用指针遍历数组
printf("数组元素: ");
for(int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // p+i指向第i个元素
}
printf("\n");
return 0;
}
2.2 指针的算术运算
指针可以进行特殊的算术运算,其运算结果与指针指向的数据类型有关:
#include <stdio.h>
int main() {
int a = 10;
char b = 'A';
int *p_int = &a;
char *p_char = &b;
printf("int指针初始地址: %p\n", p_int);
printf("int指针+1后地址: %p (差值为4字节,int类型大小)\n", p_int + 1);
printf("\nchar指针初始地址: %p\n", p_char);
printf("char指针+1后地址: %p (差值为1字节,char类型大小)\n", p_char + 1);
return 0;
}
这段代码展示了指针算术运算的特点:指针加 1 并不是简单的地址加 1,而是加上所指向数据类型的大小(字节数)。
三、指针与函数
指针在函数中的应用主要体现在两个方面:传递地址参数和返回指针。
3.1 指针作为函数参数
通过指针传递参数,可以在函数内部修改外部变量的值:
#include <stdio.h>
// 通过指针交换两个变量的值
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp;
}
int main() {
int a = 10, b = 20;
printf("交换前: a = %d, b = %d\n", a, b);
swap(&a, &b); // 传递变量地址
printf("交换后: a = %d, b = %d\n", a, b);
return 0;
}
3.2 函数返回指针
函数可以返回指针,但要注意不能返回局部变量的指针,因为局部变量在函数结束后会被释放:
#include <stdio.h>
#include <stdlib.h> // 包含malloc函数
// 正确:返回动态分配内存的指针
int *createArray(int size) {
int *arr = (int *)malloc(size * sizeof(int));
if(arr == NULL) {
printf("内存分配失败\n");
exit(1);
}
return arr;
}
int main() {
int *nums = createArray(5);
// 使用数组
for(int i = 0; i < 5; i++) {
nums[i] = i * 10;
}
printf("数组元素: ");
for(int i = 0; i < 5; i++) {
printf("%d ", nums[i]);
}
// 释放动态分配的内存
free(nums);
return 0;
}
3.3 函数指针
#include <stdio.h>
// 简单的数学函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int main() {
// 声明函数指针
int (*operation)(int, int);
int choice, x = 10, y = 5;
printf("选择操作: 1-加法, 2-减法, 3-乘法: ");
scanf("%d", &choice);
switch(choice) {
case 1:
operation = add;
break;
case 2:
operation = subtract;
break;
case 3:
operation = multiply;
break;
default:
printf("无效选择\n");
return 1;
}
int result = operation(x, y);
printf("结果: %d\n", result);
return 0;
}
三(2). 动态内存分配
3.1 malloc, calloc, realloc, free
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("请输入数组大小: ");
scanf("%d", &n);
// 使用malloc分配内存
int *arr = (int*)malloc(n * sizeof(int));
if(arr == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 初始化数组
for(int i = 0; i < n; i++) {
arr[i] = i * 10;
}
printf("分配的数组:\n");
for(int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// 重新分配内存
int new_size = n + 3;
int *new_arr = (int*)realloc(arr, new_size * sizeof(int));
if(new_arr == NULL) {
printf("内存重新分配失败!\n");
free(arr);
return 1;
}
arr = new_arr;
// 初始化新分配的元素
for(int i = n; i < new_size; i++) {
arr[i] = i * 10;
}
printf("\n重新分配后的数组:\n");
for(int i = 0; i < new_size; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// 释放内存
free(arr);
return 0;
}
四、多级指针
多级指针是指向指针的指针,常用于处理二维数组或需要修改指针本身的场景:
#include <stdio.h>
int main() {
int a = 100;
int *p = &a; // 一级指针,指向int变量
int **pp = &p; // 二级指针,指向一级指针
int ***ppp = &pp; // 三级指针,指向二级指针
printf("a的值: %d\n", a);
printf("通过p访问: %d\n", *p);
printf("通过pp访问: %d\n", **pp);
printf("通过ppp访问: %d\n", ***ppp);
printf("\n地址关系:\n");
printf("&a = %p, p = %p\n", &a, p);
printf("&p = %p, pp = %p\n", &p, pp);
printf("&pp = %p, ppp = %p\n", &pp, ppp);
return 0;
}
五、指针与字符串
在 C 语言中,字符串是以\0结尾的字符数组,通常使用字符指针来操作字符串:
#include <stdio.h>
int main() {
char str[] = "Hello, World!"; // 字符数组
char *ptr = "Hello, Pointer!"; // 字符串常量,指针指向首字符
// 使用指针遍历字符串
printf("字符串内容: ");
while(*ptr != '\0') {
printf("%c", *ptr);
ptr++; // 指针移动到下一个字符
}
printf("\n");
return 0;
}
六、指针的常见错误与注意事项
1.** 野指针 **:未初始化的指针,指向未知内存区域
int *p; // 野指针,危险!
*p = 10; // 未定义行为,可能导致程序崩溃
2.** 空悬指针 **:指向已释放内存的指针
int *p = (int*)malloc(sizeof(int));
free(p); // 释放内存
*p = 10; // 错误!p现在是空悬指针
3.** 指针越界 **:访问超出数组范围的内存
int arr[5] = {1,2,3,4,5};
int *p = arr;
*(p + 10) = 100; // 越界访问,可能导致数据损坏
4.** 正确使用方法 **:
- 指针定义时初始化,不确定指向时设为
NULL - 释放内存后将指针设为
NULL - 访问指针前检查是否为
NULL - 避免指针越界访问
七. 指针与结构体
7.1 结构体指针
#include <stdio.h>
#include <string.h>
// 定义结构体
typedef struct {
char name[50];
int age;
float salary;
} Person;
// 函数接受结构体指针作为参数
void printPerson(const Person *p) {
printf("姓名: %s\n", p->name);
printf("年龄: %d\n", p->age);
printf("薪资: %.2f\n", p->salary);
}
void increaseSalary(Person *p, float percentage) {
p->salary += p->salary * percentage / 100;
}
int main() {
Person person1;
Person *ptr = &person1;
// 通过指针访问结构体成员
strcpy(ptr->name, "张三");
ptr->age = 30;
ptr->salary = 5000.0;
printf("原始信息:\n");
printPerson(ptr);
increaseSalary(ptr, 10);
printf("\n加薪后信息:\n");
printPerson(ptr);
return 0;
}
八. 常见指针错误及避免方法
8.1 常见错误示例
#include <stdio.h>
#include <stdlib.h>
void demonstrateErrors() {
// 错误1: 未初始化的指针
int *ptr1;
// *ptr1 = 10; // 错误:未初始化的指针
// 错误2: 空指针解引用
int *ptr2 = NULL;
// *ptr2 = 20; // 错误:空指针解引用
// 错误3: 野指针
int *ptr3;
{
int temp = 30;
ptr3 = &temp;
}
// *ptr3 = 40; // 错误:temp已超出作用域
// 错误4: 内存泄漏
int *leak = (int*)malloc(sizeof(int));
*leak = 50;
// 忘记free(leak);
// 正确做法
int *correct = (int*)malloc(sizeof(int));
if(correct != NULL) {
*correct = 60;
// 使用完毕后释放内存
free(correct);
correct = NULL; // 避免野指针
}
}
int main() {
demonstrateErrors();
return 0;
}
十. 综合示例
10.1 指针综合应用
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义学生结构体
typedef struct {
char name[50];
int score;
} Student;
// 比较函数用于排序
int compareStudents(const void *a, const void *b) {
const Student *studentA = (const Student*)a;
const Student *studentB = (const Student*)b;
return studentB->score - studentA->score; // 降序排列
}
// 动态创建学生数组
Student* createStudents(int count) {
return (Student*)malloc(count * sizeof(Student));
}
// 输入学生信息
void inputStudents(Student *students, int count) {
for(int i = 0; i < count; i++) {
printf("输入第%d个学生的姓名: ", i + 1);
scanf("%s", students[i].name);
printf("输入第%d个学生的分数: ", i + 1);
scanf("%d", &students[i].score);
}
}
// 打印学生信息
void printStudents(const Student *students, int count) {
printf("\n学生信息:\n");
printf("%-20s %-10s\n", "姓名", "分数");
printf("-------------------- ----------\n");
for(int i = 0; i < count; i++) {
printf("%-20s %-10d\n", students[i].name, students[i].score);
}
}
int main() {
int studentCount;
printf("请输入学生人数: ");
scanf("%d", &studentCount);
// 动态分配内存
Student *students = createStudents(studentCount);
if(students == NULL) {
printf("内存分配失败!\n");
return 1;
}
// 输入数据
inputStudents(students, studentCount);
// 排序
qsort(students, studentCount, sizeof(Student), compareStudents);
// 输出结果
printStudents(students, studentCount);
// 计算平均分
int total = 0;
for(int i = 0; i < studentCount; i++) {
total += students[i].score;
}
printf("\n平均分: %.2f\n", (float)total / studentCount);
// 释放内存
free(students);
return 0;
}
指针是C语言中最强大但也最容易出错的功能之一。通过本文的学习,你应该掌握:
-
指针的基本概念和操作
-
指针与数组、函数的关系
-
动态内存管理
-
多级指针的使用
-
指针与结构体的结合
-
常见指针错误及避免方法
记住以下最佳实践:
-
总是初始化指针
-
检查malloc/calloc/realloc的返回值
-
使用完毕后及时释放内存
-
避免野指针和空指针解引用
-
使用const修饰符保护数据
通过不断练习和实践,你将能够熟练运用指针,编写出高效、安全的C语言程序。对于编程语言的学习是没有止境的,对于编程语言的理解也是随着时间的推移发生变化的,上部分关于C语言的理解可能会有疏漏。希望大家积极的指出。
2288

被折叠的 条评论
为什么被折叠?



