Char字符串cout输出地址相关问题探究与总结
在学习C++char类型数组时,对cout如何输出地址感到疑惑,于是开始了一些简单的测试验证
说明:
1.本次验证中3~8项的结论不止适用于char类型,同样适用于其他类型。
2.由于重点在于通过cout输出探究Char字符串引用、地址等相关问题,因而验证实验时将只会采用char类型相关的代码进行验证,难免缺乏对比试验的严谨性。
3.如果其他的类型在3~8项中存在特例,烦请各位知情者补充。
4.上方目录链接的部分多出的“\”是用于转义字符,标题里实现了但是目录里好像出了问题。
5.如果只是为了查看相关的结论建议直接查看目录名即可
6.验证和结论可能存在疏漏,请各位不吝赐教多多指点。
1-cout方法:
1-1 cout输出char*类型的变量时会直接输出char*或const char*所指向的字符或字符
指针和数组的特殊关系可以扩展到c-风格字符串。请看下面的代码
char flower[10] = “rose”;
cout << flower << “s are red\n”;
数组名是第一个元素的地址,因此cout语句中的flower是字符‘r’的地址。cout对象认为char的地址是字符串的地址,因此他打印带地址处的的字符,然后继续打印后面的字符,直到遇到‘\0’为止。总之,如果提供一个字符的地址给cout,它将从该字符开始打印,直到"\0"为止。
以上内容出自C++ Primer Plus 第92页,我将最后这一句话用粗体标识了出来,这里一共展示了两个特性,第一个特性是进行的对象,第二个是打印操作的执行逻辑。
1-1-1 cout在输出字符数组的名称时不会返回地址而是返回对应的值
为测试cout该特性,我使用了如下代码进行简单地检测,可以发现,cout在输出char类型数组的数组名时,会输出数组的所有内容,而非地址。
1-1-2 cout在输出单个字符的char*时会展现2个特性
1-1-2-1 第一个特性,输出的类型是char*类型:
由1-1-1可知,cout在输出字符数组的名字时会输出完整的字符串而非地址,而字符数组的名字的类型正是char*类型。
而这里进行的实验则使用了单个字符,并输出这单个字符的地址,可以发现,输出中成功的显示出了对应的字符。
1-1-2-2 第二个特性,输出的结尾条件是找到‘\0’
这里进行了如下图所示的检测
程序没有报错,但是存在一个奇怪的现象,直接输出该字符地址的类型时不仅仅输出了对应的字符,还输出了一串随后的乱码。如果你用的是菜鸟工具的在线工具你甚至可能会陷入时间漫长的编译。(可恶!我才不会去勤奋到打开Dev和VS的!)
注意到这里,回顾1-1开头给出的书中的内容, “它将从该字符开始打印,直到"\0"为止”
我们会发现一般的字符并没有’\0’因此cout会以该字符为第一个字符开始持续的打印,一直寻找到第一个\0为止,于是便强行将随之的后续地址当做了后续的字符,于是便出现了乱码现象。
事实上字符串和字符数组都是默认以‘\0’结尾,因此并不需要特意的撰写‘\0’
2-“字符串常量”
2-1双引号包含字符串的变量类型为const char*类型
这里进行了如下图所示的检测
2-1-1 证明其至少为char*类型
经过对cout的测试验证,我们知道了cout的特性。
也就是说对于一个char而言,直接输出char是返回char的值,直接输出char的地址是携带着一堆乱码的值。那么对于一个string类型而言呢?
检验里用了多种方式来接受words,以此来确认它的类型。
我们会发现如下的5种接收方式里,都成功输出了,我们能确定这种字符串是一个char*类型,因此cout中的输出原理实际上也是接收到了一个char*的地址,然后输出了一个以‘\0’结尾的字符串。
2-1-2 证明其为const类型
但这里值得关注的是char* s1虽然仍然是正常的输出出来了,但是仍然有一行关于它的错误:
deprecated conversion from string constant to 'char*'
他的意思就是:
从字符串常量到“char*”的转换已弃用
这是因为双引号中的字符串实际上不单单是一个char还是一个常量,因此他的类型应该是const char类型的。
而s1是:
char* s1=“words”;
这种操作虽然本质上是没有什么太大的问题的,但是char这个可变动的指针的存在,意味着即使是常量字符串也有被char指针强行更改而导致报错崩溃的可能,因此现在s1的写法已经不再适用。
现在保存这种常量的方式更多的是s5的方式,这样显然更加保险。
即:
const char* s5=“words”;
3-char str[n]类型
上方终于对cout相关的特性给摸清楚了,不过又有一个问题出现了。
如果说字符串的名字这个表示其地址的变量在接收后转义成了字符串而输出了。那么我们该用什么方式来输出地址呢?此处将会围绕字符串数组进行一系列进一步的探究。
3-1 char str[n]类型中,str、&str[0]的返回值是数组第一个元素的地址,结果为T*类型
数组的数组名str被C++解释为首元素的地址。
而str[0]就是首元素,固易得&str[0]就是首元素的地址。
str和&str[0]等价。
3-2 char str[n]类型中,&str的返回值是数组地址,地址数值上和上述(3)中的类似,但是类型为(T*)[n]
上方说了,数组名str返回的是数组第一个元素的地址,那么地&str又是个什么?
实际上&str是整个数组的地址,虽然其值和3-1中两个的值一般一致,但是&str其类型是(char *)[n],这个类型被cout接收时不会做关于char*引起的特殊变换,而只会输出地址。
3-3 char str[n]类型中,(int*)str的返回值是数组第一个元素的地址关于(int*)类型的强制转换,结果为int*类型
经过上方关于cout对于char类型特性的思考,于是有了这种强制转换更改地址类型从而输出地址的方式,这种方式只会更改类型而不会更改原本的值。因此将数组名str更改为int类型只需要前置一个(int*)进行强制转换即可在cout的时候显示对应的值。
4-char* str类型
4-1 char* str类型中,str、&str[0]的返回值都是指针所指的对应位置的地址
此处的str类型明确,就是一个char*类型,就是指向对应位置的地址。
而指针可以有类似于数组的关于中括号的用法。
如str[0]的意思在这里和*str的意思就等价,str[1]就是顺着地址往下char单位大小格后的地址所对应的元素。
&str[0]和str的等价也就显而易见了。
4-2 char* str类型中,&str的返回值与(6)中的不同,此处的&str是指str指针自己的地址而非所指值的地址
str的值是所指位置的地址,而*str是所指位置的值,而&str则是这个指针自己的地址了。
4-3 char* str类型中,(int*)str的返回值是指针所指的对应位置的地址关于(int*)类型的强制转换,结果为int*类型
这里的强制转换不需要过多的解释,毕竟是最基本的同类型强制转换。
5-附:char和cout的综合检测代码测试
检测结果如下,其中的特殊点可以结合上方的相关解释进行查看该测试代码如下:
#include <iostream>
using namespace std;
int main()
{
char Str1[10]="C++";//str1是字符数组
char * Str2 =Str1;//str2是字符指针
cout<<"Str1:\t\t" <<Str1<<endl;
cout<<"Str2:\t\t" <<Str2<<"\n"<<endl;
cout<<"&Str1[0]:\t\t" <<&Str1[0]<<endl;
cout<<"&Str2[0]:\t\t" <<&Str2[0]<<"\n"<<endl;
cout<<"&Str1:\t\t" <<&Str1<<endl;
cout<<"&Str2:\t\t" <<&Str2<<"\n"<<endl;
cout<<"(int*)Str1:\t" <<(int*)Str1<<endl;
cout<<"(int*)Str2:\t" <<(int*)Str2<<"\n"<<endl;
return 0;
}
文章难免存在纰漏,如果有什么问题或者建议,还请多多在评论区指点。