初阶C语言 - 4000字带你走进指针(前篇)

目录

1. 指针是什么

2. 指针和指针类型 

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

2.2 指针类型的意义  

2.2.1 意义1:指针的类型决定了指针在被解引用的时候可以访问几个字节 

2.2.2 意义2: 指针的类型决定了指针+-1操作的时候,跳过几个字节,即指针的步长。

3. 野指针 

 3.1 野指针成因

1. 指针未初始化 

 2. 指针越界访问 

3. 指针指向的空间释放 ​

 3.2 如何规避野指针

4. 指针运算 

 4.1 指针+-整数

4.2 指针-指针 

 例子:自定义函数求字符串的长度

4.3 指针的关系运算 (比较大小)

5. 指针和数组


1. 指针是什么

1、每个内存单元都有一个编号,把内存单元的编号就称为地址(地址也叫指针)

指针其实就是地址,地址就是编号,指针就是内存单元的编号。

2、平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。

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

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

2. 指针和指针类型 

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

当不知道给这个指针赋值什么时,就赋值为NULL 。

sizeof 返回值的类型是无符号整型 unsigned int,严格意义上打印时应该用%u,不能用%d,
%zu打印最准确,%zu本身就是给sizeof准备的一种格式打印。

#include <stdio.h>

int main()
{
	//当不知道给这个指针赋值什么时,就赋值为NULL 
	char* pc = NULL;
	short* ps = NULL;
	int* pi = NULL;
	double* pg = NULL;

	//sizeof 返回值的类型是无符号整型 unsigned int,严格意义上打印时应该用%u,不能用%d
	//%zu打印最准确,%zu本身就是给sizeof准备的一种格式打印
	printf("%zu\n",sizeof(pc));
	printf("%zu\n", sizeof(short*));
	printf("%zu\n", sizeof pi);
	printf("%zu\n", sizeof(pg));

	return 0;
}

×86: (32位)指针的大小在32位平台是4个字节

 

×64: (64位)指针的大小在64位平台是8个字节

2.2 指针类型的意义  

1个16进制位是4个二进制位(比特位)

2个16进制位是8个二进制位,也就是1个字节 

存0x11223344这个16进制数字刚好需要4个字节(int类型是4个字节)

2.2.1 意义1:指针的类型决定了指针在被解引用的时候可以访问几个字节 

指针的类型决定了指针在被解引用的时候可以访问几个字节

如果是int*的指针,解引用访问4个字节

如果是char*的指针,解引用访问1个字节

 图解:

 

2.2.2 意义2: 指针的类型决定了指针+-1操作的时候,跳过几个字节,即指针的步长。

指针的类型决定了指针+-1操作的时候,跳过几个字节,即指针的步长。

如果是int*的指针,+1跳过4个字节

如果是char*的指针,+1跳过1个字节

当然,指针不仅可以+-1,指针+-整数时也很类似。

#include <stdio.h>

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

	printf("pa   = %p\n",pa);
	printf("pa+1 = %p\n", pa+1);
	printf("pc   = %p\n",pc);
	printf("pc+1 = %p\n", pc+1);

	return 0;
}

3. 野指针 

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

 3.1 野指针成因

1. 指针未初始化 

  

 2. 指针越界访问 

int main()
{
	int arr[10] = { 0 };
	int* p = arr;//&arr[0]
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		*p = i;
		p++;
	}

	return 0;
}

3. 指针指向的空间释放 

 3.2 如何规避野指针

1. 指针初始化

2. 小心指针越界

3. 指针指向空间释放及时置NULL

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性

4. 指针运算 

 4.1 指针+-整数

将数组中的元素都赋值为1:(下面代码都可行)

int main()
{
	int arr[10] = { 0 };
	int i = 0;
	//数组下标的写法
	for (i = 0; i < 10; i++)
	{
		arr[i] = 1;
	}
	return 0;
}
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p = 1;
		p++;
	}
	return 0;
}
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = 1;
	}
	return 0;
}
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for ( ;p<&arr[10]; )
	{
		*p++ = 1;//也可以写成 *p = 1; p++;
		         //(*p)++;是对p指向的内容进行+1,两者不一样
	}
	return 0;
}

 

4.2 指针-指针 

指针 - 指针的绝对值得到的是指针和指针之间元素的个数

当然,不是所有的指针都能相减,指向同一块空间的2个指针才能相减!!!

int main()
{
	int arr[10] = { 0 };
	printf("%d\n", &arr[9] - &arr[0]);//9

	return 0;
}

 例子:自定义函数求字符串的长度

方法一:

int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n",len);

	return 0;
}

方法二:递归 

int my_strlen(char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

方法三:指针-指针 (得到指针和指针之间元素的个数,可以得到字符串的长度)

int my_strlen(char* str)
{
	char* start = str;
	while (*str != '\0')
	{
		str++;
	}
	return (str - start);
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

运行结果: 

4.3 指针的关系运算 (比较大小)

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

例如: 将数组中的元素都赋值为1

这种做法可以:

int main()
{
	int values[5] = { 0 };
	int* vp;
	for (vp = &values[5]; vp > &values[0];)
	{
		*--vp = 1;//可以写成 --vp; *vp = 1;
	}
	return 0;
}

这种做法不行: 

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

 

5. 指针和数组

arr[i]  等于 *(p+i)  等于  *(arr+i) 

 例子:打印数组中的所有元素:

下面三种方式都可以。

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("%d ", arr[i]);
	}
	return 0;
}
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("%d ", *(arr + i));
	}
	return 0;
}
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("%d ", *(p + i));
	}
	return 0;
}

 

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值