C语言指针

1.指针的概念:

       指针是一种数据类型,它存储了另一个变量的内存地址。通过指针,程序可以直接访问和操作内存中的值。

&:取地址运算符                %p:取地址运算符

	int a = 10;
	//&:取地址运算符    %p:取16进制地址
	printf("a的十进制地址 = %d\n", &a);
	printf("a的16进制地址 = %p\n", &a);
	printf("\n");

	return 0;

运行结果:

2.定义一个指针变量 

int* p;

 3.如何给指针变量初始化?

	char* pc = &c;
	//*:取值	&:取地址
	printf("*pc = %c\n", *pc);
	printf("pc的十进制地址 = %d\n", pc);
	printf("a的16进制地址 = %p\n", pc);

 运行结果:

4.定义一个空指针 

	int* pnull = NULL;
	printf("pnull = %d\n", pnull);

 运行结果:

5.间接访问 

       间接访问是指不直接通过变量名或数据本身来访问数据,而是通过一个或多个中间层(如指针、引用等)来访问。

*:间接访问运算符

	int b = 20;
	int* pb = &b;
	printf("*pb = %d\n", *pb);
	printf("\n");
	//*pb <==> b

 可以给*pb重新赋值

	*pb = 30;
	printf("*pb = %d\n", *pb);
	printf("\n");

 5.1 空指针:不能进行间接访问

	int* pnull = NULL;
	printf("pnull = %d\n", *pnull);

 直接报错:

一般用if(pnull !=NULL)进行判断 

	int* pnull = NULL;
	if(pnull != NULL)
		printf("pnull = %d\n", *pnull);
	else
		printf("这是一个空指针\n");

5.2 非法指针:不能进行间接访问

非法指针是指向错误内存地址的指针,也称“野指针

简单来说,就是没有被初始化的指针

    int* pd;
    printf("*pc=%d\n", *pd);

直接报错

Run-Time Check Failure #3 - The variable 'pd' is being used without being initialized.

意思是:运行时检查失败#3 -未初始化就使用了变量` pd `。 

6. 在指针中,&与 * 的使用

6.1 &的使用(升级)

    int a;  //定义一个int型变量
    int* b; //定义一个int*型变量

已知a的类型是int型

那在a的前面加一个&号,&a的类型就是int*型,这个用通俗的话就叫做升级

以此类推

已知b的类型是int*型

那在b的前面加一个&号,&b的类型就是int**型

-------------------------------------------------------------------------

当然&a的意思是取a的地址,即&a就是一个常量。

所以&&a这种写法是错误的。

6.2 * 的使用(降级)

    int* pint;  //定义一个int*型变量
    int** ppint; //定义一个int**型变量

已知pint的类型是int*型

那在pint的前面加一个 * 号,*pint的类型就是int型,这个用通俗的话就叫做降级

以此类推

已知ppint的类型是int**型

那在ppint的前面加一个 * 号,*ppint的类型就是int*型

加两个 * 号,**ppint的类型就是int型

--------------------------------------------------------------------------

当然,如果变量本身就是int型,那就不能在这个变量前面加 * 号

因为 * 的操作数必须是指针,而int型变量不是一个指针

用通俗的话说就是无级可降。

 *&b = ?      

*&b先升级后降级,*和&相互抵消,所以 *&b <==> b

    int a = 30;
    printf("%d", *&a);

运行结果:

7.二级指针

 二级指针:指向一级指针的指针
 二级指针存储的是一级指针的地址

	int b = 30;
	int* pb = &b;  //*pb <=> b
    printf("pb的十进制地址是:%d\n", &pb);
	printf("pb的十六进制地址是:%p\n", &pb);
    //定义一个二级指针
	int** ppb = &pb;
	printf("**ppb = %d\n", **ppb);   //**ppb <=> *pb	 
	printf("ppb的十进制地址是:%d\n", ppb);
	printf("ppb的十六进制地址是:%p\n", ppb);

 运行结果:

使用二级指针来修改整数的值

	//b = 100;          //*pb = b
	//*pb = 100;	    //*ppb = pb
	**ppb = 100;		//**ppb = *(*ppb) = *pb = b
	printf("b = %d\n", b);

运行结果:

8.多级指针的定义 

	//以此类推  多级指针的定义
	//三级指针
	int*** pppb = &ppb;
	//四级指针
	int**** ppppb = &pppb;

9.指针所占内存大小

16位机器的代码时,指针占2个字节。
32位机器的代码时,指针占4个字节。
64位机器的代码时,指针占8个字节。

	int* pint;
	char* pchar;
	double* pdouble;
	printf("pint的内存大小:%d\n", sizeof(pint));
	printf("pchar的内存大小:%d\n", sizeof(pchar));
	printf("pdouble的内存大小:%d\n", sizeof(pdouble));
	printf("pppb的内存大小:%d\n", sizeof(pppb));

运行结果:

        

 10.指针与数组的关系

数组是自带地址的。‌

        数组名实际上代表了数组元素存储的内存地址的首地址,‌这意味着当你声明一个数组时,‌它自动获得了与之关联的内存地址。‌因此,‌数组名本身就是指向数组首元素地址的指针,‌这表明数组确实自带地址。‌此外,‌数组首元素地址与数组地址的值相等,‌进一步证明了数组确实有一个固定的内存地址与其相关联。‌

int arr[] = { 0 };//定义一个整型数组

 数组名arr是一个int*型常量,使用不能作为左值。

 10.1 指针与一维数组的关系

	int ShuZu1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("ShuZu1 + 0=%d\n",ShuZu1 + 0);
	printf("&ShuZu1p[0]=%d\n", &ShuZu1[0]);
	printf("ShuZu1 + 1=%d\n", ShuZu1 + 1);
	printf("&ShuZu1p[1]=%d\n", &ShuZu1[1]);
	printf("ShuZu1 + 9=%d\n", ShuZu1 + 9);
	printf("&ShuZu1p[9]=%d\n", &ShuZu1[9]);
    putchar(10);
    printf("*(ShuZu1 + 0)=%d\n", *(ShuZu1 + 0));
	printf("ShuZu1p[0]=%d\n", ShuZu1[0]);
	printf("*(ShuZu1 + 1)=%d\n", *(ShuZu1 + 1));
	printf("ShuZu1p[1]=%d\n", ShuZu1[1]);
	printf("*(ShuZu1 + 9)=%d\n", *(ShuZu1 + 9));
	printf("ShuZu1p[9]=%d\n", ShuZu1[9]);

运行结果:

                  

 由运行结果可得这样的关系:

        ShuZu1 + i = &ShuZu[ i ]                                             *(ShuZu1 + i) = ShuZu[ i ]

 结合下面的定义,*parr++、*++parr 、*arr++、*++arr 分别代表什么?

int arr[10];//定义一个int型数组
int* parr;  //定义一个int* 型变量  <==>  定义一个一级指针

首先,由如下的网址可知

运算符优先级_百度百科

 * 号和 ++ 的优先级是一样的,并且结合的方向是从右到左

*parr++:

  • ①parr++是后置++,会先返回pa原来的值,即arr[0]的地址,再对parr自增,此时parr中存储的是arr[1]的地址。
  • ②有第①点可得:parr++返回的值为arr[0]的地址,所以此时*是与arr[0]的地址结合,访问arr[0]的空间。但是parr中存储的是arr[1]的地址。

代码验证:

	int arr[3] = { 0,1,2 };
	//打印arr数组所以元素的地址
	printf("%p %p %p\n", arr, arr + 1, arr + 2);
	int* pint = arr; //将数组名arr赋值给pint指针
	int x = *(pint++);
	printf("x = %d  *pint = %p\n", x, pint);

运行结果:

*++parr :

  • ①++parr是前置++,会先将parr自增,此时parr中存储的是arr[1]的地址,再返回parr中的值,即返回的是arr[1]的地址。
  • 由第①点可得:++parr返回的是arr[1]的地址,所以此时*与arr[1]的地址结合,访问arr[1]的空间。

代码验证:

	int arr[3] = { 0,1,2 };
	//打印arr数组所以元素的地址
	printf("%p %p %p\n", arr, arr + 1, arr + 2);
	int* pint = arr; //将数组名arr赋值给pint指针
	int y = *++pint;
	printf("y = %d  *pint = %p\n", y, pint);

运行结果:

*arr++、*++arr

先计算arr++,++arr,但是arr是int*型常量,常量不可作为左值

10.2 指针与二维数组的关系

	int ShuZu2[4][5] = {
							{ 1, 2, 3, 4, 5},
							{ 6, 7, 8, 9,10},
							{11,12,13,14,15},
							{16,17,18,19,20}
					   };
	//推导
	printf("&ShuZu2[0][1] = %d\n", &ShuZu2[0][1]);
	printf("*(ShuZu2 + 0) + 1 = %d\n", *(ShuZu2 + 0) + 1);
	printf("&ShuZu2[1][2] = %d\n", &ShuZu2[1][2]);
	printf("*(ShuZu2 + 1) + 2 = %d\n", *(ShuZu2 + 1) + 2);
	printf("&ShuZu2[2][3] = %d\n", &ShuZu2[2][3]);
	printf("*(ShuZu2 + 2) + 3 = %d\n", *(ShuZu2 + 2) + 3);
	putchar(10);
	printf("ShuZu2[0][1] = %d\n", ShuZu2[0][1]);
	printf("*(*(ShuZu2 + 0) + 1) = %d\n", *(*(ShuZu2 + 0) + 1));
	printf("ShuZu2[1][2] = %d\n", ShuZu2[1][2]);
	printf("*(*(ShuZu2 + 1) + 2) = %d\n", *(*(ShuZu2 + 1) + 2));
	printf("ShuZu2[2][3] = %d\n", ShuZu2[2][3]);
	printf("*(*(ShuZu2 + 1) + 2) = %d\n", *(*(ShuZu2 + 2) + 3));
	putchar(10);

运行结果:

  由运行结果可得这样的关系:

*(ShuZu2 + i) + j <=> &ShuZu2[i][j]                            *(*(ShuZu2 + i) + j) <=> ShuZu2[i][j]

根据一维数组的关系可得:

ShuZu2[i] + j <=> &ShuZu2[i][j]                                  *(ShuZu2[i] + j) <=> ShuZu2[i][j]

说到二维数组,那我们复习一下怎么遍历二维数组吧 

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 5; j++)
		{
            //printf("%2d ", ShuZu2[i][j]);
			printf("%2d ", *(*(ShuZu2 + i) + j));
		}
		printf("\n");
	}

11.指针与字符串的关系

输出字符串的首地址,指针

	printf("%d\n", "IliveYou");//输出字符串的首地址,指针

这个指针类型为:char * 型

可以理解为是一个char型数组,但是char*型变量存储的是常量字符串,是只读的。

所以不能修改char*中存储字符串的任意一个字符的值

关系: 

	char* ZiFuCuan = "I love you";
	printf("ZiFuCuan = %s\n", ZiFuCuan);
	printf("ZiFuCuan + 0 = %d\n", ZiFuCuan + 0);
	printf("&ZiFuCuan[0] = %d\n", &ZiFuCuan[0]);
	printf("ZiFuCuan + 2 = %d\n", ZiFuCuan + 2);
	printf("&ZiFuCuan[2] = %d\n", &ZiFuCuan[2]);
	putchar(10);
	printf("%c\n", ZiFuCuan[0]);
	printf("%c\n", *(ZiFuCuan + 0));
	printf("%c\n", ZiFuCuan[2]);
	printf("%c\n", *(ZiFuCuan + 2));

 运行结果:

                   

        ZiFuCuan + i = &ZiFuCuan[ i ]                                   *(ZiFuCuan + i) = ZiFuCuan[ i ] 

与一维数组同理

12.指针数组与数组指针的区别

指针数组:是一个数组,存的全部是指针

    int* ShuZU3[4] = { &b,&pb,&ppb,NULL };

数组指针:是一个指针,可看成一个一维数组   

 数组的首地址,就是一个数组指针

    int ShuZU4[4] = { 1,2,3,4 };//&ShuZu4[0]就是一个数组指针

定义一个数组指针 

    int (*p)[4];

13.函数指针与指针函数的区别 

函数指针:

指针函数:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值