C和C++中字串符(String)和字串字面量(String Literal)的區別
C/C++中的物件指的是一塊儲存區。字串字面量是不需要建立過程就可以使用的物件,所以它既沒有變數那樣的宣告或者定義(字串字面量是無名物件),也不需要向動態分配的物件那樣進行動態分配。由於這個原因,用來限定變數的型別限定符(如const、volatile)以及儲存類別指示符(如extern、static、auto、register)不能用在字串字面量上。
陣列型別
字串字面量是陣列型別的物件,因而具有陣列的一切特點。關於這一點在下面還會進一步說明。
靜態儲存期
C/C++中物件的生存週期按照其儲存性質可分為三類:靜態儲存期(static storage duration)、自動儲存期(automatic storage duration)以及動態儲存期(dynamic storage duration)。相應地,物件可根據儲存期性質分為靜態物件、自動物件和動態物件三種。
字串字面量是靜態物件,所以在程式執行期間會一直存在。字串字面量是左值,而且是不可被更改的左值。
例如,char s[] = "hello"中的"hello"是陣列型別的左值(lvalue),用於初始化s陣列;
sizeof("hello")
以及
&"hello"
中的“hello”也是左值。在這些情況下,“hello”處於左值語義上下文環境中,所以不會產生下面將要提到的陣列轉換為指標的現象。
另外,有些運算不但要求其運算元是左值,還要求可變。例如,對物件進行賦值、自加、自減等運算。因為陣列是不可被更改的左值,所以不能對陣列進行這些操作,也就說不存在陣列型別的賦值、自加、自減等運算。
字串字面量可以轉換為指向其第一個字元的指標。
處於右值語義環境中的字串字面量將被預設轉換為指向第一個字元的指標。例如,
char* p = "hello"
中的“hello”在轉換為字元指標後用於初始化指標變數p;表示式
“hello”[0]
(相當於
*("hello"+0)
或者
*"hello"
)中的“hello”也是同樣轉換為指標後參與下標運算,等等。
這種性質也是陣列型別的特點。在右值語義環境下,一般型別的物件表示的值是由其儲存內容決定的;而陣列型別的物件與此不同,它代表的值不是來源於陣列物件首元素所在的地址。這是陣列最為特殊的地方,也是人們容易產生誤解的地方。
取址運算
字串字面量是一個可取址的物件。例如:&"hello"是合法的表示式。
地址常量
靜態物件的地址在編譯期間即可被確定,所以其地址(如
&"hello"
)是常量;而字串字面量又可以從陣列型別自動轉換為指標(如
“hello”
轉換為指標後等同於
&"hello"[0]
),所以字串字面量可以直接作為地址常量表達式來使用。
修改字串字面量的行為是無意義的
下面的操作都試圖修改字串字面量中的第一個字元從而修改字串字面量,所以其結果是無定義的(Undefined)的:
"hello"[0] = 'A' //報錯 Undefined
char * p = "hello";
*p = ‘A’; //報錯 Undefined
使用了無定義行為的程式是錯誤的;避免在程式中出現無定義行為是程式設計師的責任。
區別點:在型別限定上的不同
C中的字串字面量"hello"是陣列型別char[6](相應地,每個字元元素是無const限定的char型);作為右值使用的時候轉換為指標型別char*。
在C++中“hello”是char const[6]型(相應地,每個元素的型別是char const);轉換為指標使用的時候是char const*,在特殊情況下也可以是char*。
之所以在C中字串字面量不是const陣列(也就是說每個字元元素的型別不是char const),是因為C要照顧或者考慮到標準制定之前已經存在的大量程式碼--那時的c語言還沒有const關鍵字,如果硬性規定為const陣列,則char *p = "hello"這樣的初始化或者char *q; q="hello"這樣的賦值就是非法的了(因為右邊的型別char const*不能預設轉換為左邊的型別char*)。
同樣,為了使上述程式碼能順利通過編譯過程,C++採取了另外一種策略:它規定了字串字面量的型別是const陣列,同時有特別規定了字串字面量也可以有限制地轉換為指向非常量的指標(對於"hello"來說就是char*),從而解決了上述程式碼中存在的問題。不過,轉換到char*主要是為了相容以前的程式碼,這種轉換被C++標準標記為“Deprecated”,所以在寫程式時不應該依賴於這種轉換。
C++中的字串字面量是常量,而在C中不是常量。
正是由於標準在型別上的不同規定造成了在C和C++中字串字面量常量性質上的差別。
在C中,除了string literals和compound literals(C99)之外,其他的字面量都是常量;而在C++中,包括string literals在內的所有literals都是常量(注意:C++中不存在compound literals)。
在現實中,經常可以看到用“字串常量”來指代“字串字面量”的情況,其實對於C來說這是不正確的,因為在C中字串字面量不屬於常量;而對於C++來說,“字串常量”和“字串字面量”實際上是一回事,只不過看問題的角度不同罷了。
順便提一下:C++中的常量可以有物件的常量(如字串字面量、const限定的物件)和非物件的常量之分,而C中的常量不包括物件,它們最明顯的特徵就是不能進行取值運算,因此常量只能作為右值來使用。
語法及語義上的區別
C中的字串字面量不是常量,它的每個字元元素也不是常量,所以字元元素的不可變性僅僅表現在語義層面,但在語法和約束條件上沒有要求。而C++中的字串字面量是常量,每個字元元素也是常量,因此在語義和約束條件兩方面都要求不能改變其中的每個字元元素;另外,處於相容性考慮C++還存在著特殊情況下的向非const指標的轉換。
下面用具體的嗲嗎來對以上內容進行說明。
*"hello" = 'A';
表示式*"hello"代表字串字面量的第一個字元元素物件。上述語句試圖通過賦值操作改變第一個元素,當然這樣的行為在C和C++中都是無定義的。除了這個相同點外,還有如下的一些細微的區別:
在C++中,*“hello”是一個const物件(其型別是const char。注意:這裡的"hello"不會轉換為char*指標,所以*"hello"不會是char型別),所以上述賦值違反了賦值號做運算元必須是一個可被改變
的左值的約束條件。在此情況下,標準要求會給出診斷資訊。
在C中,*"hello"是一個非const物件(其型別是char),是一個可被改變的左值,所以不違反賦值的約束條件。在此情況下,儘管這個賦值操作是未定義的,標準對診斷資訊沒有要求。
char* p = "hello";
char* q;
q = "hello";
void foo(char*s);
foo ("hello");
上面的初始化和賦值語句中的"hello"都能轉換為char*指標型別,所以都是合法的。在C++中,儘管"hello"作為指標使用時是char const*型別,在此情況下(如果不是char*型別則初始化或者賦值不能成立)基於對字串字面量的特殊規定使它可以轉換為char*使用。
要注意C++中字串字面量轉換為指向非常量的指標是有限制的,僅僅在有明確的名錶型別要求的情況下才能進行這樣的轉換,否則是非法的。比如下面的情況:
char *p = “hello” + 1;
char *q;
q = "hello"+1;
void foor(char*s);
foo("hello" + 1);
上述是合法的C程式碼,但是作為C++程式碼是非法的。非法的原因在於:”hello“轉換為char const*指標型別,而不能轉換為char*,因為+運算子對於其運算元的型別沒有轉換為char*這樣的直接的要求(因為無論是char const*還是char*都能進行指標加法運算),所以孩子真假挨罰表示式的結果仍然是char const*型別。這樣,上面指標的初始化或賦值操作就違反了在型別上的約束條件,需要給出診斷資訊。
相關推薦
C和C++中字串符(String)和字串字面量(String Literal)的區別
轉自:http://www.360doc.com/content/12/0511/19/7775902_210379219.shtml C/C++中的物件指的是一塊儲存區。字串字面量是不需要建立過程就可以使用的物件,所以它既沒有變數那樣的宣告或者定義(字串字面量是無名物件
C#讀取含中文字符的數據,失敗原因,和解決辦法
address p s tps home 拓撲 時代 res 筆試題 baidu C++內存檢測 如何理解設備樹中address-cells和size-cells 補《歡聚時代2017校招筆試題目(PHP工程師類)---錯題解析》 QGC地圖上任務項 銜擄懶蹤恃梢刻
方法:C#在WinForm中如何將Image存入SQL2000和取出顯示
close database 耗時 .exe while type comm res orm //保存 sql="insert into p_ry_photo(id,photo) values (‘"+id+"‘,@i)"; SqlComma
C巨集定義中##連線符與#符的含義
文章目錄 參考 note 不帶引數的巨集 附加說明: 帶引數的巨集 應題重點 有參巨集定義中#的用法 有參巨集定義中##的用法 實際的例子:
c/c++英文句子中單詞逆置(遞迴和非遞迴實現)
遞迴實現: #include <iostream> using namespace std; void change() { char str[1024] = {0}; if (scanf("%s", str) != EOF) { change(); } el
google C++ 程式設計規範中的禁用複製建構函式和賦值運算子
在google C++程式設計規範中有下面一段描述: 僅在程式碼中需要拷貝一個類物件的時候使用拷貝建構函式;不需要拷貝時應使用 DISALLOW_COPY_AND_ASSIGN。 定義:通過拷貝新建物件時可使用拷貝建構函式(特別是物件的傳值時)。 優點:拷貝建
c++建構函式中初始化列表的作用和機制
#include <iostream> #include <string> using namespace std; class base { private: int m_i; int m_j; public: base(int i): m_j(i), m_i(m_j){}
C++/C巨集定義中## 連線符與# 符的含義
轉載 ## 連線符號由兩個井號組成,其功能是在帶引數的巨集定義中將兩個子串(token)聯接起來,從而形成一個新的子串。但它不可以是第一個或者最後一個子串。所謂的子串(token)就是指編譯器能夠識別的最小語法單元。具體的定義在編譯原理裡有詳盡的解釋,但不知道也無所謂。
c++ 找陣列中兩個元素,其和等於固定值sum,並輸出
週二去中興面試的,筆試題做的是選擇填空,加一道程式設計題。 程式設計題求得是陣列中兩個元素,求和等於固定值sum。 這題最簡單的方法當然是採用雙重迴圈,進行遍歷。但是這種方法的效率無疑是比較低的。複雜度是O(n^2)。 所以我就從另外一個角度進行分析:1. 先對陣列進行排序
C結構體中賦值使用的冒號和點號
根據論壇中,別人的回答,總結試驗的成果 1、其中位域列表的形式為: 型別說明符 位域名:位域長度 struct bs { int a:8; int b:2; int c:6; }data; 說明data為bs變數,共佔兩個位元組。其中位域a佔8位,位域b佔2位,位域c
【Objective-C】OC中協議(Protocol)的基本概念和用法
協議(Protocol)的基本概念 協議的宣告看起來比較類似於Java中一個類的介面,但是和介面不同的是:協議沒有父類也不能定義例項變數。 協議是一種特殊的程式設計結構,用於宣告專
C# 9.0中引入的新特性init和record的使用思考
寫在前面.NET 5.0已經發布,C# 9.0也為我們帶來了許多新特性,其中最讓我印象深刻的就是init和record type,很多文章已經把這兩個新特性討論的差不多了,本文不再詳細討論,而是通過使用角度來思考這兩個特性。initinit是C# 9.0中引入的新的訪問器,它允許被修飾的屬性在物件初始化的時候
wprintf、wcout輸出中文和unicode中文字符串的轉換問題
fan 5% 轉換問題 int 字符串 ssa dst unicode zed %E4%BD%BF%E7%94%A8CHttpFile%E4%BB%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%AD%A3%E7%A1%AE%E7%9A%
c89和c99中/運算符和%運算符為負數時的區別
區別 str tro c99 根據 負數 cpu strong 除法 運算式 -8 / 5 = -1.6,在C89中取值為 -1 或 -2,C99的出現,CPU對除法的結果向零取整,上述運算式結果為 -1。 在C89和C99中都要確保 (a / b) * b + a % b
js中字符串轉base64和base64轉字符串
uri func var span clas 字符串 return nbsp 字符 var str = ‘阪井泉水‘; //console.log(encodeURI(str)); //console.log(btoa(encodeURI(str)));
Java中字符串的拼接和轉義
size images vpd pre c51 pro shadow src mar Java中字符串的拼接和轉義 package com.testToken.demo; public class Test { public static void main(St
python中格式符的應用%s,%d,%f以及format()的例項以及輸出格式
python中用%代表格式符,表示格式化操作,常用的操作有%s,%d,%r等. 1.%s,%r,%d分別表示字串以str(),rper(),以及十進位制整數表示,%f表示結果為浮點型,更多見下表:
node.js環境在Window和Mac中配置,已經安裝cnpm和配置Less環境
use usr 版本 htm args gin targe mpi ffffff Node.js 和cnpm安裝 最近準備學習vue.js,但首先需要配置電腦的環境。配置node.js。 1.在node(https://nodejs.org/en/)官網上下載安裝node.
Java字面量(Java直接量)和符號引用
1、Java字面量(Java直接量) int i = 1;把整數1賦值給int型變數i,整數1就是Java字面量, 同樣,String s = "abc";中的abc也是字面量。 資料型別 直接量描述 舉例 int 整數直接量(可用二、十、八
tensorflow 一個矩陣與多個矩陣相乘時的計算方法(二維和三維張量相乘為例)
當tensor1 的 shape 為[k, m, n], tensor2 的 shape 為 [n, p]時, 要將tensor1的後兩維構成的k個矩陣與tensor2中的矩陣做矩陣乘法得到 shape 為[k, m, p]的向量 解決辦法: 1,我們知道Tenso