char *p和 char p[]的区别

"本文详细探讨了C++中字符串与指针的关系,指出`char* p = "test"`这样的声明会导致指针指向只读内存,尝试修改会引发未定义行为。推荐使用`const char *p`或`char str[]`来避免混淆。同时,文章列举了C++中若干已废弃的特性,强调遵循const语义的重要性。"
摘要由CSDN通过智能技术生成
#include <stdio.h>
#include <string.h>

void go(char *str,int len)
{
        strcpy(str,"yujinchi");
}

int main()
{
        char str[] = "yuJinChi love tangLiNa"; 
            //正确   
            //char p[]是一个数组,已经分配内存,是将"abc123ABC" 复制到该内存里面,这个内存是可读写的
        
        //char *str = "yuJinChi love tangLiNa";   
            //错误
            //char* p是一个指针,根本没分配内存,他指向的"abc123ABC" 是只读的,不能改变,>给他赋值肯定是错的
        int len = strlen(str) -1;

        go(str,len);

        printf("%s\n",str);

        return 0;
}
答:
char* p是一个指针,根本没分配内存,他指向的"abc123ABC" 是只读的,不能改变,给他赋值肯定是错的而char p[]是一个数组,已经分配内存,是将"abc123ABC" 复制到该内存里面,这个内存是可读写的

问:

char *p是不是相当于定义了一个字符串常量,字符串的内容不能改变?

答:

它是字符串常量了,怎么可以改变它的值呢?
只要记住,指针是不分配内存的,它指向的是系统的只读的内存,而数组是分配内存的,就是将系统的只读的内存里面的值复制到它的内存里面,因此可读写

问题:
char ca[] ="abcd"; char *cb = "abcd"; 其中ca[]是可以通过ca[x]修改字符串中的数据,但cb是不能修改abcd这个字符串常量的值,那既然都是字符串常量,为什么数组方式可以修改呢? 

解决:

从一般实现方式来看:
char ca[]="abcd";——ca为局部变量(自动变量或寄存器变量)时具有自动存储期,放在运行期内存的栈中;ca为全局或局部静态变量时具有静态存储期,放在内存的静态区;字符串字面量"abcd"具有静态存储期,放在文字常量区。这里,对象"abcd"作为一个右值用来初始化对象ca,两者不是同一回事。
char* cb = "abcd";(这种用法在C++中为deprecated,应使用const char* cb = "abcd";代替)——按指针引用cb和"abcd"时视为引用同一个对象(作为一元&和sizeof的操作数等左值语义上下文中时则不同),一般放在文字常量区。
文字常量区是只读的,而且更改字面量本身语义是不明确的,因此C++把字符串字面量视为字符串常量,禁止通过指针更改字面量本身。但是基于为了兼容旧代码等原因,ISO C标准中,更改字符串字面量的行为是未定义的,尽管具体的编译器可能把字符串字面量作为常量处理。

也即:
char ca[]="abcd";//这个是栈常量,可以修改。
char *cb = "abcd";//这个是字符串常量,存在文字常量区,地址受保护,只读。

声明:

char s[] = "abc", t[3] = "abc";

界定了“平原”char数组对象S和T的元素是文字初始化字符串。这项声明是相同的字符

s[] = { 'a', 'b', 'c', '\0' }, t[] = { 'a', 'b', 'c' };

该数组的内容修改的。另一方面,宣言

char *p = "abc";

定义型“指针为char”p和初始化它指向一个类型的对象“数组char”的长度为4的是一个字符字符串初始化的元素。如果试图使用p作出修改数组的内容,该行为是未定义的。

我相信,使用C/C++多年的人对下面这个字符串赋值语句都不会陌生吧。

               char* p = "test";

同时,我也相信,各位在使用这种语句后吃过很多苦头也不少吧?只要你想利用指针
p来改变字符串的内容,你的程序都会得到一个让你颜面尽失一个内存非法操作。比
如,下面的这些语句:

               p[0] = 's';
               strcpy(p, "haoel");

原因就在于,char* p = "test"; 这个声明,声明了一个指针,而这个指针指向的是
全局的const内存区,const内存区当然不会让你想改就改的。所以,如果你一定要写
这块内存的话,那就是一个非常严重的内存错误。另,之所以加粗“全局const内存
区”,是强调一下,如果你不信的话,你可以试试下面这段代码,看看p1和p2的地址
是不是一样的。

               char* p1 = "anything";
               char* p2 = "anything";
               printf(“ p1=%x, p2=%x \n”, p1, p2);

我想这应该是一个众所周知的问题吧。取而代之的,应该是使用数组来做初始化声明。如:char str[] =“hello world”;

至于你问我为什么要对学C++的人那么苛刻,那是因为学过C++的人都知道C++中的const关键字的有着什么样的权力,你也应该知道C++对 const有着无比的照顾
和关爱,几乎所有关于C++的书都会提到const这东西。所以,如果作为一个C++的程序员来说,如果你不知道的话,那就太说不过去了。

我们知道,双引号引起来的字符串是const的,所以,在C++的世界中,你应
该进行如下的声明才比较稳妥:
               const char *p = "test";

这样,当你修改这个字符串的内容时,编译器会给你一个错误而导致你的程序编译不
通过,从而不会产生运行时的内存错误。

可问题是,像C++这种对类型要求很严格的语言来说,为什么它在编译诸如
char *p="test" 程序的时候不出错,甚至连个警告都没有(g++和vc++7)?难道这
是他的一个bug?我想,这应该是对古老的C的一个向下兼容。因为,在C的世界中,
这种用法太多了。

在C++中,比如:函数的参数和异常的捕获都存在这种问题,如下所示:(因编译器
而定,在gcc 3.4.3版中,下例中的异常示例不能被捕获,但VC++6中却可以被捕获)

        func( char* p) { }    // 以这种方式调用函数func(“abc”);

        try { thow “exception”; } catch (char* p) { }

这些都是C++编译器默认了可以把const char* 转成 char* 的罪行,无疑会
对大家是一个误导。甚至让人无所畏惧地走入其中,并自以为走入了正途。这样看来
,这种向下兼容的C++标准,就显得有点误人不浅了。

不过好在,C++标准委员会早已意识到了这一点。这个C++的feature被定义为了“Deprecated Feature”,即“不被建议使用的特性”。意思就是,在将来,这
种特性将被从C++中移出,于是,你目前的这种程序将无法在新的C++编译器上编译通过。对于程序的可移植性来说,我们今天所写的代码尤其要注意这些“Deprecated Feature”。

据我所知,目前C++中被列为“Deprecated Feature”如下所示(可能不准确,请大家指正)下面的这些feature都已被C++标准委员会订为废除featrue了。

一、隐晦的字符串的const转换。
char *p = "test";
w_char *pw = L"test";
把一个const的字符串类型转成non-const的。包括指针和数组。

二、隐晦的类型声明。
func() {}    //函数的隐晦返回类型是int
static num;     //变量的隐晦类型是int
这种feature在C89中还可以使用,但在C99和C++中都被去除了。(gcc 3.4版本对于这种声明会给出编译错误,而VC++6.0会认为这是合法的程序)

三、布尔变量的累加操作。
bool isConn = false;
isConn++;           //这个操作会把isConn变为true
就目前而言,几乎所有的编译器都认可这种操作,但这种用法也是不被建议的,终有一天会被取消。

四、更改父类成员的存取权限。
class B
{
     protected:
         int i;
};

class D : public B
{
      public:
         B::i; //这种方式可能大家很少看到。
};

对于这种语法,子类重新暴露了父类的私有成员。这会带来很大的安全性问题。目前而言,这个feature对于所有的编译器来说应该都是可以编译通过的(连个Warning都没有)。但这个feature也是要被废除的。

五、文件中域的static声明
static int i;
static void func()

据说,这种旧的在C中的为了实现其作用域在本文件中的feature在未来的C++中也要被取消。文章到这里应该结束了,在结束之前,让我再给大家共享一个有趣的关于const的例子(在网上看到的)

     const int a = 1;
     int *p = const_cast<int*>(&a);
     *p = 2;

     cout << “value a=”<< a << endl;
     cout << “value *p=” <<*p << endl;
     cout << “address a=” <<&a << endl;
     cout << “address p=” <<p << endl;

这段代码输出的结果如下:
value a=1
value *p=2
address a=0xbff1d48c
address p=0xbff1d48c

地址都是一样的,可值为什么不一样呢?呵呵。这个问题看起来有点“学术味”过浓,不过是个好例子,可以让你知道C++的一些用法和一些原理。有以下几个方面大家
可以考虑一下:
1)const int a = 1是不是和宏有点像,会不会被编译器优化了?
2)去修改一个const的值,本来应该是不对的。这可能会是向旧的C兼容。是否会让编译器产生未知行为?

所以,这个示例也告诉我们,我们应该遵循C++中的const和non-const的语义,任何想要破坏这个语义的事情都会给我们带来未知的结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值