指针篇之七 看我七十二变

    指针优点之一是能灵活读写内存,实现这一功能主要依靠灵活多变的类型转换,重复这里类型指指针所指向的内存的类型。通过改变编译器对“被指向内存”的解读方式实现对内存中数据的灵活转换,这是指针的精髓应用之一。

    指针本身存储的值被编译器当作一个地址,这个值的类型,即指针所指内存块的类型尤为重要,”a pointer make no sense if it has no type definition for the pointed memory segment”比如void *p=malloc(100); 这个定义只能说明p是一个指针,它的值是一段内存的地址,但由于类型是void,编译器不知道怎样解析p所指内存中的数值,为什么?假设p指向地址0x1000,在0x1000这个内存位置按字节依次存放数据,如下:

 

    此时如果试图用*p去取指针p所指内存的值,问题就出现了:p=0x1000,这个0x1000代表内存起始地址,那么*p=0x11?还是2字节的0x1122?或4字节的0x11223344?如果取0x11223344,这个值怎么解释?有符号or无符号?整型or浮点型?很明显还缺少一个因素。

    所以完整指针定义必须包含另一个限定,用来解决取多长,取出的数据代表什么或者说编译器怎么解释它的问题。这就是指针类型定义(简化称呼)所起的作用,也就是指针定义时*号之前的类型。比如把之前定义改为char *p,就可以确定*p等于0x11char1字节长度),如果定义为unsigned short *p,会得到*p = 0x1122

    计算机内存中只有二进制数,没有整型、浮点、字符等的区别,二进制数的具体含义取决于编译器根据内存类型的翻译解读,同一段内存里的二进制数用不同的类型“解释”,意义千差万别,据此可引出指针的一个重要技巧:指针指向内存的强制类型转换。下面是有关的两道C考题:

1:完成一个C函数,利用指针的某功能特性检测CPUEndian属性,Big_endian返回0Little_endian返回1。答案:

    int checkCPU( )

    {

      int a =0x1234abcd;

      char *b;

      b=char *&a;                //强制转换

      if(0x12 == (*b))      return 0;        //Big-endian

      else if(0xcd == (*b))   return 1;    //Little-endian

    }

    分析:Endian是硬件概念。Little-endianCPU内存中多字节数从低字节到高字节存放,而Big-endian是从高字节到低字节。以上代码利用指针类型变换,改变对指针所指目标内存的“解读”方式,从而判断数据在内存中的实际存放顺序。

2:用一句话让程序跳转到绝对地址0x1000处去执行。

     答案:*((void (*)( ))0x1000 ) ( );

    首先把0x1000强制转换成函数指针,即:(void (*)())0x1000,然后调用此函数: *((void (*)())0x1000)()。用typedef方式可能更容易看懂:

    typedef void(*)() FuncPtr;

    *((FuncPtr)0x1000)();

    一个普通地址值硬是变成函数入口地址,这就是指针强制类型转换的威力,前提是完全掌握各种变量的存储形式,比如上例事先必须确认地址0x1000中存放着合法的程序指令,否则就会”illegal instruction exception”了。

    这两个例子再次呼应说明:访问指针所指内存区,指针指向的那块内存的类型决定编译器把那块内存里的内容当做什么来操作凡事有利必有弊,类型转换让指针可以千变万化,灵活使用,同时也带来了很多问题。

指针指向内存的类型转换与普通变量强制类型转换的区别

    指针指向内存的强制类型转换,并不做内存拷贝,只是对内存数值重新解读;而变量强制类型转换会构建一个不同类型的新变量,把原来的数“加工转换”后赋给新变量。前者是用新类型解读内存中的二进制数,后者则是用新类型转换旧类型。运行下面代码,从中对比可以看出两种转换的不同:

    void main()

    {

      float f = 1.25;

      printf(“%d\n”, *(int *)&f);  //重新解读f所在的内存,指针类型转换

      printf(%d\n”, (int)f);       //转换f变量本身,变量类型转换

    }

指针变换的安全性

    指针变换并不能随心所欲,例如:

    unsigned char ca=’a’;

    int *pa;

    pa=(int*)&ca;

    *pa=1298

    指针pa的定义int *pa代表pa指向int型,所指内存占4字节,但之后pa又被指向unsigned char型的ca,而ca只有一字节。因此最后一句会发生错误,它不但改写ca所在的一字节内存,还将改变相邻的另外三个字节。也许这三字节里原本存储着重要数据,意外改变将带来崩溃性错误。这就是典型的错误指针类型变换导致内存越界访问。

    因此类似ptr1=(TYPE*)ptr2这种指针强制类型转换,如果ptr2指向的内存类型长度大于ptr1所指的类型,那么用指针ptr1访问ptr2所指的内存就是安全的,反之会越界。

指针类型不匹配导致错误的隐式指针类型变换

    在赋值以及函数参数传递时如果对应的指针类型不匹配,且编译器设置不严格就会自动隐式的变换指针类型,这往往会导致读写错误。例如:

   void main()

    {

      int i;

      char ca;

      for(i = 0; i < 5; i++)

      {

        scanf("%d", &ca);

        printf("%d", i);

      }

    }

    程序用于从标准输入中读五个数并向标准输出写入0 1 2 3 4。但某些环境下,会输出类似0 0 0 0 0 1 2 3 4等很奇怪的数值。

    scanf(%d)需要一个指向整型的指针,而&ca指向char型,scanf()并不知道其参数类型是否匹配,它仍然把&ca看成指向整型的指针并试图向其中写入一个整数。由于整数比字符占用更多内存,这就影响到ca的邻近。ca位于栈中,邻近很可能是紧挨着的局部变量i。这样每次c读入一个值,i就被部分覆盖,于是导致输出奇怪的数。所以函数传递指针参数时,实参和形参类型必须匹配,以避免隐含的错误指针类型变换。

总结指针指向的内存类型是指针最重要的属性,自如的运用指针类型变换,才能初步体会到C相对其他语言的优势。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C知道:非常高兴能为您解答有关C++指针的问题!C++中的指针是一个重要的概念,它可以让我们直接访问内存地址,提高程序的灵活性和效率。以下是关于C++指针的一些详细讲解: 1. 指针的定义和声明:在C++中,我们可以通过使用`*`符号来声明一个指针变量。例如,`int* ptr;`声明了一个名为`ptr`的指向整数的指针变量。我们还可以使用`&`符号获取一个变量的地址,并将其赋值给指针变量。例如,`int num = 10; int* ptr = &num;`将`ptr`指向`num`的地址。 2. 指针的解引用:使用`*`符号可以将指针解引用,即访问指针所指向的内存位置的值。例如,如果`ptr`是一个指向整数的指针,那么`*ptr`将给出该位置的值。 3. 动态内存分配:C++还提供了动态内存分配的能力,使用关键字`new`来动态分配内存。例如,`int* ptr = new int;`将在堆上分配一个整数大小的内存,并将其地址赋值给`ptr`。记得在使用完毕后释放内存,使用关键字`delete`。例如,`delete ptr;`可以释放之前分配的内存。 4. 指针的算术运算:指针还可以进行算术运算。例如,`ptr++`将使指针`ptr`指向下一个相同类型的内存位置。这对于遍历数组或者访问连续内存块非常有用。 5. 指针与数组:指针和数组之间有着密切的关系。实际上,数组名本身就是一个指向数组首元素的指针。我们可以通过指针来访问和操作数组中的元素,例如`*(arr + i)`或者`ptr[i]`。 这只是C++指针概念的一个简要介绍,它们在C++编程中非常重要。如果您有任何进一步的问题,请随时提问!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值