C语言指针必备基础全面覆盖

目录
  • 前言
  • 一、指针是什么?
    • 1.数据在内存中的存储
    • 2.一个小的单元到底是多大?
  • 二、指针变量
    • 1.什么是指针变量
    • 2.指针类型
    • 3.指针类型的作用
  • 三、野指针
    • 1.什么是野指针
    • 2.野指针成因
      • 2.1. 指针未初始化
      • 2.2指针越界访问
      • 2.3指针指向的空间释放
    • 3.如何规避野指针
  • 四、指针运算
    • 1.指针±整数
    • 2.指针-指针
  • 五、指针和数组
    • 1.数组元素的指针
    • 2.通过指针引用数组元素
  • 六、二级指针
  • 七、指针数组
  • 最后

前言

指针是C语言中的一个重要概念。正确而灵活的运用指针,可以使程序间接、紧凑、高效。每一个学习和使用C语言的人,都应当深入地学习和掌握指针。

提示:以下是本篇文章正文内容,下面案例可供参考

一、指针是什么?

指针是包含内存地址的变量,这个地址是内存中另一个对象(通常是另一个变量)的位置。例如如果一个变量包含另一个变量的地址,我们说第一个变量指向第二个变量。

相信大家看到上面这段话,可能有点懵,不急,我稍后再给大家解释。在这里,我先给大家讲述一下,数据在内存中是如何存储和读取的?

1.数据在内存中的存储

如果在程序中定义了一个变量,在对程序进行编译的时候,系统就会给这个变量分配内存单元。编译系统根据程序中的定义的变量类型,分配一定长度的空间

那么,这些字节在内存中被分配到哪里?我们如何找到呢?
为了解决这个问题,我们就给内存区的每一个字节一个编号,这个就是它们的“地址”。它相当于旅馆中的房间号,在地址所标志的内存单元中存放的数据则相当于旅馆房间中居住的旅客。

所以指针是个变量,存放内存单元的地址(编号)

2.一个小的单元到底是多大?

1、对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压)就是(1或者0);

2根地址线上的电信号转换成数字信号用(1/0)表示,所以可能性
00000000000000000000000000000000–11111111111111111111111111111111
也就是有2^32 编号,说明可以管理2的32次方个单元
这里就有2的32次方个地址。

每个地址标识一个字节,那我们就可以给
(2^32Byte == 2^32/1024KB == 2^32 /1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空闲进行编址。

按照同样的方法,我们可以计算出64四位机器,下面就直接给结论了。

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

指针的大小在32位平台是4个字节,在64位平台是8个字节。

二、指针变量

1.什么是指针变量

思考一个问题,在编译器中,如何把3赋值给i这个变量中?

第一种作法,把3直接送到i所表示的单元中,例如“i=3”;

int main()
{
	int i=3;
	return 0;
}

第二种方法,把3送到变量p所指向的单元(即变量i的存储单元,也就是地址,如p=3,其中i表示p指向的对象)

int main()
{
	int i;
	//int i = 3;//第一种方法
	int *p = &i;//第二种方法
	//这里我们对变量a,取出它的地址,可以使用&操作符。
   //将i的地址存放在p变量中,p就是一个指针变量。
	*p = 3;
	printf("%d\n", i);
	return 0;
}

2.指针类型

思考一个问题:

把int型变量a和float型变量b先后分配到2000开始的存储单元中,&a和&b的信息完全相同吗?

答案是不相同的,因为虽然存储单元的编号相同,但他们的数据类型不同。

此外,还因为数据类型的不同,无法确定是从一个字节中取信息(字符数据),还是从两个字节取信息(短整型),抑或是从四个字节取信息(整型),不同的类型,存储方式是不一样的。

如果我们要将&num(num的地址)保存到p中,我们需要我们给指针变量相应的类型。

如下:

char  *pc = NULL;//har* 类型的指针是为了存放 char 类型变量的地址。
int   *pi = NULL;//int* 类型的指针是为了存放 int 类型变量的地址。
short *ps = NULL;//short* 类型的指针是为了存放 short 类型变量的地址
long  *pl = NULL;
float *pf = NULL;
double *pd = NULL;

这里可以看到,指针的定义方式是: 类型名 * 指针变量名 。

【总结】

C语言中的地址包括位置信息(内存编号,或称纯地址)和它所指向的数据的类型信息,或者说它是“带类型的地址”,如&a,一般称它位“变量a的地址”,但是确切地说,它是“整型变量a的地址”

3.指针类型的作用

作用一:

指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存的大小)

int main()

{	int a = 0x11223344;
	int* pa = &a;
	*pa = 0;
	return 0;
}

int main()
{
	int a = 0x11223344;
/*	int* pa = &a;
	*pa = 0;*/
	char* pc = &a;//int*
	*pc = 0;
	return 0;
}

指针类型的意义1

指针类型决定了指针解引用操作的时候,一次访问几个字节(访问内存的大小)
char* 指针解引用访问1个字节
int* 指针解引用访问4个字节

作用二:

指针类型决定了,指针±整数的时候的步长(指针±整数的时候,跳过几个字节)

int main()
{
	int a = 10;
	int * pa=&a;
	char *pc = &a;
	printf("%p\n", pa);
	printf("%p\n", pc);

	printf("%p\n", pa+1);//如果是整型指针int*,+1则跳过4个字节、
	printf("%p\n", pc+1);//char* 指针+1,跳过1个字节
	return 0;
}

三、野指针

1.什么是野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

什么意思?举个例子
就是你捡到一把钥匙,但是不知道它可以开那道门。

2.野指针成因

2.1. 指针未初始化

指针没有初始化,里面放的是随机值

#include <stdio.h>
int main()
{
 	int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;//通过p中存的随机值作为地址,找到一个空间,这个空间不属于我们当前的程序,就造成了非法访问
//如果非法访问了,p就是野指针
 return 0; }

2.2指针越界访问

指针越界造成野指针问题

int main()
{
	int arr[10] = 0;
	int i = 0;
	int * p = arr;

	for (i = 0; i <= 10; i++)//这里循环了11次,当指针指向的范围超出数组arr的范围时,p就是野指针
	{
		*p = 1;
		p++;
	}
	return 0;
}

2.3指针指向的空间释放

当一个指针指向的空间释放了,这个指针就变成野指针了

int* test()
{
	int a = 10;
	return &a;  //int *,生命周期,出来就销毁了
}
int main()
{
	int *p = test();
	//printf("不愧是你\n");//加入这里加了一条语句,下面的值就变了
	printf("%d\n", *p);//编译出10是因为编译器会对值做一次保留。所以能访问到上面函数不一定是对的
	return 0;
}

3.如何规避野指针

  • 指针初始化
  • 小心指针越界
  • 指针指向空间释放即使置NULL
  • 避免返回局部变量的地址
  • 指针使用之前检查有效性
//规避野指针
int main()
{
	int a = 10;
	int * p = &a;//1、明确初始化,确定指向

	int * p2 = NULL;//NULL本质是0,2、不知道一个指针当前应该指向哪里是,可以初始化位NULL
	//*p2 = 100;//err,对于空指针,是不能直接解引用的

	//如何规避?
	if (p2 != NULL)//先判断是不是空指针
	{
		*p2 = 100;//这样才对
	}

}

四、指针运算

1.指针±整数

int main()
{
	float arr[5];
	float *p;
	for (p = &arr[0]; p < &arr[5];)
	{
		*p++ = 0;//对一个指针加1使它指向数组中的下一个元素,把指针指向的内容全部赋值给0
	}
	return 0;
}

也就说,如果加2使它向右移动2个元素的位置,依次类推。把一个指针减去2使它向左移动2个元素的位置。

2.指针-指针

1、指针减去指针的前提,是两个指针指向同一块区域
2、指针减去指针,得到数字的绝对值,是指针和指针之间元素的个数

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	char ch[5] = { 0 };
	//printf("%d\n", &arr[9] - &ch[0]);//这种算法是错误的

	printf("%d\n", &arr[9] - &arr[0]);//算出的是元素的个数
	printf("%d\n", &arr[0] - &arr[9]);//
	//指针减去指针的前提,是两个指针指向同一块区域
	//指针减去指针,得到数字的绝对值,是指针和指针之间元素的个数

	return 0;
}

【注意】
指针与指针之间不能进行加法运算,因为进行加法后,得到的结果指向一个不知所向的地方,没有实际意义

什么意思,举个例子。

五、指针和数组

1.数组元素的指针

一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。

指针变量既然可以指向变量,当然也可以指向数组元素,也就是把某一元素地址放到一个指针变量中。

所谓数组元素的指针就是数组元素的地址

(1)用一个指针变量指向一个数组元素

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int* p;//定义p位指向整型变量的指针变量
	p = &arr[0];//把a[0]元素的地址赋给指针变量p
	return 0;
}

以上是使指针变量p指向a数组的第0号元素

2.通过指针引用数组元素

(1)下标法,如a[i]形式
(2)指针法,如*(a+i)

下标法:

int main()
{
	int arr[10];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("%d\n", sz);
	printf("%p\n", arr);//数组名就是首元素地址
	//下标法
	printf("%p\n", &arr[0]);
	int* p=&arr[5];//整型地址放在整型指针上,从而让指针跟数组建立联系

	//数组名确实是首元素地址,
	//但是有两个例外
	//1.sizeof(数组名),这里的数组名不是首元素地址,是表示整个数组,计算的是整个数组的大小,单位是字节
	//2.&数组名,拿到的是整个数组的地址

	return 0;
}

指针法:

int main()
{
	int arr[10];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);

	int* p=&arr[0];//整型地址放在整型指针上,从而让指针跟数组建立联系
	//指针法
	for (i = 0; i < sz; i++)
	{
		*(p + i) = i;// p+i 其实计算的是数组 arr 下标为i的地址。
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

一个小知识:

int main()
{

	int arr[10] = { 0 };
	arr;//数组名
	&arr[0];//取出首元素地址
	&arr;//取出整个数组的地址
	printf("%d\n", &arr[0]);
	printf("%d\n", &arr);
	return 0;
}

六、二级指针

指针变量的地址二级指针

什么意思?举个例子

int main()
{
	int a = 10;//4byte,向内存申请4个字节
	int* p=&a;//p指向a,称为一级指针

	int* *pp=&p;//pp就是二级指针,pp存放的是一级指针的地址
	* *pp = 20;//需两层解引用
	printf("%d\n", a);
	//int** * ppp = &pp;//ppp就是三级指针
	return 0;
}

七、指针数组

存放指针的数组就是指针数组

int main()
{
	int arr[10];//整型数组,存放整型的数组就是整型数组
	char ch[5];//字符数组,存放字符的数组就是字符数组
	//指针数组,存放指针的数组就是指针数组
	//int*  整型指针的数组
	//char* 字符指针的数组

	int* parr[5];//整型指针的数组,存放的类型都是int*
	char* pc[6];//字符指针的数组
	return 0;
}

我们也可以用同样的方式来访问指针数组。
如下

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;

	int * arr[3] = { &a, &b, &c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d\n",*(arr[i]));
	}

	int *pa = &a;
	int *pb = &b;
	int *pc = &c;
	return 0;
}

最后

本文介绍的是指针的基础知识,往后还会继续深入讲解指针更深入的知识。此外,本文参考了谭浩强《C语言设计》(第五版),以及网上的部分资料,加之自己在学习听课时的笔记,梳理而成,花费了我很多心思。当文章写成之时,时间已过去4个多小时!

希望能对看到的大家有所帮助!

以上就是C语言指针必备基础全面覆盖的详细内容,更多关于C语言 指针的资料请关注我们其它相关文章!

(0)

相关推荐

  • C语言中长度为0的数组详解

    目录 概述 使用方式 总结 概述 长度为0的数组在标准c和c++中是不合法的,但是在gcc中是可行的. 长度为0数组它的最典型的用法就是位于结构体中的最后一项. 使用方式 如下面的例子,分别使用长度为0的数组和指针声明结构体,实现可变长度的数组功能: #include <stdio.h> #include <stdlib.h> struct test1 { int a; int b[0]; }; struct test2 { int a; int *b; }; struct tes

  • C语言中的初阶指针详解

    目录 1.指针是什么 2.指针和指针类型 3.野指针 3.1野指针成因 3.2如何规避野指针 4.指针的运算 4.1指针±整数 4.2指针-指针 4.3指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 ​ 总结 1.指针是什么 ​ 初学者都有一个疑问,那就是指针是什么?简单的说,就是通过它能找到以它为地址的内存单元. 地址指向了一个确定的内存空间,所以地址形象的被称为指针. int main() { int a = 10; int* pa = &a; return 0; } //pa是

  • C语言编程之初识数组线性查找和二分查找

    目录 线性查找 二分查找 先来了解一下什么是查找, 额,好吧,这没什么可了解的, 就是查找数组中的某个元素的位置或是否存在. 就这,没了.直接了解查找算法吧. 线性查找 线性查找与二分查找有些差别. 数组内元素可以是混乱无序的,即没有按顺序储存.这方法很简单,就是从首元素开始,依此向后查找,比较.仅此而已.运用循环,依次对比. 看代码吧. #include <stdio.h> int main(void) { int arr[] = { 5,4,6,8,7,9,10,2,3,1 }; int

  • C语言指针基础详解

    目录 1.1:概述 1.1.1:内存 1.1.2:内存 1.1.3:指针和指针变量 1.2:指针基础知识 1.2.1:指针变量的定义和使用 1.2.2:通过指针间接修改变量的值 1.2.3:指针的大小 1.2.4:空指针与野指针 1.2.4:万能指针 1.3:指针与数组 1.3.1:数组名 1.3.2:指针操作数组 1.3.3:指针的加减运算 1.4:指针基础小结 1.5:总结 1.1:概述 1.1.1:内存 内存含义: 储存器:用来存储程序和数据,辅助CPU进行运算处理的重要组成部分. 内存:

  • C语言小知识之为什么要使用指针详析

    刚开始学习C语言的时候,感觉最难理解的就是指针,什么指针变量,变量指针,指向指针的变量,指向变量的指针?一堆概念,搞得人云里雾里的,今天不讨论这些概念的问题,从最底层来分析C语言中为什么要使用指针,指针存在的意义又是什么呢? 首先从一个简单的例子来看,写一段代码来交换x.y的值. void main( void ) { u8 x = 10, y = 20; u8 temp; __asm( "sim" ); //禁止中断 SysClkInit(); delay_init( 16 ); L

  • C语言动态数组详解

    目录 内存分配函数malloc calloc realloc free 内存操作函数 memset memcpy memmove 二维动态数组的建立和释放 总结 内存分配函数malloc calloc realloc free 堆内存分配函数 说明 void * malloc(int n) 形参n为要求分配的字节数.需要注意的是,malloc函数分配得到的内存空间是未初始化的.必须使用memset函数来初始化. calloc(10, sizeof(char)); 两个参数:单元数,单元的size

  • C语言指针必备基础全面覆盖

    目录 前言 一.指针是什么? 1.数据在内存中的存储 2.一个小的单元到底是多大? 二.指针变量 1.什么是指针变量 2.指针类型 3.指针类型的作用 三.野指针 1.什么是野指针 2.野指针成因 2.1. 指针未初始化 2.2指针越界访问 2.3指针指向的空间释放 3.如何规避野指针 四.指针运算 1.指针±整数 2.指针-指针 五.指针和数组 1.数组元素的指针 2.通过指针引用数组元素 六.二级指针 七.指针数组 最后 前言 指针是C语言中的一个重要概念.正确而灵活的运用指针,可以使程序间

  • c语言 指针零基础讲解

    1.指针是什么(可能有点难理解) 指针的是啥? 指针实际上就是地址,地址就是系统给定的编号,编号就是一个个内存单元. 在某种情况来说指针=地址=编号=内存单元. 指针就是地址,顾名思义,就是可以用来寻找目标的. 所以指针变量就是存放地址的变量. 当然我们口头上常说的指针就是指针变量~ 那指针是怎么产生的呢,也就是说内存是怎样产生的呢? 我们知道我们的计算机就是32位或64位系统组成,这32与64在物理上就是32根物理电线或64根物理电线组成.这物理电线通电时,就会产生高电频,从而产生电信号,再由

  • 零基础详解C语言指针进阶

    目录 前言 1.字符指针 例题 1 2.指针数组 例题 2 3.数组指针 3.1数组指针的定义 3.2 &数组名与数组名 3.3 数组指针的使用 4.数组与指针在函数里的传参 4.1 一维数组的传参 4.2 二维数组的传参 4.3 一级指针的传参 4.4 二级指针的传参 5.函数指针 6. 函数指针数组 用函数指针数组实现一个计算器 7.回调函数 前言 这是指针的进阶,如果想入门指针的朋友可以关注我的另外一篇文章—c语言 指针零基础讲解 坚持看完,一定会有很大的收获~~ 那接下来—起航 1.字符

  • C语言指针基础知识实例讲解

    对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小.比如int整型变量分配4个字节,char字符型变量分配1个字节等等.被分配在内存的变量,可以通过地址去找到,内存区每一个字节都有一个编号,地址也可以形象的理解成我们生活中的住址,通过住址找到每一个人所在的地方.指针作为一个变量用来存放地址,可以通过指针来改动变量. 上图就是一个简单的定义一个一级指针变量和利用指针改变变量数值的过程.int*表示整型指针,*p表示解引用操作,就是利用指针找到a的地址然后再改

  • 深入理解C语言指针及占据内存空间

    原文链接:https://www.cnblogs.com/l-hh/p/12288613.html 第一.了解内存空间 本文章文字有点多,会有点枯燥,配合图文一起看可以缓解枯燥,耐心阅读哦!!! 先了解内存地址,才更好的理解指针! 我们可以把内存想象为成一列很长很长的货运火车,有很多大小相同的车厢,而每个车厢正好相当于在内存中表示一个字节.这些车厢装着不同的货物,就像我们的内存要存着各式各样的数据. 多啰嗦一下 我们平时在电脑上能够听音乐.看视频和文章,其实看到的这些东西就是内存中每个"车厢&q

  • 手把手带你走进Go语言之语法基础解析

    目录 概述 Go 语法基础 关键字 标识符 数据类型 变量声明 多变量声明 值类型和引用类型 概述 Golang 是一个跨平台的新生编程语言. 今天小白就带大家一起携手走进 Golang 的世界. (第 2 课) Go 语法基础 Go 程序可以由多个标记组成: 关键字 标识符 常量 字符串 符号 关键字 Go 语言有 25 个关键字: 关键字 作用 var & const 变量和常量的声明 package & import 导入 func 用于定义函数和方法 return 用于函数返回 d

  • 赌你会懵的C语言指针进阶数组场景解析

    目录 正片开始 一维数组 字符数组 二维数组 整点硬菜 正片开始 细化指针这一部分内容,现在着重把一些指针的运用情景搬出来康康,如果对指针盘的不是非常熟练,或者指针还出于入门阶段的铁子请绕道(晕头警告) 直接给大家盘个套餐: 一维数组 int a[] = {1,2,3,4,5}; printf("%d\n",sizeof(a)); printf("%d\n",sizeof(a+0)); printf("%d\n",sizeof(*a)); pri

  • C语言指针超详细讲解下篇

    目录 前言 指针运算 指针±整数 4.1 指针±整数 4.2 指针-指针 4.3 指针的关系运算 5.指针和数组 6.二级指针 7.指针数组 7.1 举例 1 7.2 举例 2 总结 前言 本文接着上一篇内容,继续学习指针相关知识点. 指针运算 指针±整数 指针-指针 指针的关系运算 4.1 指针±整数 #define VALUE 5 int main() { float values[VALUE]; float *vp; //指针+-指针,关系运算 for (vp = &values[0];

  • C语言线索二叉树基础解读

    目录 线索二叉树的意义 线索二叉树的定义 线索二叉树结构的实现 二叉树的线索存储结构 二叉树的中序线索化 线索二叉树的中序遍历 总结 线索二叉树的意义 对于一个有n个节点的二叉树,每个节点有指向左右孩子的指针域.其中会出现n+ 1个空指针域,这些空间不储存任何事物,浪费着内存的资源. 对于一些需要频繁进行二叉树遍历操作的场合,二叉树的非递归遍历操作过程相对比较复杂,递归遍历虽然简单明了,但是会有额外的开销,对于操作的时间和空间都比较浪费. 我们可以考虑利用这些空地址,存放指向节点在某种遍历次序下

  • C语言数据结构算法基础之循环队列示例

    目录 说明 示例代码 1. 首先定义结构体: 2. 定义各种算法: 3. 测试: 4. 最后的结果: 说明 循环队列是一种先进先出的,首尾相连的队列. 大致的结构如下图: 用数组来抽象的表示一下的话,如下图: 循环队列有两个指针指向数据,上图中的start和end就是那两个指针,它们指向相同的位置,表示的是空,即队列是空的. 随着数据的放入,队列一般有下面的两种形式: 需要注意第二种形式,从图上看end在start的前面了,但是因为循环关系,前后并不重要. 另外需要考虑的是队列满的情况: 但这种

随机推荐