字符串数组与指针


思维来源于一道题

char str1[]       = "abc";
char str2[]       = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5  = "abc";
const char* str6  = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出什么0?
cout << boolalpha << ( str3==str4 ) << endl; // 输出什么0?
cout << boolalpha << ( str5==str6 ) << endl; // 输出什么1?


可以明白 静态分配的数组(name[])有唯一的地址空间,因为它在静态分配的时候就分配好了。

此题的关键不在于5.6的const属性,即使没有const仍相等。因为指针是动态分配的(就我的理解)。
就是说!它虽然跟数组名同样为指针,可是却是行为完全不同的两个东西!!

 

我想这可能根编译器或者C语言的不够规范有关。
众所周知,在引用数组名的时候它会自动退化为指针。根据大家对指针的理解,也知道:指针,实际上
就是保存了一个目标地址的东西。再准确点说,也就是个地址值,类似于“0012FF60”的东西。

 

但是为什么,我们在输出的时候,单纯的输出指针——特别的,这里有两种情况。

首先,我实验了const char *p = "abc";

当指针指向一个数组的时候,cout<<p<<endl;,它会自动的输出整个数组序列"abc"。我很吃惊!因为这
跟我对指针的理解产生了质上的冲突!——它明明是一个地址,为什么会输出它实际的信息?……
至今我也没有得到答案,网上也查不到相关的信息,一直没有权威的说法给我答案(这可能要追溯到
编译器、或者C语言的创造上……)。我暂时地把它归纳为:编译器,或者是C语言对它(指针)的优化。
这肯定跟内部实现有关。我想创造者是为了方便我们的使用——因为我们输出指针(指向数组),如果
输出的是一个地址值,那么将非常不利于我们的使用——我们不得不解释出它(数组)里面的每一个元素。

 

然而这样的方便也是灾难性的——它给许多人造成了困惑。它的不规范,让我们也置疑C语言的严密性。

接下来,我又证明了我这一想法——

我使用单个元素而不是使用数组来为指针赋值——

int a = 31;
int *b = &a;

cout<<b<<endl;
cout<<&a<<endl;

结果跟我想象得一样,输出的是两个相同地址“0012FF60”……我很满意,这证明为了前面的想法。

特别的,我开始用的不是int,是char。输出的是两个相同的乱码。这让我又多了一个信息——由于int是
4字节的(32位),而char是1字节(8位)。我以为,在32位机上,不足32位的结构的地址会出问题——
但是我换成short又没问题了……所以我想只跟char的编码有关,为此我没做更深入研究。

 

最后,我想知道它们(数组、指针)的大小到底是多少。
众所周知,指针大小都是4。


char c[] = "1234567890";
char *a = c;
不要小看这两个句子。在任何时候,只要你定义的时候它是一个数组(c),编译器就永远会记住它。
无论你引用它的名字(c),使它退化成一个指针,还是在任何时候使用sizeof(c),它永远都是一个数组,
大小永远是固定的。

 

而指针,只要你定义的是一个指针,那么它大小永远就是4。
这一点只看你在定义时的行为。

 

就好像const char *h = "abcdefg";

 

最后我要强调的,上面这个句子其实很危险。因为要给一个指针赋值,必须给它一个地址!是地址!。
安全的做法,是我们最好先静态或动态创建一个对象。比如char a[10]; 或 char *a = new char[10]。
这样,我们再在其中赋值——只能a[0]、a[1]、a[2]、a[3]……挨着复制,比较麻烦。但是至少我们
使它(数组)的地址可见!它们是唯一的,并且可以安全地释放(静态的自动,动态的手动delete)。
指针本身是不能创建对象的。上面这个句子const char *h = "abcdefg";实际上做了两件事——
创建了一个"abcdefg"对象——我们并不知道它在哪;把这个对象的地址给了h。

如果我们再加一句(就像题目中的5,6),const char *g = "abcdefg";我们会惊奇地发现,h跟g是
相同的——它们的地址竟然是相等的!


明白吗?也就是说整个系统中"abcdefg"被当作一个对象放在某处,它(编译器)自动就创建了一个
这样的对象,而把这个对象的值(地址值),赋给我们的指针……

 

我们以为,我们是用指针const char *h = "abcdefg"; 用h,创建了一个对象。其实不然,是系统创建
了一个对象,并且把这个对象给我们的指针。这个对象在系统中是唯一,即使你再用const char *g = "abcdefg"; 它(编译器)也仅仅是把这个对象("abcdefg")找出来,赋(地址)给
你的指针。

 

像"abcdefg"这样的语句在我们的语法中是不允许的。因为它在这个句子里的实际作用是创建了一个
对象!而我们的本意不是创建这个对象,只是想让我们的指针指向这个对象!……因此我们写下的语句跟我们实际所做的事产生了分歧,这在C++中会引发“未定义的行为”。
不知道自己错在哪这是最恐怖的……

 

但是,这只是一个BUG,明白吗?只有在使用字符串的时候才会出现这个问题。但是由于人的拓展思维,如果不加强调,很容易就和其他规律弄混淆……所以,你应该对字符串有独到的理解——仅此而已。

 

指针永远是一个地址……

 

以上都是思维过程,以下的是结论:

我们写这样的句子    char *p = "abc";  //  ---1

似乎跟                   char p[] = "abc";  //  ---2

没什么区别。

 但是当我们要改变p的时候,这种区别便出来了。比如说我们要让p重新“代表”"aaa"。对于1,我们很容易写出p = "aaa";但对于2,我们不得不写p[0] = 'a';  p[1] = 'a'; p[2] = 'a'; p[3] = '/0';

你可能会觉得1很好。但是当真正我们在使用p的时候,又有多少时间需要去更改呢?

更为严重的:即便你更改了p,使p现在指向"aaa",但是原来系统中的"abc"仍然是存在的;如果你将来还要更改,那么现在的"aaa"也永远是存在的。——你只是更改了p指向的对象,然而却没有对之前的对象进行任何销毁处理——你也没办法进行销毁处理——这是一个BUG。 

但是2,永远都只有一份副本,永远都不会产生这样的问题。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值