C语言:详解指针

article/2025/8/29 7:10:03

本人录制技术视频地址:https://edu.csdn.net/lecturer/1899 欢迎观看。

指针应该算得上是c语言的精华,但也是难点。很多教程或者博客都有对其详细的讲解与分析。我这一节的内容,也是讲解指针,但我会尽量使用图解的方式,使大家很容易理解及掌握。

一、基本使用

先来看看下面的代码:

int i = 3; 
int *p;    
p = &i;printf("i 存放的内容的值: %d, i 自己所在的地址: %p\n", i, &i);printf("p 存放的地址的值: %p; p 自己所在的地址: %p; p 存放的地址所指所存放内容的值: %d", p, &p, *p);return 0;
变量i是int类型,所以存放的是int数据。

变量p是int *类型,所以存放的是指向int类型的地址。

这样说,似乎还是没有表达清楚,我使用下面的一张图进行说明:

1. int i = 3; 这句话执行完毕之后,变量i中的内容是3,假设 变量i本身的内存地址为 "0x8000"。

2. int *p; 只是为指针变量p申请了一块内存地址,假设它的内存地址为 "0x7000",此时变量p中存放的内容为nil。

3. p = &i; 表示将变量i的地址赋值给指针p所存放的内容,至此,就呈现出了上图的情形。

所以程序中的那两句打印结果就很明显了。

i 存放的内容的值: 3, i 自己所在的地址: 0x8000

p 存放的地址的值: 0x8000; p 自己所在的地址: 0x7000; p 存放的地址所指所存放内容的值: 3


二、 交换两个整数的值

示例代码如下:

void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}int main(int argc, const char * argv[]) {int a = 3, b = 5;swap(&a ,&b);printf("a: %d; b: %d", a ,b);return 0;
}
当程序执行完 int a = 3, b = 5, 之后,假设a,b他们自己在内存中的地址分别为0x8000, 0x9000。如下图:

然后调用swap(&a, &b); 注意,这里传递的是变量a, b 本身的地址。而函数swap的接受形参int *a, int *b均是指针变量,用来接受传递过来的变量a, b的地址,即传递过来的变量a, b 和 函数 swap中的形式参数a, b 完全是两回事;为了以示区别,我将形参中的a, b 表达为 swap_a, swap_b。所以将变量a, b 的地址赋值给swap_a 和 swap_b 之后,内存中的地址分布大致如下(假设swap_a本身的地址为0x6000, swap_b本身的地址为0x7000)。


int temp = *a,  定义一个临时变量temp,用来存储指针swap_a 所指向变量a所存储的值,所以临时变量temp此时存储的值为3,如下图:



*a = *b, 表示将将指针swap_b所指向变量b的内容赋值给指针swap_a所指向变量a的内容,所以执行完毕之后,内存图大致如下:


最后一句代码 *b = temp, 就是将temp所存储的内容赋值给指针swap_b所指向的变量b存储的内容,所以执行完这句话之后,内存图大致如下:



当程序执行到swap函数 右边 “}” 结束后,此时,表示函数swap已经结束,而变量temp是局部变量,所以此时它也会被立刻销毁,所以最终的内存结构图大致而下:



通过上面两个例子的图解,相信大家对指针的概念有了初步的了解,下面的内容,我就直接讲解其内容不做画图处理了,如果自己感兴趣的话,也可以画图尝试尝试。

三、 字符数组与字符串常量

1. 字符数组

char str[] = "good";
while (*str != '\0') {putchar(*str++);
}
注意:字符数组名是一个常量指针,不能进行类似于 str = str + 1 或者 str++ 等操作,所以上面的代码是错误的。而且str存放的是数组第一个元素的地址。如果想了解更所的关于地址方面的知识,可以参考我前面讲解的内容《C语言:内存地址分析 & sizeof和strlen用法总结》。

2. 字符串常量

char *str2 = "good";
while (*str2 != '\0') {putchar(*str2++);
}
注意: "good"本身就是一个常量内容,它存放在只读存储区,并且有自己的地址,而变量str2中存放的内容就是常量 "good"所在的地址。所以上述str2++操作完全是正确的。

比如下面的Demo

const char *str = "hello";
printf("address: %p\t%s\t%c\n", &str,str, *str);
str++;
printf("address: %p\t%s\n", &str,str);
打印的结果为:

address: 0x7fff5fbff6b8helloh

address: 0x7fff5fbff6b8ello

所以str的值就是指针所在位置及后面字符的值,而*str取得的就是指针所在位置字符的值。

四、函数指针

示例代码:

int add(int num1, int num2) {return num1 + num2;
}int main(int argc, const char * argv[]) {printf("result:%d", add(1, 2));printf("\n%p", add); int (*p)(int,int) = add; printf("\n%d", p(3, 4));return 0;
}
函数指针,顾名思义,就是一个指向函数的指针。函数指针的目的就是为了实现方法的回调。而回调不是本节讲解的重点,我就不做具体说明了。上面的Demo中定义了一个函数add,它是一个有两个参数,返回值为int的函数。printf("\n%p", add) 打印出来的结果就是函数add入口点的地址。

int (*p)(int,int) = add;
就是自定义一个函数指针p指向具体的函数add。需要注意的一点是:add是常量地址,不能被修改;而指针p是变量地址,可以被修改。

五、 泛型指针

泛型指针就是在定义的时候还不知道指针的具体类型,直到调用的时候才确定类型,并且进行相应的强制类型转换工作,完成任务。泛型指针的形式就是void *。下面用泛型指针实现一个冒泡排序,代码如下:

void init_array(int *a, int n) {srand(time(NULL));for (int i = 0; i < n; i++) {a[i] = rand() % 100;}
}int cmp_array(void *a, void *b) {int num1 = *((int *)a);int num2 = *((int *)b);return num1 > num2;
}void swap_array(void *a, void *b) {int temp = *((int *)a);*((int *)a) = *((int *)b);*((int *)b) = temp;
}// 这里的void * 就是 泛型指针,需要进行指针转换,达到自己想要的结果
void sort_array(int *a, int n, int (*cmp)(void *, void *), void (*swap)(void *, void *)){int i,j;// 冒泡排序for (i = 0; i < n; i++) {for (j = 0; j < n - i - 1; j++) {if (cmp(&a[j], &a[j+1])) {swap(&a[j], &a[j+1]);}}}
}int main(int argc, const char * argv[]) {int a[10];init_array(a, 10);sort_array(a, 10, cmp_array, swap_array);return 0;
}

在c语言中,书写起来看起来是有点复杂了,其实现在的高级语言没有必要这么麻烦了,用泛型就可以解决问题了,但是这些高级语言的泛型底层还是依赖于c语言的泛型指针。

六、更为复杂的指针

注:以下的测试代码均为在64位系统处理所得。

1) 指针数组

char *p[10]; 
printf("sizeof(p):%lu \t sizeof(*p):%lu\n",sizeof(p), sizeof(*p));

1. p存放的是一个指针数组的首地址,而指针数组中每一个元素又是指向char *类型元素的地址。

2. sizeof(p)计算的是数组字节大小,输出80,而sizeof(*p)是计算首元素中存放内容的大小,而存放的内容是地址,所以结果为8。

3. p+1 就是 p[1], 每一个地址中存放的就是char *类型元素的地址,即8个字节,所以p+1的地址是在首元素地址的基础上面加8。

最终打印结果:

sizeof(p):80 sizeof(*p):8


2) 指针的指针

char **pl; 
printf("sizeof(pl):%lu \t sizeof(*pl):%lu, \t **pl:%lu \t pl:%p \t pl+1:%p\n",sizeof(pl), sizeof(*pl), sizeof(**p), p, p+1);

1. pl是指针的指针,所以pl指向的是一个指针的地址,所以sizeof(pl)结果为8, 打印出来的pl是一个地址,由于pl指向的是一个“指针的地址”,而“指针的地址”存放的是char类型变量的地址,所以占8个字节,所以pl+1 比pl大8。

2. *pl指向char类型变量的,所以它存放的是char类型变量的地址,所以sizeof(*pl)结果为8。

3. **pl,获取char类型变量的值,由于是char类型,所以sizeof(**p)结果为1。

最终打印结果:

sizeof(pl):8 sizeof(*pl):8, **pl:1 pl:0x7fff5fbff6b0 pl+1:0x7fff5fbff6b8


3) 函数指针

char (*pt)(void);

1. pt存放的是函数的首地址,所以sizeof(pt)结果为8

2. *pt 获取的是函数内部的代码块,所以sizeof(*pt) 和 pt + 1 均没有实际意义


4) 数组指针

char (*pk)[10]; // 数组指针
printf("sizeof(pk):%lu \t sizeof(*pk):%lu, pk:%p \t *pk:%p \t pk + 1:%p \t *pk + 1: %p\n",sizeof(pk), sizeof(*pk),pk,*pk, pk + 1, *pk + 1);

1. char (*pk)[10];相当于二维数组 char a[][10]

2. pk相当于指向二维数组的指针,存放的是地址,所以sizeof(pk)结果为8;pk存放的是整个二维数组的首地址; pk + 1 就是相当于指针移动了一个一维数组的距离,所以地址在pk的基础上面加了10.

3. *pk就是取得二维数组第一行的值,它是一个含有10个char元素的一维数组,所以sizeof(*pk)结果为10;*pk存放的是二维数组中第一维数组的首地址; *pk + 1就是在一维数组的基础上,移动了一个char的距离,所以地址在*pk基础上面加1。

4. 所以 pk和*pk打印的地址结果是一样的。

最终打印结果:

sizeof(pk):8 sizeof(*pk):10, pk:0x7fff5fc27190 *pk:0x7fff5fc27190 pk + 1:0x7fff5fc2719a *pk + 1: 0x7fff5fc27191


http://chatgpt.dhexx.cn/article/PKn448af.shtml

相关文章

【C语言】指针(详解)

文章目录 前言1.字符指针2. 指针数组3. 数组指针3.1 数组指针的定义3.2 &数组名VS数组名3.3 数组指针的使用 4. 数组参数、指针参数4.1 一维数组传参4.2 二维数组传参4.3 一级指针传参4.4 二级指针传参 5. 函数指针6. 函数指针数组7. 指向函数指针数组的指针8. 回调函数8.1…

【c语言】 指针详解 【图文+代码】

指针 前言一、指针是什么&#xff1f;1.1 认识指针1.2 变量1.3 变量放在哪&#xff1f;1.4 指针的本质1.5 解引用 二、指针类型2.1指针有类型吗&#xff1f;2.2指针类型的意义是什么&#xff1f; 三. 野指针3.1什么是野指针3.2野指针成因3.2.1指针未初始化3.2.2 指针越界访问3.…

【初阶】C语言指针详解——指针必备的7大知识点

文章目录 前言一、指针是什么&#x1f351;1、浅谈指针&#x1f351;2、内存&#x1f351;3、指针变量 二、指针和指针类型&#x1f351;1、指针类型&#x1f351;2、指针-整数&#x1f351;3、指针的解引用 三、野指针&#x1f351;1、野指针成因&#x1f333;(1) 指针未初始化…

详解C语言指针

文章目录 1.字符指针2.指针数组3.数组指针3.1 数组指针的定义3.2 &数组名和数组名的区别3.3 数组指针的使用 4.数组参数&#xff0c;指针参数4.1 一维数组传参4.2 二维数组传参4.3 一级指针传参4.4 二级指针传参 5.函数指针6.函数指针数组7.指向函数指针数组的指针8.回调函…

C语言——指针(入门详解)

文章目录 1.什么是指针&#xff1f;1.1.理解指针的两个要点&#xff1a;1.2.指针变量&#xff1a;1.3.内存是如何编址&#xff1f; 2.指针和指针类型2.1指针的创建与初始化2.2.指针类型 3.野指针3.1.什么视野指针&#xff1f;3.2.野指针成因3.3.规避野指针 4.指针运算4.1.指针-…

c语言指针用法详解,通俗易懂超详细!

文章转自&#xff1a;无际单片机 大家好&#xff0c;我是无际。 今天给大家来讲解一下指针。 我会由浅到深&#xff0c;最后结合实际应用讲解&#xff0c;让大家学会指针的同时&#xff0c;知道大佬们都用指针来干嘛&#xff01; 长文预警&#xff01;全文大约5200多字&#xf…

C语言指针详解(超级详细)

C语言指针精解 前言 这不是我第一次写关于C指针的文章了&#xff0c;只是因为指针对于C来说太重要&#xff0c;而且随着自己编程经历越多&#xff0c;对指针的理解越多&#xff0c;因此有了本文。然而&#xff0c;想要全面理解指针&#xff0c;除了要对C语言有熟练的掌握外&…

C语言中的指针详解

1. 什么是指针 C语言中指针是一种数据类型,指针是存放数据的内存单元地址。 计算机系统的内存拥有大量的存储单元,每个存储单元的大小为1字节,为了便于管理,必须为每个存储单元编号,该编号就是存储单元的“地址”,每个存储单元拥有一个唯一的地址。 指针变量除了可以存…

C语言——指针详解(必收藏)

目录 1.什么是指针&#xff1f; 1.1概念 1.2指针的大小 ​ 1.3指针类型的作用 2.野指针 2.1野指针产生的原因 2.2 如何规避野指针 3.指针运算 3.1指针-整数 3.2指针-指针 3.3 指针的关系运算 4. 二级指针 5. 数组名 *6.指针数组和数组指针 6.1指针数组 *6.…

C语言指针详解

一、什么是指针 C语言里&#xff0c;变量存放在内存中&#xff0c;而内存其实就是一组有序字节组成的数组&#xff0c;每个字节有唯一的内存地址。CPU 通过内存寻址对存储在内存中的某个指定数据对象的地址进行定位。这里&#xff0c;数据对象是指存储在内存中的一个指定数据类…

C语言之指针详解

文章目录 1 指针1.1 简介1.2 什么是指针1.2.1 定义1.2.2 指针表示1.2.3 为什么*p&a不正确 1.3 使用指针1.3.1 简单使用1.3.2 NULL 指针1.3.3 指针算术运算1.3.3.1 定义1.3.3.2 遍历数组&#xff1a;递增一个指针1.3.3.3 遍历数组&#xff1a;递减一个指针1.3.3.4 指针的比较…

c语言—指针详解

文章目录 一、指针是什么二、指针和指针变量1.左值与右值2.两者的区别 三、指针和指针类型1.定义指针2.大小端3.指针的解引用 四、野指针野指针成因如何规避野指针 五、指针运算1.指针- 整数2.指针-指针3.指针的关系运算 六、指针和数组七、二级指针八、指针数组和数组指针1.指…

机器学习主动学习和半监督学习

一、主动学习&#xff08;active learning&#xff09; 学习器能够主动选择包含信息量大的未标注的样例并将其交由专家进行标注&#xff0c;然后置入训练集进行训练&#xff0c;从而在训练集较小的情况下获得较高的分类正确率&#xff0c;这样可以有效的降低构建高性能分类器的…

active learning主动学习

active learning 是半监督式的机器学习的一种&#xff0c;这种机器学习算法能够交互式地查询用户或者信息源&#xff0c;从而对于一个新的数据样例得到可人的输出。在统计学文献中&#xff0c;它有时也被称为最佳实验设计。 在这样的一种情形下&#xff1a;无标签的数据量很大…

深度主动学习综述2020

A Survey of Deep Active Learning 中文版仅作参考&#xff0c;以正式的pdf版为主。 https://arxiv.org/pdf/2009.00236.pdf 西北大学等最新《深度主动学习》全面综述论文&#xff0c;30页pdf abstract 主动学习试图通过标记最少量的样本使得模型的性能收益最大化。而深度学习…

每日一学-- 主动学习(active learning)

1. 在机器学习中&#xff0c;有监督学习、半监督学习、无监督学习。 在使用监督学习时&#xff0c;模型在标注的数据中学习信息&#xff0c;而存在的问题就是有大量的数据需要标注&#xff0c;非常费时费力。so主动学习为我们提供了方法&#xff0c;通过一定的算法找出最有用的…

一张图展示被动学习与主动学习的效率差距

一张图展示被动学习与主动学习的效率差距 起因一张图 起因 由于工作原因。最近的一段时间&#xff0c;又回到了大量的学习实践当中。之前的主观感受是&#xff0c;通过文字语音视频结合的多渠道信息获取方式学到的东西&#xff0c;记忆会比自己只是看文字&#xff0c;读文字&a…

深度学习 主动学习(Active Learning)概述、策略和不确定性度量

文章目录 主动学习概念策略基于数据流的主动学习方法基于数据池的主动学习方法基于查询的主动学习方法 不确定性度量 参考 主动学习 概念 主动学习是指对需要标记的数据进行优先排序的过程&#xff0c;这样可以确定哪些数据对训练监督模型产生最大的影响。主动学习不是一次为…

主动学习-综述

主动学习是机器学习&#xff08;更普遍的说是人工智能&#xff09;的一个子领域&#xff0c;在统计学领域也叫查询学习、最优实验设计”(Active learning (sometimes called “query learning” or “optimal experimental design” in the statistics literature) is a subfie…

深度学习--主动学习

主动学习简介 主动学习是指对需要标记的数据进行优先排序的过程&#xff0c;这样可以确定哪些数据对训练监督模型产生最大的影响。主动学习是一种学习算法可以交互式查询用户(teacher 或 oracle)&#xff0c;用真实标签标注新数据点的策略。主动学习的过程也被称为优化实验设计…