zoukankan      html  css  js  c++  java
  • 字符串常量初始化指针

    今天写个小文说一说字符串地址和字符串常量。

    在C/C++中,一个字符串常量表示的是该字符串第一个元素的地址,就跟char指针名,char数组名表示的是字符串第一个元素的地址一样。

    想要打印一个地址,用一个简单的 cout << 地址; 语句就可以搞定;

    但是下面这两条语句将打印整个字符串

    char a[20] = "1234";
    cout << a << endl;
    
    char *p = a;
    cout << p << endl;

    这也是字符数组 与其他数组不同的一个地方,那么该如何得到该字符串的地址呢?

    下面有两种方法可供参考

    cout << (int*)a << endl;
    cout << &a << endl;

    这两种方法都可以正确打印出“字符串的地址”,但是有细微区别之处

    在字符数组a中,a表示第一个字符的地址,a+1表示第二个字符的地址;

    在第一条打印地址的语句中,(int*)a只是起到了一个强制类型转换的作用,换句话说,a表示第一个字符的地址,但是cout <<a;输出的是整个字符串,这是因为这个地址是char*类型的,cout识别到char*类型的地址将会自动打印从该地址指向的空间开始直到遇到'\0'的字符串内容,所以这里我们只需要进行一个强制类型转换(这里举例强制转换为int*,可以转换为其他类型的指针,只要不是char*,哪怕是double*也可以正确打印地址)。所以如果想要第二个字符的地址呢,cout << (int*)(a+1);就可以啦

    第二条打印地址的语句中,直接使用了&a;这里需要强调的是,不论任何数组,数组名表示的都是第一个元素的地址,&数组名表示的才是该数组的地址,虽然二者打印出来是同一地址,但是如果进行指针运算则天差地别,看一下下面这段代码和效果

    int a[10];
    cout << "a  is: " << a << " a+1  is: " << a+1 << endl;
    cout << "&a is: " << &a << " &a+1  is: " << &a+1 << endl; 

    有图有真相,a+1只是向后移动了4个字节,而&a+1直接向后移动了40个字节,所以&数组名表示的才是整个数组的地址。所以同理用&字符数组名也可以打印出字符串地址。

     下面说一说字符串常量,首先字符串存储在静态存储区,其次,不可修改。

    于是就产生了下面几条语句

    char a[20] = "1234";
    char * p = "1234";
    const char * p = "1234";

    第一条语句不用说,很常用了,正确,那么二三条语句哪个对哪个错呢。

    ----------------------------以下为9.7号加更----------------------------

    很明显,像"1234"这样的字符串常量被存储在静态存储区中,他们的类型是const char*型,根据类型匹配原则,很显然将其赋给一个char * 的指针肯定是不行的。第三条语句百分百正确。

    但是第二条语句在c 和 c++两种环境下编译产生了微小差异。

    在C环境下编译程序正常运行,且0warning.

    而在C++下,则产生了一条警告

     这是因为在传统的原始C中,是没有const的概念的,也就是说在C中没有硬性规定常量"1234"不能修改,但是再进一步尝试着修改时,会产生内存错误,也就是说字符串常量"1234"是const char*类型的,不能被修改

    而在C++中规范了这种情况,明确规定"常量1234"就存储于静态存储区,这里没有报错而只是抛出一条警告仅仅是为了兼容C。

    所以不论是在C还是C++中建议使用const char*指向一个字符串常量。

    ------------------------------end-------------------------------------------

    刚开始我还觉得加不加const无所谓,反正都是错的,后来细思极恐,果然还是知识贫穷限制了想象力;

    一开始我认为后面两句错的原因是因为 没见过,看着像是给*p赋了个"1234",可又不是,这是个什么东西,总之就是看着怪怪的;

    所以这里就牵扯到开头提到的一个知识点了;在C/C++中,一个字符串常量表示的是该字符串第一个元素的地址,就跟char指针名,char数组名表示的是字符串第一个元素的地址一样

    所以这回就明白了,看着是"1234",其实本质上是"1234"第一个字符的地址,那么将 char*型的指针p指向“1234”就没有问题啦,地址对地址嘛,但是编译后出错了,理由是"1234"是一个常量,所以加上了const限定。

    关于这里为什么要加const限定才正确,如果不加的话类型也是匹配的,为什么会报错呢,所以这里需要再强调一下,"1234"是字符串常量,常量不允许被修改,如果char * p = "1234";编译成功,那就意味着"1234"有可能被修改,而这是不被允许的,所以必须乖乖听话加上const限定符让系统安心才是嘛。

    咦?常量不允许被修改,要加const限定符才可以,那为什么我们一直用的第一条语句是正确的而且从来没有报错过呢?

    这就要说说第一条语句和第三条语句的区别啦,"1234"是字符串常量,常量存储在静态存储区,有地址

    第三条语句是让一个常量指针直接指向静态存储区的"1234"的地址,因为是直接指向本尊地址,所以要加上const限定符

    而第一条语句则是将静态存储区的"1234"的副本拿出来复制到字符数组a中,所以这样初始化可以在后面随意更改副本"1234",这样并不会影响到真正的"1234";

    是真地址假地址cout一下就知道了

    1 char a[20] = "1234";
    2 const char * p = "1234";
    3 cout << &a << endl; // 获取字符数组a的地址
    4 cout << &"1234" << endl; // 获取 "1234"在静态存储区的地址
    5 
    6 //再来验证一下字符串常量为该字符串第一个元素的地址
    7 cout << *"1234" << endl;
    8 cout << *("1234"+1) << endl;

    OK,明显副本”1234“与本尊”1234“的地址不同,而且差了很多很多地址位,肯定是跨内存区了的,最后面两行想要的结论也得到了验证,实践出真知,真是不枉我熬夜到12点。

  • 相关阅读:
    【集训第二天·翻水的老师】--ac自动机+splay树
    【集训第一天·特来卖萌】树链剖分之水水的例题
    1.ARM寄存器简解
    2.汇编代码的简单实现
    Android之Activity 生命周期
    Android之SQLite
    魅族 -- 禁止D及以下级别LOG的输出
    常用的SQL语句
    Android开发工具
    Android介绍
  • 原文地址:https://www.cnblogs.com/GuoYuying/p/11391981.html
Copyright © 2011-2022 走看看