指针中那些容易被忽视的小事

  一.sizeof和strlen求取字符数组和字符串常量

 1.整形数组:int a[ ]={1,2,3,4};

     
	printf("%d ",sizeof(a));  // 16 表示整个数组的字节数16=4*4
	printf("%d ",sizeof(a+0));// 4 首元素的地址
	printf("%d ",sizeof(*a)); // 4 第一个元素的字节数,a的类型是整形数组,它的元素也是整形,是4个字节
	printf("%d ",sizeof(a+1)); // 4 第二个元素的地址
	printf("%d ",sizeof(a[1]));// 4 第二个元素的字节数
	printf("%d ",sizeof(&a));  // 4 指向整个数组的地址,但是我们规定只要是个指针它就是4个字节
	printf("%d ",sizeof(&a+1)); // 4 &a表示指向整个数组的地址,&a+1表示跳过整个数组,指向数组的下一块空间
	printf("%d ",sizeof(&a[0]));// 4 表示第一个元素的地址
	printf("%d ",sizeof(&a[0]+1));// 4 表示第二个元素的地址
	printf("%d ",sizeof(*&a)); // 16 &a表示指向整个数组的地址,在解(*)应用说明求取整个数组的大小

 2.字符数组: char name[ ]="abcdef";

 
	printf("%d ",sizeof(name[0]));// 1 表示第一个元素的字节数
	printf("%d ",sizeof(&name));  // 4 表示指向整个数组的指针,一半情况下我们为指针分配4个字节
	printf("%d ",sizeof(*name));  // 1 同第一个,也是第一个元素的字节数 
	printf("%d ",sizeof(&name+1));/* 4 &name表示指向整个数组的指针,&name+1表示指向整个数组的下一块空间
	                              本质上依然是个指针,系统为它分配4个字节*/
	printf("%d ",sizeof(name+1)); // 4 表示b的地址,系统为它分配4个字节
	printf("%d ",sizeof(name));  // 7 求取整个数组的字节数,加上'\0'
	printf("%d ",strlen(name));  // 6 求取整个数组的大小,不加'\0'
	printf("%d ",strlen(&name)); // 6 &name代表指向整个数组的指针,遇到'\0'停止
	printf("%d ",strlen(&name+1)); /* X 随机值,&name代表指向整个数组的指针,&name+1代表指向数组的
	                                  下一块空间*/
	printf("%d ",strlen(name+1)); // 5 表示从开始测量数组大小

 3.字符串常量: char *name="abcdef";

	printf("%d ",sizeof(name[0])); // 1 表示第一个元素的字节数
	printf("%d ",sizeof(&name));  // 4 表示指向整个数组的指针,我们为指针分配4个字节
	printf("%d ",sizeof(*name));  // 1 表示第一个元素的字节数
	printf("%d ",sizeof(&name+1));/* 4 &name表示指向整个数组的指针,&name+1表示指向数组的下一块空间
	                                它的本质依然是一个指针*/
	printf("%d ",sizeof(name+1)); // 4 表示第二个元素的字节数
	printf("%d ",sizeof(name));  // 4 代表首元素的地址,4个字节
	printf("%d ",strlen(name)); // 6 表示指针指向的常量字符串的大小
	printf("%d ",strlen(&name)); /* X 随机值,因为name里面存放的是a的地址,而从name往后的空间里
								      遇到'\0'是未知的*/
	printf("%d ",strlen(&name+1)); // X &name+1表示name指向所有元素中的下一个位置,未知的
	printf("%d ",strlen(name+1)); // 5 表示从b开始求取字符串的大小

  二.指针中的左值和右值

    定义为:

                 char ch='a';

                 char *cp=&ch;

 1.++cp

    .++cp在.c文件不可以作为左值,但是在.cpp文件中可以作为左值,不管在.c还是.cpp文件中都可以作为右值

 2.cp++

   .cp++不管在.c还是.cpp文件下都不可以作为左值,但是可以作为右值

 3.*++cp;

   .可以作为左值也可以作为右值

   .*++cp,首先我们需要知道的是 ++优先于*,所以cp先与++结合,因为是前置加加(先加加再使用),所以cp先指向ch的下一块空间,在*(解)引用,当作为左值时指向ch的下一块空间,作为右值时指向下一块空间的内容

 4.*cp++=*(cp++)

  .可以作为左值也可以作为右值

  .首先这两种类型是一致的,cp先与++结合,但是这是后置加加(先使用再加加),所以cp与*结合,当该表达式作为左值时指向ch的这块空间,当作为右值时表示a.

 6.++*cp

  .不可以作为左值,可以作为右值

  .cp先与距离它最近的*结合,再加加,如果可以作为左值,*cp表示指向ch这块空间,再加加,由上述例子可知前置加加不可以作为左值,所以++*cp也不可以作为左值;当作为右值时指向的内容是b

 7.(*cp)++

  .不可以作为左值,可以作为右值

  .()使得cp先与*结合,指向ch这块空间,再与++结合,同样由上述例子可知后置加加也不可以作为左值,所以(*cp)++同样不可以作为左值;但是可以作为右值,当作为右值时表示a

三.指针数组和数组指针

【理论分析篇】

     int *arr[10];  //arr是指针数组

     int (*ptr)[10];  //ptr是数组指针

1.指针数组

       .在上例中int *arr[10]是指针数组,在C语言中,[]优先于*,所以arr先与[]结合这就说明它本身就是一个数组,不过它里面存储的元素类型是int *.

       .int *arr[3]={"text","abc","bcd"};

       .在初始化arr时,我们只能一个一个的赋值,因为arr是一个指针数组,它包含三个值,他们分别是arr[0],arr[1]和arr[2],且arr[0]指向字符串"text"的首地址,arr[1]指向字符串"abc"的首地址,arr[2]指向字符串"bcd"的首地址.

 2.数组指针

      .在上述例子中int (*ptr)[10]是数组指针,()改变了优先级使得ptr先与*结合,在本质上ptr是一个指针,在内存分配的时候我们知道任何指针我们都为它分配四个字节来存储它指向的内容,而ptr又不同于基本的指针,它指向的是一个整形的数组,这个数组的长度是10.

    两者的区别如下图:

  

【理论实践篇】

 1.我们先来看如下的一个例子:

   

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a[5]={'A','B','C','D'};
	char (*p3)[4]=&a;
	char (*p4)[5]=a;
	system("pause");
	return 0;
}


      .我们知道p3和p4都是数组指针,他们都是有能力指向一个整形数组的,可是对于p3还是p4他们的类型是char (*)[4]和char (*)[5],不管对他们进行赋值&a还是a都是不能编译通过的,除非类型转化,下面我们来看强制类型转化之后的代码.

   

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char a[5]={'A','B','C','D'};
	char (*p3)[4]=(char (*)[4])&a;
	char (*p4)[5]=(char (*)[5])a;
	system("pause");
	return 0;
}

        .在强制类型转化之后编译没有报错,可是如果p3+1,p4+1指向的是数组的哪块位置呢?

        .我们可以知道p3+1指向的是D之后的那块空间,因为p3赋值的是整个数组的地址,p3向后偏移就是偏移所有的数组元素;当然p4+1指向的就是B的空间啦!

 2.我们再来看可以可以更好的理解指针数组和数组指针区别的例子:

    已知结构体Test的大小是20个字节,假设p的地址为0x10000000,

#include<stdio.h>
#include<stdlib.h>
struct Test
{
	int Num;
	char *pcName;
	short cha[2];
	short sBa[4];
}*p;
int main()
{
	printf("%p\n",p+0x1);
	printf("%p\n",(unsigned long)p+0x1);
	printf("%p\n",(unsigned int *)p+0x1);
	system("pause");
	return 0;
}

  结果为:

   0x10000014
   0x10000001
   0x10000004

     .p+0x1

     .我们知道p是结构体类型的指针,它有点类型于刚才的数组指针p3+1,p向后偏移1说明偏移了一个结构体类型,而结构体的大小是20个字节,就是p在原来的地址基础上向后便宜了20个字节,转化成16进制就是加14;

    .(unsigned long)p+0x1

    .在这里我们把结构体类型的指针强制类型转化成unsigned long   类型的相当于把一个结构体类型的指针强制类型转化unsigned long类型的数字,向后偏移一个就是在原来的基础上加1.

    .(unsigned int *)p+0x1

    .在这里我们应用了强制类型转换,我们把结构体类型的指针p强制转换成unsigned int *类型,我们知道只要是指针,内存就给它分配四个字节,所以当强制类型转换之后的p偏移1时,p在原来地址的基础上偏移4个字节.
    指针数组和数组指针对于初学者来说很难理解,但是只要你认真思考把握理解物质的本质就会变得很好理解啦!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值