C语言初学者关于指针的认识

1.什么是指针?

    指针其实与电脑内存有关,所以在讲指针前应该讲讲电脑的内存。实际上,为了提高内存的利用率,电脑把一大块的内存依次划分为一个个基本的内存单元,而每一块内存单元的大小就是1个字节。这些内存单元可以存放电脑数据,同时每一个内存单元都有自己的编号,而这个编号也就是常说的地址。我们知道,在日常生活中通过房门号可以找到相应的房间。在电脑中也一样,我们可以通过地址找到相应的内存单元。既然地址如此重要,那么我们就想着创建一个变量来储存这个地址,而所创建的变量就是指针变量了,我们通常直接称它为指针。下面是一个例子:

int main()
{
  int a=10;//由于我们创建了一个整形变量(占内存4个字节),需要向电脑申请四个内存单元组成的内存空间
 //假设该内存空间的地址编码为0x11223344
 //此时我们需要一个指针变量将该地址编码存储起来
int*p=&a;
 //&是取地址操作符,&a即取a的地址,其数值为0x11223344
//*p表示p是一个指针变量,int表示p所指向的类型为整形

    return 0;
}

2.指针类型

    指针类型有很多,笔者作为初学者只列举几种最简单的类型。

1.int*p; //把声明指针的名称p去掉,该指针的指针类型就是int*
2char*pa;//同理把pa去掉,该指针的指针类型就是char*
3int**paa//同理把paa去掉,该指针的指针类型就是int**

//指针类型可以理解为去掉指针名称所剩下的声明部分

3.指针所指向的类型

  作为初学者,此处只简单讲讲几句。其实,指针所指向的类型就是指针声明去掉指针名称及其左边第一的*所剩下的部分,以下是举例:

1.int*p;//去掉*p,指针声明剩下int,说明该指针所指向的类型是int,是整形;
2.char*p;//去掉*p,剩下char,说明该指针所指向的类型是char,是字符型;
3.int**p;//去掉*p,剩下int*,说明该指针所指向的类型是int*,依旧是个指针类型;

 4.野指针

  野指针是指指向不明的指针,其所指向的地址与解引用访问都是随机的。由于野指针对程序有危害性,所以应该尽力回避。产生野指针的原因有很多,以下分三种;

//没有初始化
int main()
{

int*p;
//这种情况下,p右边没有具体赋值,没有初始化,因此p是野指针
//p访问的地址是随机的,一般情况下编译器会报错

return 0;
}
//越界访问
int main()
{
	int arr[10] = {0};
	int*p = arr;
	int i = 0;

	for (i = 0; i <= 10; i++)
	{

		*(p++)= i;

	}
	return 0;
}
//在这种情况下,编译器会出现报错。
//因为当i=10时,p已经时srr数组中最后一个元素之外的地址,*p属于越界访问数组之外的地址
//指针所指向的空间释放
int*test()
{   
	//程序执行进入test()函数后,系统为变量a开辟空间,同时a是临时变量
	int a = 10;
	//a的地址也是临时地址,程序执行离开test()函数后会自动销毁
	return &a;
}
//所以test()的返回值是一个已经销毁的地址
int main()
{
	int*p = test();
	printf("%d", *p);
	//此时p去访问一个被销毁的地址,如果这个地址已经重新分配,编译器会自动报错
	//这就是因临时变量的地址被销毁而产生的野指针
	return 0;
}

5.指针的空置

    当我们不再想用某个指针时,我们可以把它赋值为空指针。

int main()
{
  int a=10;
  int*p=&a;
  p=NULL;
  //当我们不再想使用某个指针时,可以将它改为p=NULL
  *p=20;
  //此时若再次访问p时,编译器会报错,说明p已被设置为空指针
  return 0;
}

6.指针与整数的加减运算

    指针是地址,而地址是以编码表示的,编码是一串二进制数或十六进制数,可见指针是有相应的整数数值的,因而可以与整数进行加减运算

int main()
{
	int a = 0;
	int*p = &a;
	int**pa = &p;
	printf("%d\n", sizeof(int));//4
	printf("%d\n", sizeof(int*));//4
	printf("%p\n", p);// 0019FA20
	printf("%p\n", p + 1);//0019FA24
	printf("%p\n", pa);//0019FA14
	printf("%p\n", pa + 1);//0019FA18
	char b = 'c';
	char*ph = &b;
	printf("%d\n", sizeof(char));//1
	printf("%p\n", ph);//00D1F877
	printf("%p\n", ph + 1);//00D1F878
	return 0;

	//由上面数值可见,指针的整数运算后的值等于原地址加上或减去n*sizeof(指针所指向的类型)
	//其中n是指针所加或减的整数
}

7.指针间的减法运算

     两个所指向类型相同的指针可以进行减法运算

int main()
{

	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int sz = &arr[9] - &arr[0];
	int i = 0;
	for (i = 0; i <= 9; i++)
	{
		printf("&arr[%d]=%p\n", i, &arr[i]);

	}
	printf("%d\n", sz);//9

	return 0;
}

//指针间的减法运算的结果等于指针1的地址减去指针2的地址的值除以sizeof(指针所指向的类型)

8.指针的关系运算

int main()
{
	int arr[10] = {0};
	int i = 0;
	int*vs;
	for (vs = &arr[9]; vs >= &arr[0]; vs--)
	{

		*vs = 1;

	}
		
	//arr数组的值为 1 1 1 1 1 1 1 1 1 1 
	//可见指针间可以进行大小关系比较
	return 0;
}

9.指针与数组

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int*p = arr;
	int*p2 = &arr;
	int*p3 = arr + 1;
	int*p4 = &arr + 1;
	    printf("%p\n", p); //012FFC5C
		printf("%p\n", p2);// 012FFC5C
		printf("%p\n", p3); //012FFC60
		printf("%p\n", p4); //012FFC84

		//可见arr单独表示时是arr数组首元素的地址,arr前面加&表示的是整个数组(sizeof(数组名),此时数组名也代表整个数组)
		//其他情况,arr都代表数组首元素的地址
		//arr+1表示数组首元素后第一的元素的地址
		//&arr+1表示数组后面第一个内存单元的地址
		//由于arr数组中有10个整形元素,所以&arr+1跳过40个字节
	return 0;
}

10.二级指针

int main()
{
	int a = 0;
	int*pa = &a;
	int**paa = &pa;

	//二级指针指所指向的类型为一级指针的指针
	//由于指针是变量所以系统也会开辟空间存储这个变量,而这个变量的赋值就a的地址

	return 0;
}

11.指针数组

   指针数组不言而喻就是数组,只不过这些数组的内容是指针


int main()
{
	int a = 10;
	int b = 20;
	int*arr[] = { &a, &b };

	printf("%p %p", arr[0], arr[1]);
	            //008FFB40   008FFB34
    //创建指针数组的方式是在数组名前加指针类型
    //数组内容是地址



	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值