我在刷算法題的時候經常遇到,用了' '出現警告或者使用" "直接報錯,尤其是在字符串部分(py玩家后遺癥/(ㄒoㄒ)/~~)在詳細了解后總結一下加強記憶。
總的來說在 C++ 中,雙引號?""
?和單引號?''
?是完全不同的語法元素,數據類型、存儲方式有所差異。
對比維度 | 單引號?'' (字符常量) | 雙引號?"" (字符串常量) |
---|---|---|
數據類型 | char | const char[] (字符數組) |
存儲內容 | 單個字符的 ASCII 碼 | 字符序列 + 末尾?'\0' ?結束符 |
占用字節 | 固定 1 字節 | 字符數 + 1 字節(含?'\0' ) |
內容限制 | 僅 1 個字符(非空) | 0 個或多個字符(可空) |
賦值對象 | char ?變量 | const char* ?/?char[] |
隱式轉換 | 可轉?int (ASCII 碼) | 可轉?const char* (首地址) |
示例 | '5' 、'\t' 、'Z' | "" 、"abc" 、"x\ny" |
一、核心定義與數據類型
這是最根本的區別:單引號表示字符常量(char
?類型),雙引號表示字符串常量(const char[]
?類型,本質是字符數組)。
符號 | 表示內容 | 數據類型 | 本質含義 |
---|---|---|---|
'a' | 單個字符常量 | char | 單個 ASCII 字符 |
"a" | 字符串常量 | const char[] | 以?'\0' ?結尾的字符數組 |
二、存儲方式與字節長度
兩者在內存中的存儲形式和占用字節數完全不同,這是導致后續所有區別的根源。
1. 單引號(字符常量)
- 存儲:直接存儲單個字符的?ASCII 碼值,占用?1 個字節(
char
?類型的標準大小)。 - 示例:
'A'
?在內存中存儲為 ASCII 碼?65
(二進制?01000001
),僅占 1 字節。 - 長度計算:
sizeof('A') == 1
(固定為 1,與字符無關)。
2. 雙引號(字符串常量)
- 存儲:本質是?字符數組,除了顯式的字符外,末尾會自動添加一個?
'\0'
?空字符(作為字符串結束標志),總占用字節數 = 顯式字符數 + 1。 - 示例:
"A"
?在內存中實際是?{'A', '\0'}
,占用 2 字節;
"hello"
?是?{'h','e','l','l','o','\0'}
,占用 6 字節。 - 長度計算:
sizeof("A") == 2
(包含?'\0'
?的總字節數);strlen("A") == 1
(strlen
?統計?'\0'
?之前的字符數,需包含?<cstring>
?頭文件)。
三、語法規則:內容限制不同
C++ 對兩者的語法有嚴格限制,違反會導致編譯錯誤。
1. 單引號?''
?的限制
- 只能包含 1 個字符:不能為空(
''
?編譯錯誤),也不能包含多個字符('ab'
?是「多字符常量」,語法不推薦,值為各字符 ASCII 碼的組合,結果不確定)。 - 合法示例:
'5'
、'+'
、'\n'
(轉義字符,本質是單個字符,ASCII 碼為 10)。 - 非法示例:
''
(空字符常量,錯誤)、'12'
(多字符,不推薦)。
2. 雙引號?""
?的限制
- 可包含 0 個或多個字符:空字符串?
""
?合法(僅存儲?'\0'
,占 1 字節),多字符也合法(如?"123abc"
)。 - 支持轉義字符:如?
"a\nb"
?表示?'a'
、換行符?'\n'
、'b'
、'\0'
,共 4 字節。 - 合法示例:
""
(空字符串)、"Hello, C++"
、"x\ty"
(包含制表符)。 - 非法示例:無本質非法內容,但需注意轉義字符的正確性(如?
"a\b"
?合法,"a\z"
?非法,因為?\z
?不是有效轉義符)。
下面代碼最能體現區別
string s3 =s1+s2;//bwabwacout << s3<<endl;//-------------插入push_back()和insert() push_back為void類型,刪除erase()s1.push_back('c');string s4 = s1; //bwacs4.insert(s4.begin(),'i');//ibwacs1.erase(s1.begin()+3); //bwa s1.erase(s.begin() + 1, s.begin() + 3); 刪除范圍//--------------replace() repalce()函數:s.replace(pos,len,ss),將s從pos開始的len個字符替換成ss,s3.replace(2,2,"aa"); //bwabwa->bwaawacout<<s3<<endl;//s.replace(pos,n1,n2,c),將s從pos開始的n1個字符替換成n2個字符c。s3.replace(2,2,1,'z'); //bwaawa-> bwzwa
四、如何選擇區分
兩者的使用場景嚴格依賴于變量 / 函數參數的類型,混用會導致編譯錯誤。
1. 單引號?''
?的使用場景
- 給?
char
?類型變量賦值:char c1 = 'a'; // 正確:char 變量匹配字符常量 char c2 = 65; // 正確:等價于 'A'(ASCII 碼) char c3 = "a"; // 錯誤:字符串常量(const char[])無法賦值給 char 變量
- 作為?
char
?類型參數傳遞給函數:putchar('B'); // 正確:putchar 接收 char 類型 cout << 'c'; // 正確:輸出 ASCII 碼對應的字符(顯示 'c')
2. 雙引號?""
?的使用場景
- 給字符串相關類型賦值(字符指針、字符數組):
const char* s1 = "hello"; // 正確:字符串常量賦值給字符指針(指向常量區) char s2[] = "world"; // 正確:字符串常量初始化字符數組(會復制到棧區) char s3[6] = "hello"; // 正確:數組大小 = 字符數 + 1(包含 '\0') char s4[5] = "hello"; // 錯誤:數組大小不足(需 6 字節,實際僅存 5 個字符,無 '\0')
- 作為字符串參數傳遞給函數:
cpp
puts("Hi!"); // 正確:puts 接收字符串常量 strlen("test"); // 正確:計算字符串長度(結果為 4) cout << "C++"; // 正確:輸出字符串(自動識別 '\0' 結束)
五、隱式轉換與兼容性
1. 字符常量('c'
)的隱式轉換
- 可隱式轉換為?
int
?類型(值為對應的 ASCII 碼):int num = '0'; // 正確:num = 48('0' 的 ASCII 碼) cout << 'z'; // 輸出 122('z' 的 ASCII 碼)
2. 字符串常量("c"
)的隱式轉換
- 可隱式轉換為?
const char*
?類型(指向字符串的首地址),但不能轉換為?int
?或?char
:const char* p = "abc"; // 正確:p 指向 'a' 的地址 int len = "abc"; // 錯誤:字符串無法直接轉換為 int
- 字符串常量是只讀的:賦值給非?
const
?指針會導致未定義行為(C++11 后編譯器會警告):char* p = "hello"; // 不推薦:"hello" 是 const,修改 p[0] 會崩潰 const char* p = "hello";// 正確:明確只讀,避免誤修改
六、特殊情況:空值與轉義字符
1. 空值處理
- 字符常量無 “空值”:
''
?非法,若需表示空字符,需用轉義字符?'\0'
(ASCII 碼 0):char null_char = '\0'; // 正確:表示空字符(占 1 字節)
- 字符串常量可有空值:
""
?是合法的空字符串,僅包含?'\0'
(占 1 字節):const char* empty_str = ""; cout << strlen(empty_str); // 輸出 0(無有效字符) cout << sizeof(empty_str); // 輸出 8(指針大小,64 位系統) cout << sizeof(""); // 輸出 1(僅 '\0')
2. 轉義字符的使用
- 單引號中:轉義字符需用?
\
,如?'\''
?表示字符?'
,'\n'
?表示換行符。 - 雙引號中:轉義字符同樣需用?
\
,如?"\""
?表示字符串?"
,"a\\b"
?表示字符串?a\b
。
常見錯誤示例與原因
- 錯誤 1:字符串賦值給?
char
?變量char c = "a"; // 錯誤:類型不匹配(const char[] → char)
- 錯誤 2:單引號包含多個字符
char c = 'ab'; // 不推薦:多字符常量,值為 0x6162(依賴編譯器,結果不確定)
- 錯誤 3:字符串數組未留?
'\0'
?空間char arr[3] = "abc"; // 錯誤:"abc" 需 4 字節(含 '\0'),數組僅 3 字節,截斷導致無結束符
- 錯誤 4:修改字符串常量
char* s = "hello"; s[0] = 'H'; // 未定義行為:"hello" 是只讀常量,修改會崩潰