老生常谈C语言中指针的使用

 更新时间:2022年02月21日 09:23:32   作者:纸墨青鸢  
这篇文章主要为大家详细介绍了C语言中指针的使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

前提

指针,是C语言中的一个重要概念及其特点,也是掌握C语言比较困难的部分。指针也就是内存地址,指针变量是用来存放内存地址的变量,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作

指针描述了数据在内存中的位置,标示了一个占据存储空间的实体,在这一段空间起始位置的相对距离值。在 C/C++语言中,指针一般被认为是指针变量,指针变量的内容存储的是其指向的对象的首地址,指向的对象可以是变量(指针变量也是变量),数组,函数等占据存储空间的实体。

一.指针基础

1.1 变量指针

//首先我们声明一个变量
int a = 10;	//声明一个指针并指向该变量的地址	
int* b = &a;	printf("%d\n", *b);

运行结果:10

1.2 数据指针

//首先我们声明一个数组变量
int c[10] = { 0,1,2,3,4,5,6,7,8,9 };		
printf("c的地址:%p\n", c);	
printf("c[0]的地址:%p\n", &c[0]);

运行结果:两个一样

Why? 数组本质为内存空间上连续的空间,而作为数组的地址,其实为首元素的地址,而对于数组,数组名称其实就是个指针(重点)然后我们创建数组指针

int* d=c
printf("d[9]的值为:%d\n",*(d+9));
//另一种写法printf("d[9]的值为:%d\n",d[9]);

运行结果:9

1.3 指针的本质

通过1.0和1.1的指示我们突然发现,整数的指针和整数数组的指针居然是同个类型!

事实上确实是一个东西,指针为指向变量地址的类型,所以对于指针来说是不需要类型的,但是为了程序规范性,增加了类型说法

下面使用无类型指针输出a的值(10)

void* p = &a;
printf("void指针的值:%d\n", *(int*)p);
//对比b指针变量
printf("这是b的值:%p\n", b);
printf("这是p的值:%p\n", p);

在这里插入图片描述

上述结果可以完全表明,指针变量储存的是地址,而且是单一变量的地址

接下来可以解释数组和数为啥指针就是同一个类型了

回到1.2所述 单个数占了1个空间(这里不提占用内存),而数组占了n个空间,而指针指向的都是首地址,而对于单个数来说

首地址就是数所在的地址,而对于数组来说,首地址就是头元素所在的地址

1.2解释了数组地址就是数组名本身,所以数组指针就是数组本身了,所以有下面这种写法

printf("直接使用数组名获取索引3的元素:%d\n", c[3]);
printf("使用指向c的指针获取索引3的元素:%d\n", d[3]);

在这里插入图片描述

但是数组和指针数组还是有一定的区别

1.4 指针数组

/如果我们把指针看作一个变量,那么类比整数类型,我们一定可以声明一个指向指针的指针

int g = 50;
int* h = &g;
int** i = &h;
//输出一下
printf("h的值:%p\n", h);
printf("i的值:%p\n", *i);
//实验证明这是可行的,那我们尝试做一个数组
int j[5] = { 9,8,7,6,5 };
int* k[5] = { &j[0],&j[1],&j[2],&j[3],&j[4] };
int** l = k;
printf("k[1]的值:%p,对应的整数的值:%d\n", k[1], *k[1]);
printf("l获取k1的值:%p,对应的整数的值:%d\n", *(l + 1), **(l + 1));

在这里插入图片描述

这就是指针数组,把指针当作一个变量看,指针也可以做数组

1.5 指针的移动

对于数组指针获取元素,上面展示了可以通过"变量名[索引]"的方式获取,当然也可以通过移动指针位置来获取

int* m = (int*)malloc(10 * sizeof(int));
for (int i = 0; i < 10; ++i)
	*(m++) = i;

指针移动到了分配内存的最后一块

“变量名[索引]“的方式表明了是以当前指针为数组头,偏移索引个单位为方法获取元素的方式,索引获取第8个元素

错误写法 printf(”%d”,m[8]);

正确写法

printf("%d\n", *(m - 2));

在这里插入图片描述

所以释放的时候要释放头指针,所以要回到头指针

for (int i = 0; i < 10; ++i)
	(--m);
free(m);

所以实际操作的情况下尽量不要去移动指针

int* n = (int*)malloc(10 * sizeof(int));
for (int i = 0; i < 10; ++i)
	*(n + i) = i;
printf("%d\n", *(n + 8));
free(n);

在这里插入图片描述

1.5 Scanf函数的解释

scanf函数为获取变量地址函数

首先我们看一下scanf函数的定义

scanf函数有一个格式符参数和一个可变参数

可变参数类型为指针!!!!

char o;

第一种直接写法

scanf("%c", &o);

printf(“输出的字符:%c\n”, o);

同理

用指针获取

char* q = &o;
scanf("%c", q);
printf("通过指针获取输出的字符:%c\n", *q);

在这里插入图片描述

上面我们说了,指针可以代表数组,而c语言对字符串的定义就是字符数组

char r[] = { "Hello" };
scanf("%s", r);
printf("字符串的值:%s\n", r);

这样我们就可以解释为什么对于获取字符串的时候,不用写&的原因了,因为字符数组本来就是指针!!

二.指针的进阶玩法

2.1 二维指针

由名字可得,二维指针就是指针的指针,指针可以代表数组,自然二维指针就可以代表二维数组

int u[4][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6},{4,5,6,7} };
int** v = u;

二维数组在内存空间上也是线性排列的,就如下顺序

1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7

所以二维数组获取元素也可以靠指针移动

printf("u[1][2]的值:%d\n", *(v + 6));

也可以动态内存分配实现

int** w = (int**)malloc(4 * sizeof(int)); //分配行内存
for (int i = 0; i < 4; ++i)
	w[i] = (int*)malloc(4 * sizeof(int)); //分配列内存
for (int i = 0; i < 4; ++i)
	for (int j = 0; j < 4; ++j)
		w[i][j] = (j + 1) + i;
printf("w[1][2]的值:%d\n", w[1][2]);
free(w); //记得释放

2.2 结构体指针

先声明一个结构体

struct people
{
	char* name;
	int age;
}SPeople;
//方便表示替换掉标识符
typedef struct people People;
People dr;
dr.name = "aaa";
dr.age = 18;
printf("dr的名字是:%s,年龄是:%d\n", wusihao.name, wusihao.age);
People* lp = &wusihao; //注意属性获取的符号"->"
lp->name = "sss";
lp->age = 19;
printf("lp的名字是:%s,年龄是:%d\n", lp->name, lp->age);

当然也可以动态内存分配使用

People* lps = (People*)malloc(1 * sizeof(People));
lps->name = "xxx";
lps->age = 20;
printf("lps的名字是:%s,年龄是:%d\n", lps->name, lps->age);

结语

指针定义后,在不用时最好指向NULL,比如

int* s = &a;
printf("%p\n", s);
s = NULL;

因为就算释放掉内存,但是指针指向的内存地址依旧可用(获取处于沉睡状态),所以最好不要去动它,不然很容易导致内存泄露

动态声明的指针一定要释放,free函数释放

动态分配内存最好要检查是否成功分配到

int* t = (int*)malloc(10 * sizeof(int));
if (t == NULL)
{
	//分配错误
	system("pause");
	return 0;
}
else
{
	//你的代码
	system("pause");
	free(t);
}

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!     

相关文章

  • C++实现简单的希尔排序Shell Sort实例

    C++实现简单的希尔排序Shell Sort实例

    这篇文章主要介绍了C++实现简单的希尔排序Shell Sort实例,对于正在学习算法的朋友很有借鉴价值,需要的朋友可以参考下
    2014-07-07
  • 简单讲解C++的内部和外部函数以及宏的定义

    简单讲解C++的内部和外部函数以及宏的定义

    这篇文章主要介绍了简单讲解C++的内部和外部函数以及宏的定义,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C语言中实现KMP算法的实例讲解

    C语言中实现KMP算法的实例讲解

    KMP算法即字符串匹配算法,C语言中KMP可以避免指针回溯从而达到高效,接下来就来总结一下C语言中实现KMP算法的实例讲解
    2016-06-06
  • C语言菜鸟基础教程之数据类型

    C语言菜鸟基础教程之数据类型

    在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。
    2017-10-10
  • VC中SendMessage和PostMessage的区别

    VC中SendMessage和PostMessage的区别

    这篇文章主要介绍了VC中SendMessage和PostMessage的区别,较为全面的分析了SendMessage和PostMessage运行原理及用法上的不同之处,非常具有实用价值,需要的朋友可以参考下
    2014-10-10
  • Linux vmstat命令实战详细解析

    Linux vmstat命令实战详细解析

    这个命令是我查看Linux/Unix最喜爱的命令,一个是Linux/Unix都支持,二是相比top,我可以看到整个机器的CPU,内存,IO的使用情况,而不是单单看到各个进程的CPU使用率和内存使用率(使用场景不一样)
    2013-09-09
  • C++ 中 vector 的常用操作方法汇总

    C++ 中 vector 的常用操作方法汇总

    在C++的STL中,vector是一个动态数组,可以在运行时调整大小,本文介绍了vector的初始化、元素访问、修改、迭代器操作、容量管理以及性能优化技巧,通过这些操作,可以有效地使用vector管理数据,本文介绍C++  vector 操作,感兴趣的朋友一起看看吧
    2024-10-10
  • C++带有指针成员的类处理方式详解

    C++带有指针成员的类处理方式详解

    这篇文章主要为大家详细介绍了C++带有指针成员的类处理方式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • C/C++ 中extern关键字详解

    C/C++ 中extern关键字详解

    这篇文章主要介绍了C/C++ 中extern关键字详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • 老程序员教你一天时间完成C语言扫雷游戏

    老程序员教你一天时间完成C语言扫雷游戏

    这篇文章主要为大家详细介绍了C语言实现扫雷游戏初级版,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08

最新评论