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

article/2025/8/29 7:19:41

文章目录

  • 1.什么是指针?
    • 1.1.理解指针的两个要点:
    • 1.2.指针变量:
    • 1.3.内存是如何编址?
  • 2.指针和指针类型
    • 2.1指针的创建与初始化
    • 2.2.指针类型
  • 3.野指针
    • 3.1.什么视野指针?
    • 3.2.野指针成因
    • 3.3.规避野指针
  • 4.指针运算
    • 4.1.指针+-整数
    • 4.2.指针-指针
    • 4.3指针的关系运算
  • 5.指针与数组
  • 6.二级指针
    • 6.1.什么是二级指针
    • 6.2.二级指针的运算
  • 7.指针数组
  • 总结

1.什么是指针?

1.1.理解指针的两个要点:

1.指针是内存中最小单元的编号,也就是地址。
2.平时口语中的指针,通常指的是指针变量,指针变量是用来存放内存地址的变量。
总结:指针其实就是地址,口语中的指针通常指的是指针变量。

在这里插入图片描述
补充:一个内存单元占一个字节。

1.2.指针变量:

通过&取地址操作符取出变量在内存中的起始地址,将取出的地址存放在一个变量中,这个存放地址的变量就是指针变量。

下面通过代码演示指针变量的创建与使用

int main()
{int a = 10;int* pa  = &a;//创建指针变量并初始化//打印验证printf("%p\n", &a);printf("%p\n", pa);//使用指针变量需要用*解引用操作符*pa = 20;//此时*pa等价与aprintf("%d\n", *pa);printf("%d\n", a);return 0;
}

在这里插入图片描述
补充:

1、a在内存中占4个字节,&a取出的是整型变量a的第一个字节的地址(最小的地址)。
2.当需要通过地址访问来改变变量的值时,需要使用*解引用操作符对指针变量进行解引用操作。

1.3.内存是如何编址?

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0)。
在这里插入图片描述
每个地址标识1个字节,操作系统就会分配2的32次方个字节,也就是4GB的空间进行编址。同理可得64位机器,每个地址标识2个字节,操作系统就会分配2的64次方个字节,也就是8GB的空间进行编址。

32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

总结:

1、指针变量是用来存放地址的,地址是唯一标识一个内存单元的。
2、在32位平台下,地址的大小是4个字节,指针变量的大小是4个字节。在64位平台下,地址的大小是8个字节,指针变量的大小也是8个字节。

2.指针和指针类型

2.1指针的创建与初始化

我们都知道变量是分类型的,如整型、单精度浮点型、字符型等。那指针是否也分类型呢?答案是有的。
下面我通过代码举例

int main()
{int a = 10;//创建整型指针变量pa并初始化int* pa = &a;//将变量a的地址存放到指针变量pa中return 0;
}

上例中,我将变量a的地址存放到指针变量pa中。pa的类型是int*(整形指针)。由此我们可以得到
指针的定义方式是:

type(类型) + * +prt_name(指针变量名字)

其实:
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。

让我们来看看第二个例子

int main()
{int a = 10;char* pc = &a;//char*类型的指针能否得存放int类型变量的地址呢?return 0;
}

答案是:能够存放。因为一个指针变量在32位环境下都是占4个字节。这里不要门缝里看char*类型指针,把它给看遍了。这也是我们需要注意的一个小细节。

既然指针变量的大小在32位平台下都是4个字节,那指针变量的类型存在的意义是什么呢?

2.2.指针类型

这里我依旧是通过两段代码进行举例

//例一
#include <stdio.h>int main()
{int n = 10;char* pc = (char*)&n;int* pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc + 1);printf("%p\n", pi);printf("%p\n", pi + 1);return  0;
}

在这里插入图片描述
通过上面的例子我们可以发现,指针的类型决定了指针+1后访问的步长。int类型指针+1向后移动了4个字节,char类型指针+1向后移动了1个字节。同理可得short类型指针+1向后移动2个字节,float类型指针+1向后移动4个字节。

总结

指针变量的类型决定了指针的步长(向前或向后走一步的距离)。

//例二int main()
{int a = 0x11223344;//0x表示十六进制类型char* pc = (char*)&a;
//a是整型,需要对&a进行强制类型转换成char*后编译器才不会报错int* pi = &a;*pc = 0;   *pi = 0;   return 0;
}

先调试这段代码
在这里插入图片描述

让我们看看pc = 0的效果
在这里插入图片描述
接下来是
pi = 0;这句代码的效果
在这里插入图片描述
通过调试的内存窗口,可以发现当对char类型指针变量进行解引用操作后,访问的权限是1个字节,而对int类型指针变量进行解引用访问操作,访问的权限是4个字节。所以指针变量的类型其实是由意义的。

总结:

指针变量的类型决定了解引用操作后的指针的访问权限。int类型指针变量解引用操作访问权限是4个字节,char类型指针变量解引用操作访问权限是1个字节

3.野指针

3.1.什么视野指针?

野指针的概念

野指针指的是指向不可知的指针变量(随机的、不正确的、不可控的)。

3.2.野指针成因

1.指针变量未进行初始化

int main()
{int* pa;*pa = 20;return 0;
}

上例中,指针变量pa未进行初始化,所以pa是一个野指针,对pa进行仅引用操作是极其危险的,因为pa的指向是不可知的。

指针越界访问

int main()
{int arr[5]={1,2,3,4,5};int *p = arr;int i = 0;for(i = 0; i <= 5; i++){*(p++) = i;}return 0;
}

当运行这段代码后,编译器会进行报错
在这里插入图片描述

这里编译器告诉我们因为指针访问越界,数组arr被破坏了。所以当p超出数组的合理范围后,p就是一个野指针,对野指针进行解引用操作是很危险的。

3.3.规避野指针

1.创建指针变量同时初始化指针变量。
2. 小心指针越界。
3. 指针指向空间释放,及时置NULL。
4. 避免返回局部变量的地址。
5. 指针使用之前检查有效性。

4.指针运算

4.1.指针±整数

代码举例如下

#include<stdio.h>int main()
{int arr[5] = { 0 };int* pa = arr;int i = 0;//将数组内容赋值成1~5for (i = 0; i < 5; i++){*pa = i + 1;pa = pa + 1;//指针加整数}//打印数组内容for (i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述

4.2.指针-指针

指针-指针的前提条件是,两个指针变量指向同一块空间。

int main()
{int arr[10] = { 0 };int* pa = &arr[9];int* pb = &arr[0];//此时pa和pb都是指向数组arr的内容printf("%d\n",pa - pb);return 0;
}

在这里插入图片描述

指针-指针得到的绝对值是,两个指针之间相同类型的元素的个数。

下面我通过指针-指针的方式模拟实现库函数strlen

#include<assert.h>size_t MyStrlen(const char* str)
{assert(str != NULL);char* start = str;//记录初始地址while (*str++){;}//通过指针-指针返回字符串长度return str - start - 1;}int main()
{char str[] = "abcde";printf("%d\n",MyStrlen(str));return 0;
}

4.3指针的关系运算

#define N_VALUES 5
int main()
{float values[N_VALUES];float *vp;//								指针的关系运算for(vp = &values[N_VALUES]; vp > &values[0];){*--vp = 0;}return 0;
}

在这里插入图片描述
将上边的代码稍作修改

#define N_VALUES 5
int main()
{float values[N_VALUES];float *vp;//								指针的关系运算for(vp = &values[N_VALUES]; vp > &values[0];vp--){*vp = 0;}return 0;
}

在这里插入图片描述

代码修改后,看起来是更易读了,可是我们应该避免这样写代码。虽然上面的代码在市面上绝大部分编译器都可以正常运行,但是C语言标准中并不能保证它是对的。

C语言标准规定:
允许指向数组元素的指针与指向数组的最后一个元素后面那个内存空间的指针作比较,但是不允许与第一个元素前面那个内存空间的指针进行比较。

5.指针与数组

指针和数组是不同的对象。指针是一种用来存放地址的变量,大小是4/8个字节。数组是一组相同类型元素的集合,可以放多个元素,大小取决于元素个数和元素类型的。数组的数组名是数组首元素的地址,地址是可以存放在指针变量中的。可以通过指针来访问数组。

#include<stdio.h>
int main()
{int arr[10] = {0};printf("%p\n",arr);printf("%p\n",&arr);return 0;
}

在这里插入图片描述
由上例可知,数组名本质是首元素的地址(排除&数组名和sizeof(数组名)这两种情况)。既然数组名是数组首元素的地址,我们不妨将数组名存入一个指针变量中,再通过这个指针变量来遍历整个数组。

#include<stdio.h>int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,0 };int* p = arr; //指针存放数组首元素的地址int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;for (i = 0; i < sz; i++){printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);}return 0;
}

在这里插入图片描述
可以发现其实 p+i 是完全等价于 arr[ i ]的。接下来试试通过指针运算来便利整形数组并且打印数组

int main()
{int arr[10] = {0};int* pa = arr;int sz = sizeof(arr)/sizeof(arr[0]);int i = 0;
//改变数组内容1~10for(i = 0; i < sz; i++){*pa = i + 1;pa++;}pa = arr;//重新指定指针的位置以避免越界
//打印数组内容for(i = 0; i < sz; i++){printf("%d ",*pa);pa++;}return 0;
}
int main()
{int arr[10] = {0};int* pa = arr;int sz = sizeof(arr)/sizeof(arr[0]);int i = 0;
//改变数组内容1~10for(i = 0; i < sz; i++){*(pa+i) = i + 1;}//打印数组内容for(i = 0; i < sz; i++){printf("%d ",*(pa+i));}return 0;
}

补充:
[ ]下标访问操作符两边,arr 和 i 是下标访问操作符的两个操作数。[ ]下标访问操作符是支持交换律的,所以 arr[ i ] 等价于 i[arr]。

6.二级指针

6.1.什么是二级指针

指针变量是用来存放地址的,那指针变量的地址该存放到哪里呢?这就要引出二级指针这个概念了。二级指针是用来存放指针变量的地址的。

int main()
{int a = 10;int* pa = &a;int** ppa = &pa;return 0;
}

在这里插入图片描述

6.2.二级指针的运算

通过 * 解引用操作符对二级指针进行解引用操作。

int main()
{int a = 10;int* pa = &a;int** ppa = &pa;**ppa = 20;//*(*ppa) =*(pa) = a; //通过对* *ppa进行解引用操作得到pa,再通过对pa进行解引用操作得到a//**ppa等价于aprintf("%d\n", a);
}

在这里插入图片描述

7.指针数组

指针数组是什么呢?是数组?还是指针?答案是:指针数组是用来存放指针的数组。通过前面的学习,我们了解到的数组类型有字符数组、整型数组、单精度浮点型数组等。

int arr[] = {1,2,3,4,5};
char ch[] = {'a', 'b', 'c'};

在这里插入图片描述

int* arrp[] = {0x0012ff40, 0x0012ff41, 0x0012ff42};

在这里插入图片描述

下面我通过一个指针数组加三个一维数组来模拟实现二维数组

#include<stdio.h>int main()
{int arr1[3] = { 1,2,3 };int arr2[3] = { 4,5,6 };int arr3[3] = { 7,8,9 };int* arrp[3] = { arr1,arr2,arr3 };int i = 0;int j = 0;for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){printf("%d ",arrp[i][j]);//arr[i]相当于是arr[列号]。通过指针运算,就可以遍历整个数组。}printf("\n");}return 0;
}

在这里插入图片描述
在这里插入图片描述

通过了访问指针数组arrp中存放的三个整形数组的首元素地址,然后再通过指针运算,就可以模拟实现一个二维数组了。

总结

总有人说指针多难多难,在我看来只要搞清楚指针的概念以及指针的用法,理解指针也不是特别难的。这只需要我们多去思考和总结。学习没有捷径,只有你真的认真学了才能够真正的掌握知识。最后,也希望看完本篇文章的你有所收获,有什么问题也欢迎在评论区进行讨论。


http://chatgpt.dhexx.cn/article/9goI6jFc.shtml

相关文章

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;用真实标签标注新数据点的策略。主动学习的过程也被称为优化实验设计…

机器学习/深度学习几种典型学习范式|主动学习

机器学习/深度学习几种典型学习范式|主动学习 主动学习&#xff08;Active Learning,AL&#xff09;&#xff1a;Introduction主动学习的例子 应用场景成员查询合成流式选择抽样基于池的主动学习 查询策略框架不确定性抽样Uncertainty Sampling基于委员会的查询Query-By-Commit…

机器学习中的主动学习(Active Learning)

最近在做主动学习相关的东西&#xff0c;随着深入了解和学习对于某些东西有一些模糊&#xff0c;先将所见所感整理如下&#xff0c;如有不正确之处希望大佬能够指正&#xff1a; 1.主动学习 1.1关键问题 对于监督学习模型&#xff0c;足够多的已标注样例是获得高精度分类器的…

主动学习数据标注

active learning与passive(supervised) learning最大的不同是其不需要大量的专家标注样本训练模型。 主动学习是利用少量标注样本&#xff0c;然后由模型&#xff08;Learner&#xff09;主动选择hard sample返回给用户或专家&#xff08;Oracle&#xff09;打标签&#xff0c;…

浅谈主动学习(Active Learning)

1背景概述 在机器学习领域有很多学习模式&#xff0c;比方说监督学习、半监督学习、强化学习、无监督学习等。平时大家接触比较多的一般都是监督学习&#xff0c;在监督学习里面&#xff0c;比方说要做个人和鱼的图像分类模型&#xff0c;假设有200张图片&#xff0c;那就需要把…

深度主动学习综述(Deep Active Learning)

原文 Abstract 主动学习试图通过标记最少量的样本使得模型的性能收益最大化。而深度学习则对数据比较贪婪&#xff0c;需要大量的数据供给来优化海量的参数&#xff0c;从而使得模型学会如何提取高质量的特征。近年来&#xff0c;由于互联网技术的快速发展&#xff0c;使得我…