标签(空格分隔): C++ 编程语言
介绍:
C++中字符串分为两种,分别为:
- C-style string
- string类库的string
对于前者,是一个char类型的数组,但是最后一位一定是’\0’
(这一点我们可以用’\0’来截断字符串);
我们可以简单定义一个样例:
char test[5] = {’t’, ‘e’, ’s’, ’t’, ‘\0’};
这里’\0’的机制让我想了好久,在下面输出的地方细说
但是如上定义起来很麻烦,要一个一个字符加单引号敲上去,所以就可以定义一个叫做字符串常量(string constant)或者叫做字符串字面值(string literal)的字符串:
char test[5] = “test”; //预留一个’\0’的位置
char test[] = “test” //让编译器去数,然后开空间
然而还有一种情况是,我们开的空间富余下来,比如:
char test[10] = “test”;
char test[10] = {'t', 'e', 's', 't'};
这样剩下的空字符会自动被补位’\0’
BTW我这里突发奇想做一个小实验:
char test_1[10] = {'t', 'e', 's', 't'};
char test_2[10] = "test";
if (test_1 == test_2) {
cout << "equal" << endl;
}
编译的时候g++告诉我:
warning: array comparison always evaluates to false [-Wtautological-compare]
运行的时候果真是false,Tell me why。。。。。
这里同学后来提醒我,数组名是指向数组首地址的指针,拿内存了两个不同的地址相比,怎么可能正确……
——————————————————————
(另起下一个话题)
去查C-style string的原因是看到一篇博文,说到
定义
void print(const char*);
然后调用的时候是精确匹配:
print("a");//精确匹配,调用print(const char*)
查阅了书之后才发现,“test”这个常量字符串,表示的是字符串所在的内存地址,所以应该是const char*类型的变量
所以
char test[] = “test”
我们是将“test”的内存地址赋给了test
那么cout的时候
cout << test[] << endl;
是过不了的,但是
cout << test << endl;
是可以的
另外如果“test”表示的是地址
cout << “test” << endl;
也是可以过的
说明C++在输出常量字符串的时候,传入cout和’<<’的参数是常量字符串的内存地址,有点诧异,cout不忠于数据而去解析地址了呀
———————————————————————————
再做一个实验:
const char* test = "test";
cout << test << endl;
结果输出的是test
const char* test = "test";
cout << *test << endl;
结果就只能输出首字母t
————————————
char* test = "test";
cout << test << endl;
这个时候编译器给我了一个警告:
warning: conversion from string literal to ‘char *’ is deprecated
说明我在这里做了一个字符串面值到字符类型的指针的一个转换,那么我忽略警告去运行呢,
就发现输出的还是test
那么说明,const 在这里不是简简单单的常量不可变的意思
const char* 单独列出,成为了一种数据类型,就是叫做常量字符串。
操作:
cout 拼接:
cout << “hello” “world” << end;
cout << "hello"
"world" << endl;
两种都是可以过的,结果就是helloworld
头文件:
单独使用时不需要头文件,但是如果需要调用一些C-style string的函数,我们就需要
# include<cstring>
老式的是:
# include<string.h>
常见的strlen,strcmp, strcpy等等,这些函数其实用到的时候去百度一下就好,或者自己做一下小实验,理解更清晰
….好吧我懒得总结
简单写下:
strlen:
原理应该是从首地址开始计数,到最后一个不是’\0’的字符位置字符串的长度
strcmp:
原型为
int strcmp(const char *s1, const char *s2);
字符串大小的比较是以ASCII 码表上的顺序来决定,此顺序亦为字符的值。strcmp()首先将s1 第一个字符值减去s2 第一个字符值,若差值为0 则再继续比较下个字符,若差值不为0 则将差值返回。例如字符串”Ac”和”ba”比较则会返回字符”A”(65)和’b’(98)的差值(-33)。
strcpy:
把字符串s2中的内容copy到s1中,连字符串结束标志也一起copy.
strcat:
strcat() 函数用来连接字符串,其原型为:
char *strcat(char *dest, const char *src);
strcat() 会将参数 src 字符串复制到参数 dest 所指的字符串尾部;dest 最后的结束字符 NULL 会被覆盖掉,并在连接后的字符串的尾部再增加一个 NULL。
至于其他的一些函数,或者这些函数的一些特殊情况的处理, 当真正用到的时候,还需要我们去查阅和试验;
输出:
这里先不管书上的内容,我自己做了一些实验:
实验一:
char test01[3] = {'a', 'b', 'c'};
char test[3] = {'t', 'e', ’s'};
cout << test << endl;
cout << *(test+3) <<endl;
结果是tesabc和’a’
问题:
- 直接定义一个字符数组,编译器会不会自动加’\0’?
- 如果1是会的话,为什么我test01会将test的’\0’覆盖?
(后来发现其实是不加’\0’, 但是cout来访问机制会不一样)
实验二:
const char* test01 = "abc";
const char* test = "tes";
cout << test << endl;
cout << *(test+3) <<endl;
结果是”tes“和空
第一个理解为纯粹的字符数组,那么第二个为字符串常量,看来自动加上了’\0’并且不会被覆盖
后来和同学讨论了一下,字符数组不是字符串,因为在上面的例子中字符数组的内存开在一起,直接通过cout数组名来访问,本身就是一个不合适的操作,所以难免会出一些问题,但是这一块确实会容易搞混(字符数组和字符串),辨析的时候还是要理清思路。
输入:
常量肯定不能通过输入来定义了,我们定义变量字符串的时候,cin在在输入一个字符串或者字符数组的时候不能有空格,其次,cin会在输入结束的时候不读取’\n’,但是会自动加’\0’
如果想输入整行,可以用getline函数。
总结:
容易混淆的有三种类型:
字符数组
变量字符串(就是末尾为’\0’的字符数组)
常量字符串(用双引号引起来的字符串)
前两者的指针类型为char*
常量字符串的指针类型为const char*
容易混淆的操作,比如说如上的
char test[] = “test”;
“test”为常量字符串, 而test为变量字符串, 实现的机制是:
在构造test的时候,我们在常量表里找到”test“, 然后逐个字符复制到test中,最后加’\0’