top
友情提示
测试消息
确定
友情提示

处理中,请稍后...

指针操作时得注意的一些要点
通俗点讲,指针是内存地址的一个形象化描述,通过其本身可以访问以它自身为地址的内存单元。为方便理解,你可以将指针简单理解为指向某个内存空间的对象。那么,既然指针指向的是某个内存单元,通过它能够访问该内存空间也是自然的。

下面说一下几个与指针相关的值得注意的点
(1)指针的大小
(2)指针的自加和自减
(3)指针大小和一块内存的大小


指针的大小
指针的大小对于刚接触指针的人来说是一个很容易令人困惑的问题,其实指针的大小很容易理解。上面已经说过,指针及内存地址,那么容易想到在32位运行环境(32位编译器)下,地址的大小为4个字节,所以这时指针的大小为4个字节(与寻址空间相对应)。64位运行环境(64位编译器)下,地址大小为8个字节(与寻址空间相对应),这时指针大小为8个字节

说到这里,或许你还半信半疑。不要紧,敲个简单的例子就行了。以下是测试代码:
#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
    cout << sizeof(char *) << " " << sizeof(int *) << " " << sizeof(long *) << endl;
    return 0;
}

32位环境下你将看到如下输出:
4 4 4

64位环境下,你将看到以下输出:
8 8 8
这里简单说一下,如果你要测试以上代码,并且你当前的系统是64位的,那么你可以借助一些工具很轻松地完成32位和64位环境下的测试,比如Vsual Studio,你可以选择运行环境。如果你没有这样重量级的IDE,那么你可以借助Cygwin这样的工具,下载一个32位版本测试32位环境。如果你当前是32位的系统,那么你就找台64位系统的电脑测试吧

看完了上面的东西,下面说说关于指针大小的一些理解误区。很多初学者常犯的错误是:
(1)错误认为指针的大小取决于前面类型限定名的大小。一定要谨记定义指针的类型限定名只是影响指针指向的内存单元的数据长度,与指针的大小无关怎么理解呢?看下图:
有这样的语句:
#include <iostream>

using namespace std;

int main(int argc, char const *argv[])
{
 
    int x = 0x12345678; //小端序: 78 56 34 12
    cout << hex << (int)*((char*)&x) << endl; //输出 56 由于指针强转位char*, 所以访问数据长度为一个字节,即第一个字节
    cout << hex << (int)*((short*)&x) << endl; //输出 5678 由于指针强转位short*, 所以访问数据长度为两个字节,即第一和第二个字节

    return 0;
}

以上程序展示之前定义时限定符的作用效果。有关端序的内容这里不再赘述,请查看上一篇文章, 点此打开


指针的自加和自减
指针的自加自减考虑的点是一样的,或者说指针加减(如: p = p + 2)考虑的点是一样的,所以这里只讨论指针的加操作。
指针的加减操作每次的步长取决于定义指针时的类型限定。看下面的代码:
#include <stdio.h>

int main(int argc, char const *argv[])
{

    int x = 0x12345678;  //大部分常见的CPU都为小端序,包括当前测试用机:故内存中十六进制形式: 78 56 34 12
    char *p = (char*)&x;
    printf("0x%x\n", (int)*(++p));  //输出 0x56   ++p 一次跳了一个字节,即 sizeof(char) 个字节

    short *p1 = (short*)&x;
    printf("0x%x\n", (int)*((char*)(++p1)));  //输出 0x34   ++p1 一次跳了两个字节, 即 sizeof(short) 个字节
    
    return 0;
}
以上代码跑得很顺利,然而我们好像忘记了一个东西, 那就是 void。神一般地存在,用好了无所不能,用不好满地大坑。我们也常常称void*型指针为万能指针。它特殊在哪呢?我们考虑一下对其进行加减操作的步长是多少呢?将上面的代码简单修改一下:
#include <stdio.h>

int main(int argc, char const *argv[])
{
    int x = 0x12345678;  //大部分常见的CPU都为小端序,包括当前测试用机:故内存中十六进制形式: 78 56 34 12
    void *p = &x;   //变为void
    printf("%x\n", (int)*((char*)(++p)));  //输出 56     ++p 一次跳了一个字节,即 sizeof(void) 个字节
    printf("%d\n", sizeof(void));    //输出 1

    void *p1 = &x;   //变为void
    printf("%x\n", (int)*((char*)(p1 + 1)));  //输出 56    ++p1 一次跳了一个字节, 即 sizeof(void) 个字节
    return 0;
}
gcc编译运行输出结果:
56
1
56
貌似是正常的,其实不然。说void坑多肯定不是这么简单就完事的。其实void又被称为无类型或者不完整类型,那么它的大小就是一个谜,上述gcc编译运行输出其大小为 1 那么它的大小一定是1吗?换个编译器试试,先将其他注释,只测试 printf("%d\n", sizeof(void)); 用VisualStudio编译运行。结果输出为0。并且IDE会给出警告:不允许使用不完整类型。

接下来我们去掉其他注释,将代码用VisualStudio编译,编译失败,显示错误:void*:未知大小,表达式必须指向完整类型的指针。也就是说对void*型的指针进行加减操作是不合法的。

上述操作都是在C语言层面进行的,接下来换到C++:(为方便,这里就不改为C++标准输出了,只看编译结果)用 g++ 编译, 编译输出如下:
编译结果

警告内容
(1)sizeof()不能用于void
(2)ISO C++ 不允许对类型为‘void*’的指针自增 [-Wpointer-arith]
(3)‘void *’型指针用在了算术表达式中 [-Wpointer-arith]


虽然出现了警告,但是还是通过编译并正确运行。说了那么多,只为说明一点,如果你没有十足的把握避开那些坑,就不要用void*,哪怕其名为万能指针(开个玩笑 :))。总之,少用即可。


指针大小和一块内存的大小
这里不说啥高端的东西,只说一些常见的“考试题”。结合指针大小可能会出现以下题:
#include <stdio.h>
#include <stdlib.h>

/***************************************
       请给出以下程序的执行结果。
***************************************/
int main(int argc, char const *argv[])
{
    char *p = (char *)malloc(100);
    long *p1 = (long *)malloc(100 * sizeof(long));
    char arr[200];

    printf("%d  %d  %d\n", sizeof(p), sizeof(p1), sizeof(arr));
    return 0;
}

毫无疑问:
32位运行环境: 4  4  200
64位运行环境: 8  8  200


前两个sizeof()输出的都是指针大小(C++中int *p = new int[10] 亦是同等效果),而最后一个输出的是一个内存块的大小(字节数)。可见,虽然在传参的过程中传递的相当于指向数组首元素的指针,但是数组名并不是完全等同于指针,数组名总是指向数组首元素,记录数组内存开始的地方, 不能随意变更,这也就引出了另外一点:数组名不能作加减操作,如果有这方面的需求,可以用指针指向数组,然后利用指针完成相关操作。与数组相关的更多内容,可以查找其他文献看一下。

以上内容只是我个人的见解,如果您在阅读的过程中发现错误,欢迎留言。

【原创内容,转载请注明出处】
标签
指针
编程之趣
那年那坑
内存
上一篇:大端序和小端序
下一篇:不可不习的利器——正则表达式
文章阅读完了, 快到评论区留下你的看法吧!
Email:
验证码:
昵 称 :
评论列表
热门标签
今日倒计时
小时 分钟
年少不识曲中意,再听已是曲中人
加油吧骚年
文章分类
最新文章
浏览排行
最新留言
[2018/11/27]博客该更新了
[2018/10/25]网站链接以便更 www.qiuyegen.com
[2018/04/07]太难找了,找到了
[2018/03/21]Entry fileTemplates//Singleton.java.ft not found in C:/Dev/android-studio/lib/resources_en.jar 该错误对我们使用AS没有影响吗?为什么复制保存后重新创建一个新的之后这个错误又出现了?
[2018/01/23]815cce30b85eb6b7ee7930c09135fcf7好漂亮的博客空间啊
[2018/01/06]好漂亮的博客.......
[2017/12/26]试试水
[2017/12/21] 博主可以分享一下这个源码不, 很喜欢这个博客
[2017/11/20]你加我友链了????
[2017/11/20]你的alert 很不友好哈
Copyright © 2018 DevSONG . All rights reserved. 有疑问或者建议? 留言给我或者E-mail me
滇ICP备17002307号-3