zoukankan      html  css  js  c++  java
  • 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 "五个字符赋给str数组, 然后用‘’填满数组剩余元素

    为什么要加上''?,‘’代表空格符,在字符串结尾加上‘’,代表字符串已经结束,读到的时候会停下来,不然会沿着内存地址一直读下去,读到什么乱七八糟的东西就不知道了,比如会读到类似 “烫烫烫烫”的东西。。。

    那我如果让数组元素全部为其他字符,不放会怎么样呢? 可以这样,如下:

    char  str[4] = {"abcd"};   //会报错
    编译器会报错,不能把“const char[5]” 类型的值不能用于初始化“char [4]”类型的实体

    这里可以看到,编译器是把"abcd"作为“abcd”来处理的,有五个字符

    那如果就只要装四个字符呢,可以这样,如下:

    char  str1[4] = { ‘a’ ,'b', 'c', 'd' };          //这样就没''了,可是这样的话,使用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.在循环内部或者一块作用域内,定义变量要注意被释放的情况
    最好放到循环外定义

  • 相关阅读:
    团队开发day09
    团队开发day08
    团队开发day07
    python 字符串操作,获取任意字符串的方法(开头,结尾开始)
    ERIKA OS学习和使用总结
    解决win7无法正常进入睡眠的问题
    简单实用的Makefile
    js 签字插件
    html2canvas实现截取指定区域或iframe的区域
    jquery监听动态添加的input的change事件
  • 原文地址:https://www.cnblogs.com/kevinWu7/p/10163446.html
Copyright © 2011-2022 走看看