请回答c语言-指针【入门】

指针初阶这里我们简单解释指针,后面指针进阶的时候再深入提高
在这里插入图片描述

1. 什么是指针

内存

若要谈及指针我们肯定要谈到内存,内存我们把它划分为一个个小的内存单元,内存单元的编号,就是地址。

之前在初识c语言(下)提到过的内存结构可以参考下图

image-20211021213400651
32位和64位

我们知道通常我们有32位机器和64位机器

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

  • 那么32根地址线产生的地址就会是232

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001

11111111 11111111 11111111 11111111

每个地址标识一个字节,那我们就可以给(2^32Byte == 2^32/1024KB ==232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空闲进行编址。
同样的方法,那64位机器,如果给64根地址线,那就能编址更大的空间。

也因此

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

小结:

指针就是地址,口语中常说的指针值得是指针变量

2. 指针和指针类型

我们说变量有不同的类型,整形,浮点型等。指针也是有其类型的

指针的定义方式是 type * 变量名
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
char* 类型的指针是为了存放 char 类型变量的地址。
short* 类型的指针是为了存放 short 类型变量的地址。
int* 类型的指针是为了存放 int 类型变量的地址。
虽然类型不同但是大小是一样的都是四个字节
int main()
{
	char* pc;
	int* pa;
	double* pd;

	printf("%d\n", sizeof(pc));
	printf("%d\n", sizeof(pa));
	printf("%d\n", sizeof(pd));
}

image-20211117164245617

如果我们用char类型的指针变量来存放int变量会如何?

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

image-20211117163753636

只会有1个字节的内存发生变化

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

看后面的解释

2.1 指针+-整数

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;
}

通过结果可以看到,指针每走一步的大小取决于指针本身的类型char就是1字节而int就是4字节
image-20211116195331644

2.2 指针的解引用

int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	int* pi = &n;
	*pc = 0; 
	*pi = 0; 
	return 0;
}

解引用操作改变值前后,两个指针指向地址存储的数据被改变了

image-20211116194605860

小结:

现在能够回答之前的问题了

指针的类型有什么意义

  1. 决定了在解引用的时候一次访问几个字节–指针的权限

比如: char* 的指针解引用就只能访问一个字节,而float* 的指针的解引用就能访问四个字节。

  1. 决定了指针向前或向后走一步,一步会走几个字节

3. 野指针

  • 野指针: 就是指针指向的位置是随机的、不正确的、没有明确限制的

3.1 什么造成了野指针

  1. 指针定义时不进行初始化的话,默认是随机值
int main()
	{
		int* p;
		*p = 20;
		return 0;
	}

甚至都报错了
image-20211116201835609

  1. 指针越界访问

    当指针指向的范围超出数组arr的范围时,p就是野指针

int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i = 0; i <= 11; i++)//11已经超了
	{
		*(p++) = i;
	}
	return 0;
}

也是直接报错了

image-20211116203139965

  1. 指针指向的空间释放
  • 动态内存分配过程中释放后指针没置空
free(cur);
cur=NULL;

freecur指向的地址的数据之后,cur指针就成为了野指针,所以按照习惯我们要置空

还有一种被释放的情况:

  • 局部变量的地址出了函数就没了
int* test()
{
	int a = 10;
	return &a;
}

int main()
{
	int*p = test();
	*p = 20;

	return 0;
}

当进入test函数的时候a的空间创建了,但是出来的时候因为是局部变量就销毁了,返回主函数就出问题了,这块空间找不到了,p就变为野指针了

3.2规避野指针

  • 指针初始化
int main()
{
	int a = 10;
	int p1 = &a;

	int* p2 = NULL;
	return 0;
}

两种方法,要么给指针赋值,要么就指空

  • 小心指针越界

  • 指针指向空间释放即使置NULL

这个前面刚讲过

  • 避免返回局部变量的地址
  • 指针使用之前检查有效性

养成好习惯

int main()
{
	int a = 10;
	int* pa = &a;
	int* p = NULL;

	if (p != NULL)
	{
		statement;
	}
	if (pa != NULL)
	{
		statement;
	}
	return 0;
}

4. 指针运算

4.1 指针+-整数

栗子1

int main()
{
	int arr[10] = { 0 };
	int*p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		*(p + i) = i;//指针+-整数
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

利用指针加减整数倒着打印数组的值

   	int* q = arr + sz - 1;
	for (i = 0; i < sz; i++)
	{
		//printf("%d ", *q--);
		printf("%d ", *(q - i));
	}

4.2 指针-指针

能做指针与指针之间减法的前提是两个指针指向同一块空间

随意空间相减肯定是不能减的

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

由此说明指针和指针相减得到的是元素的个数,而且高地址减低地址个数为正,低减高为负

image-20211116205955960image-20211116210046086

利用指针-指针

利用指针-指针实现一个my_strlen函数求字符串长度

//模拟实现strlen
int my_strlen(char* s)
{
	char* start = s;
	while (*s != '\0')
	{
		s++;
	}
	return s - start;
}
int main()
{
	char arr[] = "Valar_Dohaeris";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

image-20211117170141424

4.3 指针的关系运算

指针关系运算就是指针比大小

下面这段代码的意义就是从后到前面,将value数组里面下标为N_VALUES全部改为0

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
	*vp = 0;
}

image-20211116211832085

for(vp = &values[N_VALUES]; vp > &values[0];)
{
	*--vp = 0;
}

如果用这样的写法,而不是第一种写法写,大部分编译器都能通过,但是标准并不保证它可行。我们还是应该避免这样写,

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

5. 指针和数组

数组和指针本身是两种事物,但是由于数组的创建要开辟空间,就有地址,而数组名恰好等于数组首元素地址

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

image-20211116213652390

由于数组的地址是连续的,我们可以通过指针来访问地址

5.1 指针来访问数组

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

既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。

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

image-20211116214645212

所以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 ", *(p + i));
	}
	return 0;
}

6. 二级指针

ppa就是二级指针,存放的是一级指针的地址

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

image-20211116214151625

7. 指针数组

什么是指针数组?

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

int arr[10];//整形数组 - 存放整形的数组就是整形数组
char ch[5];//字符数组 - 存放的是字符

int* parr[5];//整形指针的数组
char* pch[5];

image-20211117171023668

int main()
{
	//int arr[10];//整型数组 - 存放整型的数组
	//char ch[5];//字符数组 - 存放字符的数组
	//指针数组 - 存放指针的数组
	int a = 10;
	int b = 20;
	int c = 30;
	int* arr2[3] = { &a, &b, &c };//存放整型指针的数组
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", *(arr2[i]));
	}
	return 0;
}

image-20211117170924098

8.补充

指针练习题:

8.1 练习一

#include <stdio.h>
int main()
{
  int arr[] = {1,2,3,4,5};
  short *p = (short*)arr;
  int i = 0;
  for(i=0; i<4; i++)
  {
    *(p+i) = 0;
  }
   
  for(i=0; i<5; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

image-20211126155040561

8.2 练习二

image-20211126160207977

unsigned long pulArray[] = {6,7,8,9,10};
unsigned long *pulPtr;
pulPtr = pulArray;
*(pulPtr + 3) += 3;
printf(%d,%d\n”,*pulPtr, *(pulPtr + 3));

最后应该输出6,12 答案不难,搞清楚关系之后就很简单了

小结:
老铁们觉得有收获的话一定要给个赞,多多评论哦

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

言之命至9012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值