C++ 字符串、string、char *、char[]、const char*的轉換和區別

1.字符串java

字符串本質就是一串字符,在C++中你們想到字符串每每第一反應是std::string(後面簡稱string)ios

字符串得從C語言提及,string實際上是個類,C語言是沒有class的,因此C語言的字符串其實就是字符數組,也就是char [ ] ,例如:
char  str[10];   //定義了一個有十個元素的數組,元素類型爲字符char
char  str[10] = {"hello"};  //"h e l l o \0"五個字符賦給str數組, 而後用‘\0’填滿數組剩餘元素c++

爲何要加上'\0'?,‘\0’表明空格符,在字符串結尾加上‘\0’,表明字符串已經結束,讀到\0的時候會停下來,否則會沿着內存地址一直讀下去,讀到什麼亂七八糟的東西就不知道了,好比會讀到相似 「燙燙燙燙」的東西。。。數組

那我若是讓數組元素所有爲其餘字符,不放\0會怎麼樣呢? 能夠這樣,以下:函數

char  str[4] = {"abcd"};   //會報錯
編譯器會報錯,不能把「const char[5]」 類型的值不能用於初始化「char [4]」類型的實體工具

這裏能夠看到,編譯器是把"abcd"做爲「abcd\0」來處理的,有五個字符ui

那若是就只要裝四個字符呢,能夠這樣,以下:spa

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的地址。。3d

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.在循環內部或者一塊做用域內,定義變量要注意被釋放的狀況 最好放到循環外定義

相關文章
相關標籤/搜索