C语言指针基础与深入

目录

1.指针定义

1.1指针定义

1.2'*'的含义

1.3'&'的含义

1.4'*'与'&'的联用,'*&'、'&*'是否相同

1.5'[ ]'的解引用功能

2.指针大小

3.可以用指针进行的操作

3.1访问变量

3.2函数传参

4.类型对指针变量起到的两个作用

4.1解析存储单元的大小

4.2指针变量加整型的能力


1.指针定义

1.1指针定义

        指针:地址的别名,本身存放着所对应的变量的地址。通过对指针的解引用(*指针名),可以找到该地址存放的变量。

int a=5,b=0;

int *p=&a;    //一级指针定义,以变量a的地址对其进行初始化
int *q=&b;
int **t=&p;    //二级指针定义,在一级指针基础上,
               //以指针变量p的地址对其进行初始化
//p==&a;*p==a;
//*t==p;**t==*p==a;

       1) 可通过 p=&b,对指针p所存放的地址,也就是p的指向进行修改。此时,*p==0。

       2) 二级指针也可以通过t=&q(指向另一个一级指针)、*t=&b(改变它所指向的一级指针(p)(*s==p)的指向)改变指向。二级指针需解两次引用(**t)。

int a = 5, b = 0;

int* p = &a;
int* q = &b;
int** t = &p;	

printf("**t = %d,*q = %d\n", **t, *q);

t = &q;    
*t = &a;    

printf("**t = %d,*q = %d\n", **t, *q);

1.2'*'的含义

        1)指针的定义:类型*  指针变量名

        2)解引用:*指针变量名

        对一级指针解一次引用,可以直接访问其指向的变量,即*p==a。

        对二级指针解一次引用,可以找到其指向的一级指针,即*s==p;对二级指针解两次引用,可以找到其一级指针指向的变量,即**s==*p==a。

        3)乘法:数据*数据

1.3'&'的含义

        1)取地址:指针变量其本身存放的是其指向变量的地址,所以用int* p = &a;进行指针初始化,使用p = &b;进行指针指向修改的操作。

        2)按位与运算

1.4'*'与'&'的联用,'*&'、'&*'是否相同

         p==&a;p存放的变量a的地址,所以打印出来p为一长串数字,就是在我本地运行中·变量a的存放地址。

        1)*&a可以理解为*(&a)&a为取变量a的地址(等价于p),*&a就等价于*p,对p解引用可以访问的p所指向的变量即a(*p==a),所以*&a等价于a。

        2)*&p可以理解为*(&p)&p为取指针变量p的地址(等价于s),*&p就等价于*s,对二级指针s解一次引用就找到了s所指向的一级指针p(*s==p),所以*&p等价于p

        3)*&p可以理解为&(*p)*p为对一级指针p解引用,可以访问的p所指向的变量即a(*p==a),&*p就等价于&a&a为取变量a的地址(等价于p),所以&*p等价于p

        综上,*&就等价于&*,且*与&互相抵消。

1.5'[ ]'的解引用功能

        对于数组名arr,只有当其与数组arr的定义在同一作用域,使用sizeof(arr)时,arr代表整个数组的大小。其他时候arr为数组的起始地址,也是数组首元素的地址。我们通常用arr[i](i为数组元素下标)来访问数组元素。当我们用指针指向数组首地址后(int *p=arr),就可以使用*(p+i)来访问数组元素。

	int arr[] = { 1,2,3,4,5,6 };
	int* p = arr;
	int len = sizeof(arr) / sizeof(arr[0]);//数组长度

	for (int i = 0; i < len; i++) {
		printf("arr[%d] = %d\n", i, *(p + i));
	}
	printf("\n");
	for (int i = 0; i < len; i++) {
		printf("arr[%d] = %d\n", i, p[i]);
	}

         但是,我们发现使用p[i]也可以访问数组元素,且*(p+i)等价于p[i]。所以,[]也具有解引用功能。

2.指针大小

        指针大小指针类型(如int*,char*,....)、指针级别(一级,二级,...)无关只和操作系统(OS)有关。

        在x86(32 bit)系统上,指针大小为4字节。在x64(64 bit)系统上,指针大小为8字节。

3.可以用指针进行的操作

3.1访问变量

	int a = 10, b = 20;
	int* p = &a;
	printf("*p = %d\n", *p);   //1

	*p = 5;                    //2
	printf("a = %d\n", a);

	p = &b;                    //3
	printf("*p = %d\n", *p);

        1)通过指针解引用,访问指针所指向的变量。*p==a==10。

        2)通过对指针解引用进行赋值修改,达到修改指针所指向的变量的目的。*p = 5等价于a = 5。

        3)通过对指针所存地址进行修改,改变指针的指向。p = &b,意为将p原本所存的值,即a的地址,修改为b的地址。再对p进行解引用就访问到了变量b的值。

3.2函数传参

        若想要达到无需返回值,使形参的改变,影响实参的值,就必须要:

        1)将实参地址作为指针传入。

        2)对指针解引用,访问到其存放地址所指向的变量值,再进行想要的操作。

如使用无返回值交换函数Swap,实现a、b值的交换。

void Swap(int* p, int* q) {
	int temp = *p;
	*p = *q;
	*q = temp;
}
int main() {
	int a = 10, b = 20;

	Swap(&a, &b);
	printf("a = %d,b = %d\n", a, b);
}

        在主函数中,调用Swap函数,将a、b地址作为实参传入,相当于以a、b地址分别对指针p、q 进行初始化(int* p = &a;int* q = &b)。

        1)temp = *p;如图中箭头1,通过*p将a的值赋值给temp。

        2)*p = *q;如图中箭头2,通过*q寻址到变量b,获取到b的值,再通过*p寻址到变量a,将a的值赋值为b的值(*p = *q等价于a = b)。

        3)*q = temp;如图中箭头3,通过*q寻址到变量b,将b的值赋值为temp,即a的值。

        以上即为通过对指针形参的操作,影响了实参的值,达到了无需返回值的目的。

4.类型对指针变量起到的两个作用

4.1解析存储单元的大小

        在同一操作系统上,虽然各类指针的大小相同,但其所解析的存储单元的大小因指针类型而不同。如char* 的解析单元大小为1字节,int*的解析单元大小为4字节,double*的解析单元大小为8字节等。

        以一维数组为例。因为Windows系统中采用小端数据存储模式(即低位数据存放在低地址位置),并且数组元素地址在内存中由低到高,且连续。所以数组int arr[]={1,2,3,4};在内存中的表示形式如下:(数据以十六进制表示,如1-->0x0000 0001)

	int arr[] = { 1,2,3,4 };
	int* p = arr;

	printf("%d\n", *p);
	printf("%d\n", *(p + 1));

        若使p指向数组首地址,使用*p就可以访问数组的首元素(即*p==arr[0]),且*(p+1)== arr[1]。如下图所示:

        此时,*p 等价于arr[0],*(p+1) 等价于arr[1]。

	int arr[] = { 1,2,3,4 };
	int* p = arr;

	char* q = (char*)p;  //指针强转

	printf("%d\n", *q);
	printf("%d\n", *(q + 1));
    printf("%d\n", *(q + 12));

         若将int*型指针p强制转换为char*型指针,则其解析存储单元能力也从4字节变为1字节。

  

 

         此时,*q还等于1,但*(q+1) 等于0,*(q+4)才等于2。

         综上,我们可以推导出:*p读取了0x100到0x103共4字节地址上的数据,*(p+1)读取了0x104到0x107共4字节地址上的数据,*q读取了0x100上1字节地址上的数据,*(q+1)读取了0x101上1字节地址上的数据,*(q+4)读取了0x104上1字节地址上的数据。所以int*类型的指针解析存储单元能力为4字节,char*类型的指针解析存储单元的能力为1字节。

4.2指针变量加整型的能力

        同样以一维数组int arr[]={1,2,3,4};为例,使int*型指针p指向其首地址。则p+1指向了下一个单元格的地址。将p,p+1所指向的地址分别打印出:

	int arr[] = { 1,2,3,4 };
	int* p = arr;

	printf("p = %x\n", p);    //p存放的地址,以十六进制打印处
	printf("p + 1 =%x\n", p + 1);
	printf("p + 2 = %x\n", p + 2);

   

        我们可以发现地址之间相差4字节大小。所以int*型指针加整型1时会向后(高地址)偏移4字节位置,加整型2时会向后(高地址)偏移8字节位置,所以int*型指针加整型n时会向后偏移4*n个字节位置。又因为int型数据大小为4字节,所以int*型指针加整型n时会向后偏移sizeof(int)*n个字节位置

        由此,我们思考char*型指针加整型n后会不会也向后偏移n*sizeof(char)个字节位置?

验证:

	char arr[] = { 1,2,3,4 };
	char* p = arr;

	printf("p = %x\n", p);
	printf("p + 1 = %x\n", p + 1);
	printf("p + 2 = %x\n", p + 2);

        由此,我们发现char*型指针加整型n确实会向后偏移n*1(即n*sizeof(char))字节位置。 

        综上,我们可以得出结论:类型* 型指针,其加整型的能力为,地址向后偏移sizeof(类型)*整型个字节。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值