C++ 字符串、string、char *、char[]、const char*的转换和区别
1.字符串
字符串本质就是一串字符,在C++中大家想到字符串往往第一反应是std::string(后面简称string)
字符串得从C语言说起,string其实是个类,C语言是没有class的,所以C语言的字符串其实就是字符数组,也就是char [ ] ,例如:
char str[10]; //定义了一个有十个元素的数组,元素类型为字符char
char str[10] = {"hello"}; //"h e l l o \0"五个字符赋给str数组, 然后用‘\0’填满数组剩余元素
为什么要加上'\0'?,‘\0’代表空格符,在字符串结尾加上‘\0’,代表字符串已经结束,读到\0的时候会停下来,不然会沿着内存地址一直读下去,读到什么乱七八糟的东西就不知道了,比如会读到类似 “烫烫烫烫”的东西。。。
那我如果让数组元素全部为其他字符,不放\0会怎么样呢? 可以这样,如下:
char str[4] = {"abcd"}; //会报错
编译器会报错,不能把“const char[5]” 类型的值不能用于初始化“char [4]”类型的实体
这里可以看到,编译器是把"abcd"作为“abcd\0”来处理的,有五个字符
那如果就只要装四个字符呢,可以这样,如下:
char str1[4] = { ‘a’ ,'b', 'c', 'd' }; //这样就没'\0'了,可是这样的话,使用str1来表示字符串也失去了意义
输出str1,std::cout << str1 << std::endl; 会变成这样:
为什么cout << str1 读取 str1 就能读取到 abcd呢?
这是因为C中规定数组名 就代表数组所在内存位置的首地址,也是 str1[0]的地址,即str = &str[0];
可以理解成读取str1 的时候其实是在访问 abcd中 a的地址。。
C语言中操作字符串是通过它在内存中的存储单元的首地址进行的,这是字符串的本质
string、char*、char[]、const char *
看一下这四个分别是什么类型:
int main()
{
char *p;
auto s = "111"; //可以看到 "aaa"这样的类型 其实代表 const char *
std::string str = "222";
char a[] = "hello";
std::cout << typeid(p).name()<< std::endl;
std::cout << typeid(s).name() << std::endl;
std::cout << typeid(str).name() << std::endl;
std::cout << typeid(a).name() << std::endl;
return 0;
}
输出如下:
1.char * //字符指针,指向字符的指针
2."aaa"这样的类型 其实代表 const char *,字符串常量
3.string 是std::basic_string模板类的实例化,是一个类...,string str="aaa"; 其实是 const char *转class ,string重载了=号,把“aaa”封装成std::string
4.char a[8]; // a的类型是 char [8],如果是char a[6]; 则a的类型就是char [6] 既长度为N的字符数组
string、char*、char[]、const char *相互转换
如下表:
转化规律总结下:
1.转化成char[],可以用strcpy_s ,或者遍历字符串的方式
string 转char[] : strncpy_s(a, string.c_str(), N); 也可以用上图的遍历string
const char * 转char[] : strcpy_s(a, const char *); 也可以用上图的strncpy_s
char * 转char[] : strcpy_s(a, char *); 也可以用上图的strncpy_s
2.char[]变成别的,直接赋值
3.转化为std::string 最简单,可以直接=, 因为string太强大了,把=号重载了很多遍
4.const char *转化到 char * 使用const_cast<char *>
5.string转化为char * 用c_str()
for循环中的陷阱:
char** ppInsId=new char*[50]; 首先解释下这一句:
char*[50] ,因为[]的优先级高,所以是一个数组,数组元素为指针
new char*[50] 意为开辟一块内存,存放50个char*指针的内存空间 ,大小为sizeof(char*)*50 =200 个字节
而char** ppInsId 是二级指针,因为右边是数组,而数组的元素为char型指针,所以指向指针的指针,既为2级指针,char** ppInsId就代表指向内存首地址,也就是一个char*指针的 指针
对ppInsId 可以用下标访问代表数组第几个元素,也就是第几个char *指针
#include<iostream>
using namespace std;
#include <vector>
std::vector<string> vstr;
void makeData(std::vector<string> _vect)
{
char** ppInsId=new char*[50]; //定义了一个二级指针
for(int i=0;i<_vect.size();i++)
{
std::string str=_vect[i];
char *s =const_cast<char*>(str.c_str());
ppInsId[i]=s;
}
std::cout<<ppInsId[0]<<std::endl; //出了循环,ppInsId[0]和ppInsId[1]都变成了""空
std::cout<<ppInsId[1]<<std::endl;
}
int main()
{
vstr.push_back("aaaa");
vstr.push_back("bbbb");
makeData(vstr);
return 0;
}
这个例子里,输出ppInsId[0] 预想是aaaa, ppInsId[1]预想是 bbbb,实际上却都是“ ” 空
按理说,每个for{}里面都新定义了s,两个s应该不一样才对,确实在C#,java中是一样的
原因是char *s 是在for{ }里定义的,第一次循环时ppInsId[0] 被赋值为aaaa,一旦第一次循环结束,就s这个变量和s指向的内存立马被释放掉了,ppInsId[0] 为空,然后第二次循环又定义了一个新的s,可是这个s的地址又指向了那个地址,也就是两个s指向的地址是一样,然后ppInsId[1]都变成了bbbb,因为ppInsId[0]和ppInsId[1]指向的地址一样 ,s是有两个,但是两个for把s的地址刚好是一样的,然后第二次循环结束,s被释放ppInsId[0]和ppInsId[1]都变成了空。。。
这里有个插曲:相同的代码在vs2017和coldblocks的编译出来的结果不一样
vs中出了for循环后,ppInsId[0] ,[1]都为空了,已经被释放,和我预想的一样,不知为何codeblocks 还能输出两个bbbb
应该是编译器不一样导致的:
vs2017的c++编译器是:cl.exe,是控制Microsoft C 和C++ 编译器以及链接器的工具。cl.exe 只能在支持Microsoft Visual Studio 的操作系统中运行
而codeblock是不安装编译器的,需要自己配置,我配置的是Mingou的gdb.exe
那么怎么改呢。。
char *s =const_cast<char*>(str.c_str());
ppInsId[i]=s;改为:
char a[10];
strncpy_s(a, str.c_str(), strlen(str.c_str()));
ppInsId[i] = a;通过数组的方式,在用strcopy 把值拷贝进去
但是改成char a[10]后也有问题,输出的是两个bbbb,原因跟上面char *s 一样,第一次循环结束后释放了a,然后第二次进来又把a指到了之前的地址,因为ppInsId[0]的地址还是那个,所以两个都变成了bbbb
所以继续改,改成在外面定义一个二维数组:
char** ppInsId = new char*[50];
char a[50][10];
for (int i = 0; i < _vect.size(); i++)
{
std::string str = _vect[i];
strncpy_s(a[i], str.c_str(), strlen(str.c_str()));
ppInsId[i] = a[i];
}
std::cout << ppInsId[0] << std::endl;
std::cout << ppInsId[1] << std::endl;
这样既可,完成预想中的p[0]为aaaa,p[1]为bbbb
总结:
1.一定要使用strcpy()函数等来操作方法c_str()返回的指针
最好不要这样:
char* c;
string s="1234";c = s.c_str(); //c最后指向的内容是垃圾,因为s对象被析构,其内容被处理
//应该这样用:
char c[20];
string s="1234";
strcpy(c,s.c_str()); //这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作
2.在循环内部或者一块作用域内,定义变量要注意被释放的情况
最好放到循环外定义
C++ 字符串、string、char *、char[]、const char*的转换和区别的更多相关文章
- [基础-001]C++字符串转换(char*,const char*,string)
1. string转const char* string str ="abc"; const char* charArr = str.c_str(); 2. const char* ...
- 字符串复制char *strcpy(char* dest, const char *src);
⒈strcpy的实现代码 char * strcpy(char * strDest,const char * strSrc) { if ((NULL==strDest) || (NULL==strSr ...
- C语言中strcpy(char *strDest, const char *strScr)字符串复制库函数的理解与分析
1.原版的strcpy()函数原型 char * strcpy( char *strDest, const char *strSrc ) { assert( (strDest != NULL) &am ...
- 实战c++中的string系列--string与char*、const char *的转换(data() or c_str())
在project中,我们也有非常多时候用到string与char*之间的转换,这里有个一我们之前提到的函数 c_str(),看看这个原型: const char *c_str(); c_str()函数 ...
- char*,const char*和string 三者转换
1. const char* 和string 转换 (1) const char*转换为 string,直接赋值即可. EX: const char* tmp = "tsinghua&quo ...
- C++ char*,const char*,string,int 的相互转换
C++ char*,const char*,string,int 的相互转换 1. string转const char* string s ="abc";const char* ...
- 【转载】char*,const char*和string 三者转换
本文转自 http://blog.csdn.net/perfumekristy/article/details/7027678 const char* 和string 转换 const char*转换 ...
- char* 、const char*和string之间的转换
1. const char* 和string 转换 (1) const char*转换为 string,直接赋值即可. EX: const char* tmp = "tsinghua ...
- C#中数据类型char*,const char*和string的三者转换
C#中数据类型char*,const char*和string的三者转换: . const char* 和string 转换 () const char*转换为 string,直接赋值即可. EX: ...
随机推荐
- 将sparkStreaming结果保存到Redshift数据库
1.保存到redshift数据库的代码 package test05 import org.apache.log4j.{Level, Logger}import org.apache.spark.rd ...
- JS事件 卸载事件 当用户退出页面时(页面关闭、页面刷新等),触发onUnload事件,同时执行被调用的程序。注意:不同浏览器对onunload事件支持不同。
卸载事件(onunload) 当用户退出页面时(页面关闭.页面刷新等),触发onUnload事件,同时执行被调用的程序. 注意:不同浏览器对onunload事件支持不同. 如下代码,当退出页面时,弹出 ...
- GetOpenFilename的基本用法
GetOpenFilename '一.概述基本语法 Application.GetOpenFilename 方法 显示标准的“打开”对话框,并获取用户文件名,而不必真正打开任何文件,只是把打开文件名称 ...
- Maven - 深入理解maven构建生命周期和各种plugin插件
作者:亚当-adam 来源:CSDN 原文:https://blog.csdn.net/zhaojianting/article/details/80321488 版权声明:本文为博主原创文章,转载请 ...
- 【JZOJ6376】樱符[完全墨染的樱花]
description analysis 无向图上任意两点最大流不超过\(2\),说明该图是仙人掌 由于最大流等于最小割,如果一条边在两个环里,断掉两个端点至少需要\(3\)的代价 对于该仙人掌,求两 ...
- mysql 的特殊函数
1. FORMAT() 函数 函数用于对字段的显示进行格式化 语法: SELECT FORMAT(column_name,format) FROM table_name column_name 必需 ...
- 校园商铺-4店铺注册功能模块-3thumbnailator图片处理和封装Util
1. 初步使用thumbnailator 1.1 下载依赖 <!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator - ...
- jquery学习笔记(四):动画
内容来自[汇智网]jquery学习课程 4.1 显示和隐藏 在jQuery中使用 hide() 和 show() 方法来隐藏和显示 HTML 元素: hide()的语法形式:$(selector).h ...
- Socket.EndReceive 方法 (IAsyncResult)
.NET Framework (current version) 其他版本 .NET Framework 4 .NET Framework 3.5 .NET Framework 3.0 . ...
- 云-腾讯云-云点播:云点播(VOD)
ylbtech-云-腾讯云-云点播:云点播(VOD) 提供端到端的一站式VpaaS音视频点播解决方案 1.返回顶部 1. 云点播(Video on Demand,VOD)基于腾讯多年技术积累与基础设施 ...