C - 指针(初阶)

目录

一、指针是什么

二、指针和指针类型

指针类型的意义是什么呢?

 指针+整数

指针的解引用

三、野指针

野指针成因

1.指针未初始化

2.指针越界访问

3.指针指向的空间释放

如何规避野指针?

四、指针运算

指针+整数

指针-指针

指针的运算关系

 五、指针和数组

六、二级指针

七、指针数组


一、指针是什么

在计算机科学中,指针是编程语言中的一个对象,利用地址,它的值直接指向存在电脑储存器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化地称为“指针”,意思是通过它能找到以它为地址的内存单元。

基础知识补充:

整型常量的不同进制表示:

计算机中只能储存二进制数,即0,1。

为了更方便观察内存中二进制的情况,计算机还提供了八进制、十进制、十六进制

二进制:以0b开头标示,数位变化为0,1。

八进制:以0开头标示,数位变化范围为0~7。

十进制:以0开头标示,数位变化范围为0~9。

十六进制:以0x开头标示,数位变化范围为0~9和a~f,a代表10,f代表15。

计算机内存单位:

:bit,简写为b,位是最小的内存单位,可以储存0和1。

字节:byte,简写B,字节是常用的计算机储存单位,1字节为8位。一个ASCII码用一个字节表示,一个汉字用两个字节表示。

:word,简称字,设计计算机时给定的自然储存单位,最初一个字长只有8位,慢慢增至16位,32位,直到现在的64位。

扩展的储存单位:KB,MB,GB,TB,用来表示计算机各种存储介质(例如内存、硬盘、光盘等)的存储容量。

1 Byte=8 Bit

1 KB=1024 Byte

IMB = 1024KB;

1GB = 1024MB;

1TB = 1024GB;

1TB=1024GB=10242MB=10243KB=10244B=8*10244位

int main()
{
	int a = 10;//a是int类型,占4个字节
	int* pa = &a;//a占4个字节,a有4个地址,取到的是首地址
	*pa = 20;
	printf("%d\n", a);
	return 0;
}
  • 我们可以通过&(取地址操作符)取出变量的内存地址,把地址可以存放到一个变量中,这个变量就是指针变量
  • 指针是内存中一个最小单元的编号,也就是地址。
  • 平时所说的指针,通常指的是指针变量,是用来存放内存地址的变量。

总结:

  1. 指针变量,就是用来存放地址的变量。
  2. 指针的大小在32位平台是4个字节,在64位平台是8个字节。

二、指针和指针类型

int main()
{
	int * pa;
	char * pb;
	float * pc;
	printf("%d\n", sizeof(pa));
	printf("%d\n", sizeof(pb));
	printf("%d\n", sizeof(pc));
	return 0;
}

输出:4  4  4

无论是什么类型的指针,大小都是4个字节

a的值为0x11223344,为什么在内存中显示44332211呢?因为英特尔的CPU采用了小端方式进行数据储存,因此低位在前,高位在后。 

指针定义的方式是 :type *

int * 类型的指针,是为了存放int类型变量的地址。

char * 类型的指针,是为了存放char类型变量的地址。

short * 类型的指针,是为了存放short类型变量的地址。

指针类型的意义是什么呢?

  • 指针类型决定了:指针解引用的权限有多大。
  • 指针类型决定了:指针向前或者向后走一步的步长。

 指针+整数

 指针的类型决定了指针向前或者向后走一步有多大距离。

指针的解引用

 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

char*的指针解引用只能访问1个字节,int* 的指针解引用能访问4个字节。

三、野指针

野指针成因

1.指针未初始化

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

局部变量指针未初始化,默认为随机值。

相当于我们在酒店订了一间房,房号随机, 我们不知道是那间房,因此我们也不能往房间里放行李。如果随便找了一间房放进去,因为并不是我们订的那间随机房,就会构成非法访问

2.指针越界访问

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 11; i++)
	{
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		*(p ++) = i;
	}
	return 0;
}

3.指针指向的空间释放

举个简单的例子说明:

int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	*p = 20;
	return 0;
}

形参只有在函数被调用的过程中才实例化(分配内存),函数调用完成后自动销毁。

a是进入test这个函数后才被创建的,函数结束后销毁,空间已经被释放了。

再将20赋给*p的时候,找不到*p的地址了

如何规避野指针?

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

注:当不知道p应该初始化为什么地址的时候,直接初始化为NULL

例:int * p = NULL;

四、指针运算

指针+整数

指针-指针

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%d\n",arr[9]-arr[0]);
	return 0;
}

输出:9

那下列代码可行吗?

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	char ch[5] = { 0 };
	printf("%d\n",arr[9]-ch[2]);
	return 0;
}

 虽然有输出,但是并不可行。

指针和指针相减的前提是:两个指针指向同一块空间。

//指针实现my_strlen
int my_strlen(char* str)
{
	char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return str - start;
}
int main()
{
	int len = my_strlen("abc");
	printf("%d\n", len);
	return 0;
}

那指针+指针有意义吗?

没有意义。就像日期,相减可以得到天数,相加却没有意义。

指针的运算关系

标准规定:允许指向数组元素的指针,与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素的那个内存位置的指针进行比较。

#define N_VALUES 5
int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[0]; vp > &values[0];)
	{
			*--vp = 0;
	}
	return 0;
}

理论上来说,下面的写法大部分编译器也行得通。

#define N_VALUES 5
int main()
{
	float values[N_VALUES];
	float* vp;
	for (vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
	{
			*vp = 0;
	}
	return 0;
}

但是由于访问到values[0]前面一个元素内存的时候,与标准规定不一致,并不一定可行,应该避免这种写法。

 五、指针和数组

 数组名表示数组首元素的地址。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	//p存放的是数组首元素的地址
	return 0;
}

那这样写代码就是可行的。

既然可以把数组名当成地址存放在指针中,那通过指针访问数组也就有可能实现。

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

 所以,p+i 其实计算的就是数组arr下标 i 的地址。

那就可以直接通过指针直接访问数组。

 补充:

[ ] 是一个操作符,arr、2 是它的两个操作数。

同加法有交换律一样,a+b可以写成b+a,那 arr[2] 也可以写出 2[arr] 。

arr[2] --> *(arr+2)--> *(2+arr)--> 2[arr]

arr[2] --> *(arr+2) --> *(p+2)  --> *(2+p)  --> *(2+arr)  --> 2[arr]

六、二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?

这就是二级指针

int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	printf("%p\n", &a);
	printf("%p\n", &pa);
	printf("%p\n", &ppa);
	return 0;
}

 a的地址存放在pa中,pa的地址存放在ppa中。

pa是一级指针,ppa是二级指针。

七、指针数组

指针数组-存放指针的数组。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值