C语言中字符和字符串在标准头文件string.h,在C++中
在C语言中,字符char和字符串分别使用单引号和双引号表示。其中,单引号表示字符字面量,是一个数值;双引号表示的字符串字面量,是一个指针。相应的,字符字面量在编译过程中被编译成对应的ASCII码,字符串字面量被编译成对应的内存地址。例如:
(1)'a'表示字符字面量(97),在内存中占用一个字节,‘a’+1表示‘a’的ASCII码加1,即‘b’
(2)"a"表示字符串字面量(是一个指针),在内存中占用2个字节,"a"+1表示指针运算,指向"a"字符串的结束'\0';
#include
#include
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
char c = 33;
printf("%c\n", c);
char* pc = (char*)127; // Low address 0x7F = 127, can't access by point
pc = (char*)'1';
printf("0x%x\n", pc);
char charASCII = 'a' + 1;
printf("Char plus %c\n", charASCII);
char str1[] = "Hello world";
char str2[] = "Hello";
char* str3 = (char*)"Eric hao";
if(str2 == str1)
printf("String is equal\n");
else
printf("Addr str1 %ul, str2 %ul\n", str1, str2);
system("pause");
return 0;
}
另外,char还是使用unsigned char表示字符?这个问题很简单,要表示8位无符号数值的时候,用unsigned char,要表示8位有符号数值或者ASCII字符的时候,用char。
在C语言中字符串时有序字符的集合,是程序中一项基本的元素。C语言中是没字符串给你按的,而是通过特殊的字符串数组来模拟字符串,示意'\0'结束的字符数组。由这个定义我们可以确定出字符串和字符数组之间的区别。当然,C中定义字符串常常是直接使用双引号引用一个或多个特殊的字面量,被存储在程序的全局只读存储区,在本质上是一个字符串数组,只是编译器默认自动在末尾加上'\0'字符。
#include
int main(){
char ca[] = {'H','e','l','l','0'}; //栈,字符数组,注意没有\0
char sa[] = {'W','o','r','l','d','\0'}; //栈
char ss[] = "Hello world!";//栈。从语义上看,要用"Hello World!"这个
//字符数组去初始化ss数组,编译器会直接
//用这个字符数组的每个元素,去初始化栈上
//ss数组中的元素。作为优化,"Hello World!"
//这个字符串就可以不必保存在全局只读区,
//而只出现在栈上。因此ss[0] = 'h'
//这个的语句是合法的。
char* str = "Hello World!"; //全局只读存储区,str[0]='h'是非法的
printf("%s\n", ca); //输出Hello及后面的内容,直到在内存中遇到\0
printf("%s\n", sa); //输出Hello World!
printf("%s\n", ss); //输出Hello World!
printf("%s\n",str); //输出Hello World!
ss[0] = 'h'; //栈,合法
printf("%s\n", ss); //输出Hello World!
//str[0] = 'h'; //全局只读存储区,非法
printf("%s\n", str); //输出Hello World!
return 0;
}
(1)字符串字面量的本质是一个数组,如“Hello World!”是一个无名的字符数组
(2)字符串字面量可以看作常量指针
(3)字符串字面量中的字符不可改变
(4)字符串字面量至少包含一个字符,即'\0'
#include
int main(){
//本质上字符串字面量是一个字符数组,形如"Hello World!"是一个
//无名的字符数组。
char b = "abc"[0]; //合法,指向字符数组的第0个元素,即a
char c = *("123" + 1);//合法,取出字符数组第2个元素,即2
char t = *""; //字符串字面量至少包少一个\0,即t='\0'
printf("%c\n", b); //'a'
printf("%c\n", c); //'2'
printf("%d\n", t); //0
printf("%s\n","Hello");
printf("%p\n","World"); //输出字符串字面量的地址(全局区中)
return 0;
}
(1)snprintf函数本身是可变参数函数,原型如下:
int snprintf(char* buffer,int buf_size,const char* format,…);
(2)注意点:
①当函数只有3个参数时,如果第3个参数没有格式化信息,函数调用是没有问题的;相反如果第3个参数包含了格式化信息,但缺少后续对应参数,则程序行为不确定
②格式化信息必须与变参个数相匹配
#include
int main(){
char buf[20] = { 0 };
//char src[] = "hello %s";//字符串中包含格式化信息,输出会异常
char src[] ="hello world!";//不含格式化信息,则正常输出
//因src中含有格式化信息,所以这种行为不确定,
//输出hello会,后面可能跟一个奇怪的字符串。
snprintf(buf, sizeof(buf), src);
printf("buf = %s\n", buf);
return 0;
}
(1)字符串之间的相等比较需要用strcmp完成
(2)不可直接用==进行字符串直接的比较
(3)完全相同的字符串字面量的==比较结果为false。但一些现代编译器(如gcc)能够将相同的字符串字面量映射同一个无名字符数组,因此==比较结果为true。正因这个行为,所以我们不应编写依赖特殊编译器的代码!
#include
#include
int main(){
#define S1 "Hello World!"
#define S2 "Hello World!"
//S1和S2存于常量区中,因两者内容完全一样,存于全局只读区。
//一些现代的编译器会将S1和S2映射到同一个字符数组中。
if(S1 == S2) //gcc、Vc下相等,bcc下不相等!
{
printf("Equal\n");
}else{
printf("Non Equal\n");
}
if(strcmp(S1,S2) == 0) //判断两个字符串是否相等。
{
printf("Equal\n");
}else{
printf("Non Equal\n");
}
return 0;
}
(1)void right_shift_r(const char* src,char* result,unsigned int n);
(2)函数说明:
①函数功能:将输入字符串src循环右移n位,result为输出结果
②要求:以效率最高的方式实现
③示例:"abcde" →循环右移2位→"deabc";"abcde" →循环右移8位→"cdeab"
#include
#include
void right_shift_r(const char* src, char* result, unsigned int n)
{
const unsigned int LEN = strlen(src);//字符个数(不含\0)
int i=0;
//时间复杂度O(n),效率高!
for(i = 0;i
(1)字符串的长度就是字符串所包含字符的个数
(2)字符串长度指的是第一个'\0'字符前出现的字符个数,通过'\0'结束符来确定字符串的长度。
(3)函数strlen用于返回字符串的长度(不含'\0')。
(1)字符串相关的函数均以第一个出现的'\0'作为结束符
(2)编译器总是会在字符串字面量的末尾添加'\0'。
(3)字符串字面量的本质为数组
字符串、字符数组、字符指针是不同的3个概念。这个一定要有一个清楚的认知。
#include
#include
int main(){
#define STR "Hello, \0World!\0"
char* src = STR;
char buf[255] = {0};
snprintf(buf, sizeof(buf), src);
printf("strlen(STR) = %d\n", strlen(STR)); //7
printf("sizeof(STR) = %d\n", sizeof(STR)); //16,字符串字面量变质为
//字符数组,并编译器
//在最后加一个'\0'
printf("strlen(src) = %d\n", strlen(src)); //7,遇第1个\0结束
printf("sizeof(src) = %d\n", sizeof(src)); //4,指针大小
printf("strlen(buf) = %d\n", strlen(buf)); //7,遇第1个\0结束
printf("sizeof(buf) = %d\n", sizeof(buf)); //255,buf数组的大小
printf("src = %s\n", src); //hello, 共6个字符,含一个空格
printf("buf = %s\n", buf); //hello, 共6个字符,含一个空格
return 0;
}
string 是定义一个字符串,存储的是一段如“abcd”的数据,而且最后还有一个结束符'\0'。而 string str = "a" 是C++ 封装好的string。C++中的char string和string不是一回事。当用到了"string"这个关键词,就不是普通的字符串,而是用到了封装后的类。
在C++中,char仍然是一个primitive type(原始类型),而string已经经过封装,成为了一个class(类)用到它时,我们需要 #include
string 类没有接收一个整型参数或一个字符型参数的构造函数。下面的两种写法是错误的:
可以用 char* 类型的变量、常量,以及 char 类型的变量、常量对 string 对象进行赋值。例如:
string 类还有 assign 成员函数,可以用来对 string 对象赋值。assign 成员函数返回对象自身的引用。例如:
length 成员函数返回字符串的长度。size 成员函数可以实现同样的功能。
除了可以使用+
和+=
运算符对 string 对象执行字符串的连接操作外,string 类还有 append 成员函数,可以用来向字符串后面添加内容。append 成员函数返回对象自身的引用。例如:
除了可以用 <、<=、==、!=、>=、> 运算符比较 string 对象外,string 类还有 compare 成员函数,可用于比较字符串。compare 成员函数有以下返回值:小于 0 表示当前的字符串小;等于 0 表示两个字符串相等;大于 0 表示另一个字符串小。
substr 成员函数可以用于求子串 (n, m),原型如下:string substr(int n = 0, int m = string::npos) const。调用时,如果省略 m 或 m 超过了字符串的长度,则求出来的子串就是从下标 n 开始一直到字符串结束的部分。例如:
swap 成员函数可以交换两个 string 对象的内容。例如:
string 类有一些查找子串和字符的成员函数,它们的返回值都是子串或字符在 string 对象字符串中的位置(即下标)。如果查不到,则返回 string::npos。string: :npos 是在 string 类中定义的一个静态常量。这些函数如下:
replace 成员函数可以对 string 对象中的子串进行替换,返回值为对象自身的引用。例如:
erase 成员函数可以删除 string 对象中的子串,返回值为对象自身的引用。例如:
insert 成员函数可以在 string 对象中插入另一个字符串,返回值为对象自身的引用。例如:
使用流对象 istringstream 和 ostringstream,可以将 string 对象当作一个流进行输入输出。使用这两个类需要包含头文件 sstream。在C语言中,printf函数可以按照某种格式将数据打印出来,在C++中如果是想先将数据按照某种格式进行组装,然后再输出这个特定格式的字符串,这个时候流操作能够很好的满足需求。
#include
#include
#include
using namespace std;
int main()
{
string src("Avatar 123 5.2 Titanic K");
istringstream istrStream(src); //建立src到istrStream的联系
string s1, s2;
int n; double d; char c;
istrStream >> s1 >> n >> d >> s2 >> c; //把src的内容当做输入流进行读取
ostringstream ostrStream;
ostrStream << s1 << endl << s2 << endl << n << endl << d << endl << c <
第 11 行,从输入流 istrStream 进行读取,过程和从 cin 读取一样,只不过输入的来源由键盘变成了 string 对象 src。因此,"Avatar" 被读取到 s1,123 被读取到 n,5.2 被读取到 d,"Titanic" 被读取到s2,'K' 被读取到 c。
第 12 行,将变量的值输出到流 ostrStream。输出结果不会出现在屏幕上,而是被保存在 ostrStream 对象管理的某处。用 ostringstream 类的 str 成员函数能将输出到 ostringstream 对象中的内容提取出来。
string 对象也可以看作一个顺序容器,它支持随机访问迭代器,也有 begin 和 end 等成员函数。STL 中的许多算法也适用于 string 对象。下面是用 STL 算法操作 string 对象的程序示例
#include
#include
#include
using namespace std;
int main()
{
string s("afgcbed");
string::iterator p = find(s.begin(), s.end(), 'c');
if (p!= s.end())
cout << p - s.begin() << endl; //输出 3
sort(s.begin(), s.end());
cout << s << endl; //输出 abcdefg
next_permutation(s.begin(), s.end());
cout << s << endl; //输出 abcdegf
return 0;
}
进行数值转换需要注意字符串是通过'\0'字符结束的。涉及到char []字符数组与其它类型转换,一般需要进行拷贝,不能直接赋值实现。char []和char *都可以通过构造新的string完成其对string的转换。涉及到到char *转换,需要注意类型一致,同时注意const的使用。
// char []与char *之间转换,char []转char *:直接进行赋值即可
// char[] 转char *
char str[] = "lala";
char *str1 = str;
cout << str1 << endl;
// char*转char[]:字符拷贝实现,不能进行赋值操作,同事需要注意char[]的长度防护
// char*转换为char[]
const char *st = "hehe";
char st1[] = "lalalala";
strncpy(st1, st, strlen(st) + 1); // 注意加1操作
// tp = temp; //错误,不能实现
cout << st1 << endl;
// char 与const char 之间转换,const char 转char :拷贝实现,不能进行赋值
// const char *转char *
const char *st = "lala";
// 直接赋值不可以
//char *st1 = st; // (不可以编译器报错)
//cout << st1 << endl;
// 另外开辟空间,将字符一个一个复制过去
char *ncstr = new char[strlen(st) + 1];
strcpy(ncstr, st);
cout << ncstr << endl;
// char 转const char :直接进行赋值
// char *转const char *
char *st = "hehe"; // (编译提示警告)
const char *st1 = st;
cout << st1 << endl;
// char *与string之间转换,char *转string:1)直接赋值;2)构造转换实现
// char*转换为string
// (注意,定义char *变量,并直接赋值,最好定义为const变量,否则编译器警告)
const char *st = "hello";
// 赋值转换
string st1 = st;
cout << st1 << endl;
// 构造转换
string s1(st, st + strlen(st));
cout << s1 << endl;
// 改变const char *变量值
st = "lalala";
cout << st << endl;
// string转char *:赋值操作(注意类型转换)
// string转char *
string st = "My test";
//char *st1 = st; // 错误类型不同
//char *st1 = st.c_str(); // 错误类型不同
char *st1 = const_cast(st.c_str()) ;
cout << st1 << endl;
// char[]与string之间转换
// char []转string:1)直接赋值;2)构造转换实现
// char[]转换为string
char st[] = "hello";
// 直接赋值实现
string st1 = st;
cout << st1 << endl;
// 构造实现
string st2(st, st + strlen(st));
cout << st2 << endl;
// string转char[]:拷贝实现,不能直接赋值
// string转char []
string ts = "My test1";
//char ts1[] = ts; // 错误
//char ts1[] = const_cast(ts.c_str()); // 错误
char ts1[] = "lalallalalaaaa";
strncpy(ts1, ts.c_str(), ts.length() + 1); // 注意,一定要加1,否则没有赋值'\0'
cout << ts1 << endl;