当前位置: 首页 > ai >正文

C语言教程(十四):C 语言指针详解

一、指针的基本概念

        指针是一个变量,其值为另一个变量的内存地址。简单来说,指针指向了内存中的某个位置,通过指针可以间接访问该位置存储的数据。指针的使用可以让程序更加高效地处理数据,特别是在处理数组、动态内存分配等方面。

二、指针的定义和初始化

  2.1 指针的定义

        定义指针变量的语法格式为:数据类型 *指针变量名;

        数据类型:指定指针所指向的变量的数据类型,如 `int`、`float`、`char` 等。

        指针变量名:是用户自定义的标识符,用于标识这个指针变量。

         `*` :是指针声明符,用于表明这是一个指针变量。

        例如,定义一个指向整数的指针:int *ptr;

  2.2 指针的初始化

        指针变量在使用之前最好进行初始化,否则它可能包含一个随机的内存地址,访问该地址可能会导致未定义行为。指针可以初始化为 `NULL` 或者某个变量的地址。

        初始化为 `NULL`:`NULL` 是一个特殊的指针值,表示指针不指向任何有效的内存地址。int *ptr = NULL;

        初始化为某个变量的地址:使用取地址运算符 `&` 来获取变量的地址,并将其赋值给指针。

#include <stdio.h>int main() {int num = 10;int *ptr = &num;  // ptr 指向 num 的地址printf("num 的地址: %p\n", &num);printf("ptr 存储的地址: %p\n", ptr);return 0;
}

三、指针的解引用

        解引用是指通过指针访问其所指向的内存位置的数据。使用解引用运算符 `*` 来实现。

#include <stdio.h>int main() {int num = 10;int *ptr = &num;printf("num 的值: %d\n", num);printf("通过指针访问 num 的值: %d\n", *ptr);*ptr = 20;  // 通过指针修改 num 的值printf("修改后 num 的值: %d\n", num);return 0;
}

        在上述代码中,`*ptr` 表示访问 `ptr` 所指向的内存位置的数据。可以通过 `*ptr` 来读取或修改该位置的数据。

四、指针与数组

  4.1 数组名与指针的关系

        在C语言中,数组名在大多数情况下会被隐式转换为指向数组第一个元素的指针。例如:

#include <stdio.h>int main() {int numbers[5] = {1, 2, 3, 4, 5};int *ptr = numbers;  // numbers 被转换为指向第一个元素的指针printf("数组的第一个元素: %d\n", *ptr);return 0;
}

  4.2 通过指针访问数组元素

        可以使用指针的算术运算来访问数组元素。指针的算术运算包括指针的加法和减法。

#include <stdio.h>int main() {int numbers[5] = {1, 2, 3, 4, 5};int *ptr = numbers;for (int i = 0; i < 5; i++) {printf("%d ", *(ptr + i));}printf("\n");return 0;
}

在上述代码中,`ptr + i` 表示指向数组中第 `i` 个元素的指针,`*(ptr + i)` 表示访问该元素的值。

五、指针与函数

  5.1 指针作为函数参数

        将指针作为函数参数可以实现通过函数修改实参的值。这是因为传递的是实参的地址,函数可以直接访问和修改该地址所存储的数据。

#include <stdio.h>void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int main() {int x = 10, y = 20;printf("交换前: x = %d, y = %d\n", x, y);swap(&x, &y);printf("交换后: x = %d, y = %d\n", x, y);return 0;
}

        在上述代码中,`swap` 函数接受两个指针作为参数,通过指针交换了两个变量的值。

  5.2 指针作为函数返回值

        函数可以返回一个指针,但需要注意返回的指针必须指向有效的内存地址。如果返回的是局部变量的地址,可能会导致未定义行为,因为局部变量在函数结束后会被销毁。

#include <stdio.h>int *getMax(int *arr, int size) {int *max = arr;for (int i = 1; i < size; i++) {if (*(arr + i) > *max) {max = arr + i;}}return max;
}int main() {int numbers[5] = {1, 3, 2, 5, 4};int *maxPtr = getMax(numbers, 5);printf("数组中的最大值: %d\n", *maxPtr);return 0;
}

        在上述代码中,`getMax` 函数返回一个指向数组中最大值的指针。

六、动态内存分配

  6.1 `malloc` 函数

        `malloc` 函数用于在堆内存中分配指定大小的内存块。其原型为:void *malloc(size_t size);

 `size` 是要分配的内存块的大小,单位是字节。

`malloc` 函数返回一个指向分配的内存块的指针,如果分配失败则返回 `NULL`

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = (int *)malloc(5 * sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}for (int i = 0; i < 5; i++) {ptr[i] = i + 1;}for (int i = 0; i < 5; i++) {printf("%d ", ptr[i]);}printf("\n");free(ptr);  // 释放分配的内存return 0;
}

        在上述代码中,使用 `malloc` 函数分配了一个可以存储 5 个整数的内存块,并对其进行初始化和访问,最后使用 `free` 函数释放了分配的内存。

  6.2 `calloc` 函数

        `calloc` 函数也用于在堆内存中分配内存块,与 `malloc` 不同的是,`calloc` 会将分配的内存块初始化为 0。其原型为:void *calloc(size_t num, size_t size);

`num` 是要分配的元素个数。

`size` 是每个元素的大小,单位是字节。

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = (int *)malloc(3 * sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return 1;}for (int i = 0; i < 3; i++) {ptr[i] = i + 1;}ptr = (int *)realloc(ptr, 5 * sizeof(int));if (ptr == NULL) {printf("内存重新分配失败\n");return 1;}for (int i = 3; i < 5; i++) {ptr[i] = i + 1;}for (int i = 0; i < 5; i++) {printf("%d ", ptr[i]);}printf("\n");free(ptr);return 0;
}

七、指针的注意事项

        空指针解引用:对 `NULL` 指针进行解引用会导致未定义行为,可能会使程序崩溃。在使用指针之前,最好检查其是否为 `NULL`。

        野指针:野指针是指指向无效内存地址的指针,如指向已经释放的内存或未初始化的指针。使用野指针会导致未定义行为,要避免产生野指针。

        内存泄漏:在使用动态内存分配函数(如 `malloc`、`calloc`、`realloc`)分配内存后,要记得使用 `free` 函数释放内存,否则会导致内存泄漏,使系统的可用内存逐渐减少。

http://www.xdnf.cn/news/1677.html

相关文章:

  • 倚光科技:微透镜阵列低成本加工新范式
  • 【数据可视化-27】全球网络安全威胁数据可视化分析(2015-2024)
  • Linux基础命令
  • 容器修仙传 我的灵根是Pod 第10章 心魔大劫(RBAC与SecurityContext)
  • 免费版还是专业版?Dynadot 域名邮箱服务选择指南
  • 深度学习物理信息神经网络PINN+大模型辅助编程​
  • 如何在 Postman 中,自动获取 Token 并将其赋值到环境变量
  • 整平机:精密制造的“隐形守护者”
  • PCB封装主要组成元素
  • 10天学会嵌入式技术之51单片机-day-7
  • docker
  • 通付盾入选苏州市网络和数据安全免费体验目录,引领企业安全能力跃升
  • 智能小助手部署 Win10 + ollama的Deepseek + CentOS+ maxKB
  • C语言结构体和union内存对齐
  • 3.4/Q1,GBD数据库最新文章解读
  • 新增优惠券
  • 如何计算光伏电站的收益率
  • PyQt6基础_QProgressDialog
  • Discuz!+DeepSeek赋能虎跃办公:传统网址导航的智能进化之路
  • Python实例题:使用Pvthon3编写系列实用脚本
  • 【Java】jdk8安装——英文版
  • Java快速上手之实验4(接口回调)
  • 第13章:MCP服务端项目开发实战:向量检索
  • 如何在Spring Boot中配置自定义端口运行应用程序
  • 2025上海车展:光峰科技全球首发“灵境”智能车载光学系统
  • linux 中断子系统 层级中断编程
  • 【PVCodeNet】《Palm Vein Recognition Network Combining Transformer and CNN》
  • Python中random库的应用
  • openwrt作旁路由时的几个常见问题 openwrt作为旁路由配置zerotier 图文讲解
  • 【项目管理】进度网络图 笔记