关于字符串,其实字符串是一个抽象的概念,在底层,它是通过char数组来实现的,为了用char数组来模拟字符串,我们需要一个标记来界定字符串的结束位置,因此需要在char数组中字符串的最后一个字符所在位置的后面加一个’\0’。
原因是,可以这么想,字符和字符串的区别在于最后有没有空字符,如‘S’是一个字符,而“S”是一个字符串,它其实相当于’S’ + ‘\0’。
那末,为什么字符串的最后要加上一个空字符’\0’呢?
对于C++来讲,很多处理string的函数,都是逐个处理字符串中的字符,直到遇到空字符为止,如果错误地处理一个没有空字符的“字符串”,如使用cout,系统将在打印完这个“字符串”之后继续将内存中随后的各个字节解释成要打印的字符,这样就会发生错误。
所以,可以说,空字符’\0’是字符串的一个标志。
有些情况下用所谓的字符串来赋值给char数组,系统会自己给你添加’\0’,如:
char arr[10];
scanf("%s", arr);//这种情况下系统就会给你自己添加一个'\0'
又如:
char arr[6] = {"hello"};
char arr[] = {"hello"};//这两种情况系统也会自动给你加'\0'
char arr[5] = {"hello"};//这种情况就要注意了
//有的编译器会自动给你修补不足的空间,并增加一个'\0'.
//然而有的不会,因为'\0'也是一个字符,也要占据一个位置.
//我们的建议是最好不要写这种危险的,碰运气的代码。
所谓自动加’\0’的问题是:你写在双引号中的,编译程序会认为是字符串,会自动为你添加上一个字符串结尾符号’\0’,而不是运行时添加的。
char arr[]={ 'h','e','l','l','o','\0' };
//如果想用字符的方式初始化应当这么写
关于上面两种情况,我们总结一下:自己写char数组,肯定要加上’\0’。scanf(“%s”, arr)这种情况自动加上是因为scanf函数内部做了处理。自己处理要自己加上。char arr[] = {“hello”};这种情况也会自动加上,但归根结底还是写编译器的人帮你处理好了。字符串最后加’\0’是一种规范,不是想当然的自动有个’\0’在那里。
所以在处理的时候,加不加’\0’,会不会自动加,需不需要自己加,只是依照情境,在具体的情境下,代码是依照什么样的逻辑,便有什么样的处理方式。
//String.h:
#ifndef SSCPP2014_STRING_H
#define SSCPP2014_STRING_H
#include <iostream>
class String {
private:
char *_buff;
int _length, _size; // _size is of the allocated memory
public:
// constructors
String();
explicit String(const char *src);
String(const String &src);
// destructor
~String();
// member methods
void assign(const char *src);
void append(const char &other);
void clear();
int compare(const String &other) const;
const char* c_str() const;
bool empty() const;
int find(const String &other, int pos = 0) const;
int length() const;
String substr(const int &pos, const int &count) const;
// overload operators
char& operator[](const int &index);
void operator=(const String &other);
void operator=(const char *src);
String operator+(const String &other) const;
bool operator<(const String &other) const;
bool operator<=(const String &other) const;
bool operator>(const String &other) const;
bool operator>=(const String &other) const;
bool operator==(const String &other) const;
bool operator!=(const String &other) const;
// friend methods
friend std::ostream& operator<<(std::ostream& out, const String &str);
};
#endif
//
// String.cpp:
// C++
//
// Created by 舒俊淮 on 16/7/8.
// Copyright © 2016年 Shujh. All rights reserved.
//
#include "String.h"
#include "string"
#include "cstring"
using namespace std;
const int MAX_SIZE = 101;
String::String() {
_size = MAX_SIZE;
_length = 0;
_buff = new char[_size];
}
String::String(const char *src) {
_size = MAX_SIZE;
_length = strlen(src);
_buff = new char[_size];
for (int i = 0; i < _length; ++i)
_buff[i] = src[i];
_buff[_length] = '\0';
//这里要注意最后必须加上'\0'
//可以这么理解,是为了让strlen(char*)对应str.size()
//如果不这么做,就会因为字符个数的不对应导致内存错误
}
String::String(const String &src) {
_size = src._size;
_length = src._length;
_buff = new char[_size];
for (int i = 0; i < _length; ++i)
_buff[i] = src._buff[i];
_buff[_length] = '\0';
}
String::~String() {clear();}
void String::assign(const char *src) {
clear();
_buff = new char[_size];
_length = strlen(src);
for (int i = 0; i < _length; ++i)
_buff[i] = src[i];
_buff[_length] = '\0';
}
void String::append(const char &other) {
if (_buff == NULL) {
_buff = new char[_size];
}
if (_length < _size) {
_buff[_length++] = other;
_buff[_length] = '\0';
}
}
void String::clear() {
if (_buff) {
delete []_buff;
_buff = NULL;
_length = 0;
}
}
int String::compare(const String &other) const {
if (*this == other) return 0;
if (*this > other) return 1;
if (*this < other) return -1;
}
const char* String::c_str() const {return _buff;}
bool String::empty() const {return _length == 0;}
int String::find(const String &other, int pos) const {
string toFind, tobeF;
for (int i = 0; i < _length; ++i)
toFind += _buff[i];
for (int i = 0; i < other._length; ++i)
tobeF += other._buff[i];
int res = toFind.find(tobeF, pos);
if (res == string::npos) return _length;
return res;
}
int String::length() const {return _length;}
String String::substr(const int &pos, const int &count) const {
String sub;
for (int i = pos; i < pos + count; ++i)
sub.append(_buff[i]);
return sub;
}
char& String::operator[](const int &index) {return _buff[index];}
void String::operator=(const String &other) {
assign(other._buff);
}
void String::operator=(const char *src) {
assign(src);
}
String String::operator+(const String &other) const {
String result(*this);
for (int i = 0; i < other._length; ++i)
result.append(other._buff[i]);
return result;
}
bool String::operator<(const String &other) const {
if (*this == other) return false;
int min = _length < other._length ? _length : other._length;
for (int i = 0; i < min; ++i) {
if (_buff[i] < other._buff[i]) return true;
if (_buff[i] > other._buff[i]) return false;
}
return _length < other._length ? true : false;
}
bool String::operator<=(const String &other) const {
if (*this == other || *this < other) return true;
return false;
}
bool String::operator>(const String &other) const {
if (*this <= other) return false;
return true;
}
bool String::operator>=(const String &other) const {
if (*this == other || *this > other) return true;
return false;
}
bool String::operator==(const String &other) const {
if (_length != other._length) return false;
for (int i = 0; i < _length; ++i)
if (_buff[i] != other._buff[i])
return false;
return true;
}
bool String::operator!=(const String &other) const {
return !(*this == other);
}
ostream& operator<<(std::ostream& out, const String &str) {
for (int i = 0; i < str._length; ++i)
out << str._buff[i];
return out;
}
这里关于上面的_length和_size说明一下:
有初学者可能不清楚声明一个_size的意图,认为仅仅用_length就可以表示字符串的长度了,_size的存在是画蛇添足,事实并非如此,注意到我们一开始是开辟了_size大小的空间,但是这些空间并不使用,或者说,并没有完全使用,在需要的时候,直接使用已开辟的空间,而不用自己再多去new一次,_size表示总的可用的空间有多少,_length表示已经用了多少,这样做的好处是减少了使用new的次数,大大减少了程序运行的时间。因为使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。许多STL的容器如vector都是此原理。
//test.cpp:
#include <iostream>
#include <cstring>
#include "String.h"
#include <string>
using namespace std;
String a, b("MFGZ!");
String c = b;
void display() {
cout << a.empty() << " " << a.length() << " " << a << endl;
cout << b.empty() << " " << b.length() << " " << b << endl;
cout << c.empty() << " " << c.length() << " " << c << endl;
}
int main() {
string aa, bb, cc;
display();
c[0] = 'K';
display();
cin >> aa >> cc;
a.assign(aa.c_str());
c.assign(cc.c_str());
display();
b.clear();
display();
for (int i = 0 ; i < 10; ++i) {
char t;
cin >> t;
a.append(t);
b.append(t);
c.append(t);
}
display();
b = c;
display();
b = a + c;
display();
cout << a.find(String("1993")) << endl;
cout << b.find(String("HYOUKA")) << endl;
cout << c.find(String("RIKKA")) << endl;
cout << a.substr(0, 3) << endl;
cout << b.substr(3, 8) << endl;//26
cout << c.substr(6, 1) << endl;//27
cout << (a > b) << (a >= b) << (a < b) << (a <= b) << (a == b) << endl;
cout << a.compare(b) << b.compare(a) << endl;
cout << (a > c) << (a >= c) << (a < c) << (a <= c) << (a == c) << endl;
cout << a.compare(c) << c.compare(a) << endl;
b = a;
cout << (a > b) << (a >= b) << (a < b) << (a <= b) << (a == b) << endl;
cout << a.compare(b) << b.compare(a) << endl;
cout << a.compare(a) << endl;
return 0;
}