详解指针(1)

在那遥远的东方大陆,有一种C语言,C语言中有一神秘之物名为指针。

正片

       1. 什么是指针

   想要懂得什么是指针,首先我们得知道在我们创建变量时在内存中是如何创建的。当我们创建一个变量时首先向内存中申请一块空间,每一块空间都有它自己的地址,指针就是用来存放这个地址的变量。变量都是有类型的在这里int*就是指针变量的类型。

int i = 1;
int *p= &i;//&取地址符号,将i的地址取出放在指针变量p中

  解引用操作符“*”,简单来说就是打开箱子的钥匙,箱子就是地址,地址指向的就是在内存中创建的变量。

int i = 1;
int * p = &i;
int n =*p;
printf("%d",n);//输出结果为1

  2.指针变量的大小

  众所周知变量是有类型的例如:int,char~ 等,竟然指针是也属于变量那么指针的也就有数据的类型的,竟然有了类型那就有了大小那么指针变量的大小是多大呢?int*类型的指针是4个字节?char*类型的是1个字节?下面在编译器里应证一下。

int main()
{
	printf("char* = %zd\n", sizeof(char*));
	printf("int* = %zd\n", sizeof(int*));
	printf("float* = %zd\n", sizeof(float*));
	return 0;
}

结果怎么样的呢?

这里是在64位操作系统中实现的,我们可以观察到指针变量的大小跟数据类型没有关系在64位操作系统中指针变量的大小是8个字节。

在32位的操作系统中指针的大小会有变化吗?下面是在32位操作系统指针的大小,我们可以观察到在32位操作系统中,指针变量的大小是4个字节。

 由此我们可以得出结论:
32位操作系统中地址是32个比特位,指针变量的大小是4个字节。                                                  64位操作系统中地址是64个比特位,指针变量的大小是8个字节。                                                    指针变量的大小与指针变量的类型没有关系,与操作系统有关系。

3.指针变量类型的作用

竟然指针变量的大小与类型无关,那么指针变量为什么要有类型呢?

int main()
{
	int a = 0x11111111;
	int* p = &a;
	*p = 0;
	return 0;
}

在这段代码中,对a变量的地址进行修改在调试时我们会发现会将a的4个字节全部修改成零。         下面这段代码,是对b的地址进行修改,通过观察我们可以发现只将b的地址中的一个字节变成了零。                                                                                                                                                    

int main()
{
	int b = 0x11111122;
	char* p =(char*) & b;
	*p = 0;
	return 0;
}

4. void*指针

void*指针是一种无类型的指针,void*不能进行指针的运算,因为他没有类型,一遍void*指针一般用于接收函数参数的操作,因为它可以接收任意类型的指针。

5.const修饰指针

const主要用于修饰指针,可以放在指针的左边和右边

int main()
{
	int a = 9;
    int m =10;
	int* p = &a;
	int const* p = &a;
	int* const p = &a;
	int const * const p = &a;
}

 当const放在*左边时表示限制的是*p不能通过*p去修改p指向的内容,但是可以修改p变量本身的内容即:p=&m。

当const放在*右边时表示限制的是p本身的内容不能改变限制的是:p=&m,但是可以通过*p去修改片指向的内容即:*p=m。

当const在同时修饰*的左右两边时,即不能p=&m,也不能*p=m。

6.指针的运算

指针的运算有三种:指针 +- 整数、指针-指针、指针的关系运算。

1.指针 +- 整数

 int main()
 {
 int n = 10;
 char *pc = (char*)&n;
 int *pi = &n;
 printf("%p\n", &n);//打印的是n的地址
 printf("%p\n", pc);//打印的也是n的地址
 printf("%p\n", pc+1);//在n的地址上加一
 printf("%p\n", pi);//n的地址
 printf("%p\n", pi+1);//n的地址+4
 return  0;
 }

输出结果:                                                                                                                                            

 2.指针-指针

指针 - 指针表示的是两个指针之间元素的个数。可以用来模拟strlen()函数。

int my_strlen(char* a)
{
	char* p = a;
	while (*p != '\0')
	{
		p++;
	}
	return p - a;
}
int main()
{
	printf("%d", my_strlen("adafadf"));//输出值为7
	return 0;
}

3.指针关系的运算                                                                                                             

打印数组

void print(int *a,int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", *(a + i));
	}
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	int* a = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(a, sz);
}

7.野指针

野指针的概念是指针指向的内容是不可知,随机,没有限制。

野指针成因:

1.指针未初始化

int *p;
*p = 9;

2,指针越界访问

void print(int *a,int sz)
{
	for (int i = 0; i < 10; i++)//超出了数组的范围,指针越界a就变成了野指针
	{
		printf("%d ", *(a + i));
	}
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	int* a = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	print(a, sz);
}

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

当函数执行完毕的时候,n的空间已经被释放了。

int text()
{
	int n = 9;
	return &n;
}
int main()
{
	int* p = text();
	printf("%d", *p);
}

如何避免野指针

1.对指针进行初始化

int*p=NULL;

2.避免指针越界

3.当不使用指针的时候NULL,并且判断指针是否NULL。

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

8.assert断⾔

assert(*p!= NULL);

用来判断指针是否是野指针,如果是野指针就会报错,assert的机制很非常友好,可以通过开关来控制,开关。assert只能在debug版本只使用,release版本中是禁止使用的。

#define NDEBUG
#include <assert.h>

9.传值调⽤和传址调⽤

竟然要学习指针肯定有它的作用。下面我们来交换两个变量的值。

1.传值调用

void Huan(int a, int b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int x = 8;
	int y = 1;
	Huan(x,y);
	printf("交换前x = %d y = %d\n", x, y);
	printf("交换后x = %d y = %d\n", x, y);
}

按照代码的逻辑来看,这两个数已经交换了但运行后发现:

 并没有达到目的,这是为什么?

当我们进行调试的时候发现我们交换的只是形参a,b的值。如何解决这种情况呢?当然是运用指针。

 2.传址调用

void Huan(int* p, int* s )
{
	int tmp = *p;
	*p = *s;
	*s = tmp;
}
int main()
{
	int x = 8;
	int y = 1;
	int* p = &x;
	int* s = &y;
	printf("交换前x = %d y = %d\n", x, y);
	Huan(p,s);
	printf("交换后x = %d y = %d\n", x, y);
}

通过向函数传输地址,改变地址的指向从而交换两个变量。这就是传址调用。

今天的分享就到此为止啦!希望大家点赞加关注还有收藏。也欢迎大家指错,主打听劝。

  • 29
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编程菜鸟99号

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

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

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

打赏作者

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

抵扣说明:

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

余额充值