string,c_string,字符数组总结

  • 选用C++标准程序库中的string类,是因为他和c-string比较起来,不必担心内存是否足够、字符串长度等等,而且作为一个类出现,他集成的操作函数足以完成我们大多数情况下(甚至是100%)的需要。我们可以用 = 进行赋值操作,== 进行比较,+ 做串联。
  • string是STL提供的一种自定义类型,它处理空间占用问题是自动的,需要多少,用多少,不像字符指针那样,提心吊胆于指针脱钩时的空间游离,它可以从C-串转换得到,也可以转化为C-串…
  • #include 注意这里不是string.h ==string.h是C字符串头文件==
  • 声明一个字符串变量很简单: string Str;

    这样我们就声明了一个字符串变量,但既然是一个类,就有构造函数和析构函数。上面的声明没有传入参数,所以就直接使用了string的默认的构造函数,这个函数所作的就是把Str初始化为一个空字符串。

  • String类的构造函数和析构函数如下:

a) string s; //生成一个空字符串s 
b) string s(str) //拷贝构造函数 生成str的复制品 
c) string s(str,stridx) //将字符串str内“始于位置stridx”的部分当作字符串的初值 
d) string s(str,stridx,strlen) //将字符串str内“始于stridx且长度顶多strlen”的部分作为字符串的初值 
e) string s(cstr) //将C字符串作为s的初值 
f) string s(chars,chars_len) //将C字符串前chars_len个字符作为字符串s的初值。 
g) string s(num,c) //生成一个字符串,包含num个c字符 
h) string s(beg,end) //以区间beg;end(不包含end)内的字符作为字符串s的初值 
i) s.~string() //销毁所有字符,释放内存 

字符串操作函数

  1. =,assign() 赋以新值
  2. swap() 交换两个字符串的内容
  3. +=,append(),push_back() 在尾部添加字符
s+=str;//加个字符串 
s+=”my name is jiayp”;//加个C字符串 
s+=’a’;//加个字符 
s.append(str); 
s.append(str,1,3);//不解释了同前面的函数参数assign的解释 
s.append(str,2,string::npos)//不解释了 
s.append(“my name is jiayp”); 
s.append(“nico”,5); 
s.append(5,’x’); 
s.push_back(‘a’);//这个函数只能增加单个字符
  1. insert() 插入字符
  2. erase() 删除字符
  3. clear() 删除全部字符
  4. replace() 替换字符
  5. +串联字符串
  6. ==,!=,<,<=,>,>=,compare() 比较字符串
    C ++字符串支持常见的比较操作符(>,>=,<,<=,==,!=),甚至支持string与C-string的比较(如 str<”hello”)。在使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一得比较。字典排序靠前的字符小,比较的顺序是从前向后比较,遇到不相等的字符就按这个位置上的两个字符的比较结果确定两个字符串的大小。
    另一个功能强大的比较函数是成员函数compare()。他支持多参数处理,支持用索引值和长度定位子串来进行比较。他返回一个整数来表示比较结果,返回值意义如下:0-相等 >0-大于 <0-小于。
  7. size(),length() 返回==字符数量==
  8. empty() 判断字符串是否为空
  9. [ ], at() 存取单一字符
    可以使用下标操作符[]和函数at()对元素包含的字符进行访问。但是应该注意的是操作符[]并不检查索引是否有效(有效索引0~str.length()),如果索引失效,会引起未定义的行为。而at()会检查,如果使用 at()的时候索引无效,会抛出out_of_range异常。
string str1 = "Iphone 5";
cout<<str1[2]<<endl; // = h
cout<<str1.at(4)<<endl; // = n

string stuff;
getline(cin,stuff); // 输入一行字符赋值给stuff
getline(cin,stuff,'!'); // 输入一行字符以“!”结束
cout<<stuff<<endl;
  1. >>,getline() 从stream读取某值
  2. << 将谋值写入stream
  3. copy() 将某值赋值为一个C_string
  4. ==c_str()== 将内容以C_string返回。c++中c_str()的用法详解:https://blog.csdn.net/u013682388/article/details/39547773
  5. ==data()== 将内容以字符数组形式返回
    C ++提供的由C++字符串得到对应的C_string的方法是使用data()、c_str()和copy(),其中,data()以字符数组的形式返回字符串内容,但并不添加’\0’。c_str()返回一个以‘\0’结尾的字符数组,而copy()则把字符串的内容复制或写入既有的c_string或字符数组内。C++字符串并不以’\0’结尾。我的建议是在程序中能使用C++字符串就使用,除非万不得已不选用c_string。
  6. substr() 返回某个子字符串
s.substr();//返回s的全部内容 
s.substr(11);//从索引11往后的子串 
s.substr(5,6);//从索引5开始6个字符 
  1. 查找函数
find() 
rfind() 
find_first_of() 
find_last_of() 
find_first_not_of() 
find_last_not_of() 

这些函数返回符合搜索条件的字符区间内的第一个字符的索引,没找到目标就返回npos。所有的函数的参数说明如下:
第一个参数是被搜寻的对象。第二个参数(可有可无)指出string内的搜寻起点索引,第三个参数(可有可无)指出搜寻的字符个数。比较简单,不多说不理解的可以向我提出,我再仔细的解答。当然,更加强大的STL搜寻在后面会有提及。
最后再说说npos的含义,string::npos的类型是string::size_type,所以,一旦需要把一个索引与npos相比,这个索引值必须是string::size)type类型的,更多的情况下,我们可以直接把函数和npos进行比较(如:if(s.find(“jia”)== string::npos))。

  1. begin() end() 提供类似STL的迭代器支持
  2. rbegin() rend() 逆向迭代器

C-串与string的区别

  • 在C++中,有两种字符串,一种是从C沿袭过来的,称为C-字符串,简称C-串,别称为ASCII串,C-串是一个全0位(整数0)作为结束符的字符序列,该全0字节既是8位的整数0,也是ASCII码的0.
  • 字符0的ASCII码是48!
  • ‘\n’代表ASCII码为0的字符,从ASCII码表中可以查到ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不干。用它来作为字符串结束标志不会产生附加的操作或增加有效字符,只起一个供辨别的标志。
  • C-串的空间长度为字符串长度加1
  • ==C-串的类型==是什么呢?C-串的类型是char*(字符指针), 说的更精确一些,是==const char*== 。它与字符数组虽然类型不同,但操作上都是一样的,都表示C-串的==起始地址==
  • str是字符指针变量,* str是字符指针变量的间接引用,输出字符指针的间接引用,就是输出单个字符。str指向”Hello”的首地址,因而输出* str表示该地址代表的空间上的值—”H”。输出字符指针就是输出C-串,所以当输出str时,就是从‘H’字符的地址开始,逐个输出所有字符直到遇到0;
  • 由于C-串类型为字符指针,因此比较两个C-串的时候,会因空间位置的不同而不同,在库函数中,专门设计了对C-串进行一系列的操作的函数:
char* strcpy(char* x1, char* x2);//拷贝函数,它表示将x2字串拷贝到x1所在的位置,将x1原先的内容覆盖,该函数调用后返回x1的首地址,目的是让调用结果可以直接参加之后的字串操作,需要注意的是,由于x2字串的长度可能比x1字串空间要长,所以strcpy的使用并不安全,如果a字符数组长度为3(小于s1的长度),则执行strcpy(a, s1)会让紧挨a数组的邻近内存空间中的数据也被修改,导致不可预料的错误
int strcmp(char* x1, char* x2);//比较函数,它表示将x2串与x1串进行字典序列比较,如果x1小则返回值为负数,x1大则返回值为正数,相等则返回0
char* strcat(char* x1, char* x2);//连接函数,它将x2的字串连接到x1字串后面,显然这会加长x1字串,或者说,结束符0后移,当x1字串之后所余的自身空间不足以接纳x2字串时,调用该操作将不安全
char* strrev(char* x);//倒置函数,它将x字串倒过来,并改变原来的存储
char* strset(char* x, char b);//设置函数,它将x字串的每个字符用b字符来代替
char* strstr(char* x, char* s);//查找函数,在x字串中查找s字串,若找到,返回1,否则返回0
char* strchr(char* x, char b);//查找函数,在x字串中查找b字符,若找到,返回1,否则返回0
  • 这些库函数的操作默认在string.h的头文件中
  • 在操作字符串中,string比之char*(字符指针),即优雅又灵活,缺点是在大量的字符操作中,性能不如char*

字符数组的定义与初始化

字符数组的初始化,最容易理解的方式就是逐个字符赋给数组中各元素。
char str[10]={ ‘I’,’ ‘,’a’,’m’,’ ‘,‘h’,’a’,’p’,’p’,’y’};
即把10个字符分别赋给str[0]到str[9]10个元素
如果花括号中提供的字符个数大于数组长度,则按语法错误处理;若小于数组长度,则只将这些字符数组中前面那些元素,==其余的元素自动定为空字符==(即 ‘\0’ )。

在实际应用中人们关心的是有效字符串的长度而不是字符数组的长度,例如,定义一个字符数组长度为100,而实际有效字符只有40个,为了测定字符串的实际长度,C语言规定了一个“字符串结束标志”,以字符’\0’代表。如果有一个字符串,其中第10个字符为’\0’,则此字符串的有效字符为9个。也就是说,在遇到第一个字符’\0’时,表示字符串结束,由它前面的字符组成字符串。

  • char str[ ]=”I am happy”; 因为字符串常量”I am happy”的最后由系统自动加上一个’\0’

    cout << sizeof(“I am happy”) << endl; 输出为11;


因此,上面的初始化与下面的初始化等价
char str[ ]={‘I’,’ ‘,’a’,’m’,’ ‘,’h’,’a’,’p’,’p’,’y’,’\0’};

而不与下面的等价
char str[ ]={‘I’,’ ‘,’a’,’m’,’ ‘,’h’,’a’,’p’,’p’,’y’};

  • 字符数组并不要求它的最后一个字符为’\0’,甚至可以不包含’\0’
  • 可见,用两种不同方法初始化字符数组后得到的数组长度是不同的。

字符串的表示形式

  • 在C语言中,可以用两种方法表示和存放字符串:
    1. 用字符数组存放一个字符串:char str[ ]=”I love China”;
    2. 用字符指针指向一个字符串:char* str=”I love China”;

      C语言对字符串常量是按字符数组处理的,在内存中开辟了一个字符数组用来存放字符串常量,程序在定义字符串指针变量str时只是把字符串首地址(即存放字符串的字符数组的首地址)赋给str。

  • 两种表示方式的字符串输出都用
  • 字符数组只能在初始化的时候初始化为常量字符串,在赋值的时候只能单个字符的赋值
  • 字符指针可以在任何时候赋值为常量字符串的地址。

首先来说说char sz[] = “string”; 这条语句,该语句是按照数组的形式来一个个存放的,编译器将其解释为
char sz[] = {’s’,’t’,’r’,’i’,’n’,’g’,’/0’}; 如果在函数内部出现的话,这几个字符将==存放在堆栈==中,所以不是字符串常量。

再来说说char *psz = “string”; 这条语句,该语句定义了一个指向”string”字符串的指针,并没有空间存放”string”字符串,显然把“string”当做==字符串常量==并存放在常量区是最合适的选择。

  • 函数中行参为数组时应怎么看待?
如:void  foo(char sz[100], int ival[10]);
答案是将其理解为指针的形式:
void  foo(char *sz, int *ival);

问题总结:


#include <iostream>
#include <string>
#include <string.h>
using namespace std;

int main() {
    char* s = "string";
    //s[3] = 's';
    cout << s << endl;
    return 0;
}
  • 如果不修改s的内容只会出warning:==ISO C++ forbids converting a string constant to ‘char*’==
  • 如果通过s修改字符串的值程序直接崩溃
    为什么会出现这样的问题呢?
  • 原来char *背后的含义是:==给我个字符串,我要修改它==。而理论上,我们传给的字面常量是没法被修改的。
  • 所以说,比较和理的办法是把类型修改为const char *。这个类型说背后的含义是:==给我个字符串,我只要读取它==。
字符串常量
  • 字符串常量。之所以称之为常量,由于它可一看作是一个没有命名的字符串且为常量,==存放在静态数据区==。
    这里说的静态数据区,是相对于堆、栈等动态数据区而言的。
  • 静态数据区存放的是==全局变量和静态变量==。从这一点上来说,字符串常量又能够称之为一个==无名的静态变量==
  • 由于”Hello world!”这个字符串在函数 s1和s2 中都引用了。但在==内存中却仅仅有一份拷贝==,这与静态变量性质相当神似。
#include <iostream>
#include <string>
#include <string.h>
using namespace std;

int main() {
    const char* s = "string";
    cout << (void*)s << endl;
    cout << &"string" << endl;
    return 0;
}

上面程序输出的结果:
0x405065
0x405065
地址是一样的。

  • 全部的字符窜常量都被放在静态内存区
    由于字符串常量非常少须要改动。==放在静态内存区会提高效率==
#include <iostream>
#include <string>
#include <string.h>
using namespace std;

int main() {
    char str1[] = "abc";
    char str2[] = "abc";
    const char str3[] = "abc";
    const char str4[] = "abc";

    const char *str5 = "abc";
    const char *str6 = "abc";
    char *str7 = "abc";
    char *str8 = "abc";
    cout << ( str1 == str2 ) << endl;
    cout << ( str3 == str4 ) << endl;
    cout << ( str5 == str6 ) << endl;
    cout << ( str7 == str8 ) << endl;
    return 0;
}

输出:结果是:0 0 1 1

  • str1,str2,str3,str4是数组变量。它们有各自的内存空间;
    而str5,str6,str7,str8是指针,它们指向同样的常量区域。
  • 参考:https://www.cnblogs.com/gavanwanggw/p/7246880.html

  • 比如可以写一下代码:char* str=”hello”;其中的”hello”不是const char* 类型吗?为什么可以给char* 赋值?

    C中”hello”类型是char[6],所以允许char* str=”hello”; 你甚至可以接着使用 str = “d” 这样赋值,编译通过没问题只不过会在运行时报错,因为”hello”位于只读存储区不允许写操作。C++中”hello”的类型是const char[6],不过为了兼容C代码做了特殊处理,所以也允许赋值给char指针。就是为了兼容性,而且现在的C++编译器大多会编译告警这个赋值。

  • 推荐把类型==修改为const char *==
    也就是说,这种语法的存在,只是为了保证以前的C代码可以正常编译。对于标记为deprecated的语法,做为一个C++程序猿,请不要再使用了。因为这样做可能会导致一个 让你颜面尽失的==内存非法写操作==。
  • 答案也很简单,因为编译器也是人开发的程序,只要他认为可以,那就可以。大概是已经有太多类似的代码了。用户已经习惯这样写。所以没有必要明确的拒绝这样的代码通过编译。

猜你喜欢

转载自blog.csdn.net/zhc_24/article/details/80508724