int *p=123;
char *s="hi";
我嘗試在谷歌上搜索這個問題,但沒有得到確切的答案。所以,問題是為什么當我們將數字代替地址分配給整數指標時編譯器會拋出錯誤,但是當 char 指標是分配字串或字串并將它們存盤在記憶體中,每個字符通過不同的記憶體地址。就像我在開頭放置的代碼一樣,當我宣告 int 指標并為其分配整數時,它會引發警告"warning: initialization of 'int *' from 'int' makes pointer from integer without a cast",但是當我宣告 char 指標而不是為其分配地址而是字符時,它可以毫無問題地編譯。由于 char 指標在記憶體中存盤每個字符及其自己的地址,所以 int 指標不應該將記憶體中的每個數字存盤為每個元素不同地址的陣列嗎?
uj5u.com熱心網友回復:
差異是由于兩個不同的事實,這兩個都是 C 的“怪癖”,起初可能會令人困惑。
第一個怪癖是當你有一個指標變數初始化時,它與賦值不同。如你所知,當你說
int *p = 123;
這意味著,“p是一個指向 的指標int,初始值為 123”。但是,如果我們p在一行定義變數,并在第二行使用普通的賦值陳述句給它一個值呢?很容易想象會是這樣的:
int *p;
*p = 123; /* WRONG */
但實際上它看起來像這樣:
int *p;
p = 123;
在帶有初始化的指標宣告/定義中,這*意味著您正在宣告一個指標變數,但這并不意味著您正在初始化指標所指向的內容。另一方面,在正則賦值運算式中,the*是一個運算子,意思是“我正在談論這個指標指向的記憶體位置”。所以當你說
*p = 123;
這意味著,“指標p指向某個記憶體位置,我想將值 123 存盤到該記憶體位置。” 另一方面,當你說
p = 123;
這意味著,“使指標p指向某個記憶體位置 123。” 在這種情況下,可能更清楚地說
p = 0x0000007b;
但是,當然,除非我們正在做某種專門的嵌入式編程,否則我們很少為我們想要訪問的記憶體位置選擇精確的數值。
但是讓我們回到你問的問題。為避免混淆,讓我們使用帶有初始化器的兩個宣告
int *p = 123;
char *s = "hi";
并將它們更改為單獨的宣告:
int *p;
char *s;
和任務:
p = 123;
s = "hi";
正如我們剛剛看到的,整數指標賦值p = 123并沒有做我們想要的,它甚至可能根本沒有做任何有用的事情。那么為什么字符指標賦值
s = "hi";
作業?
這與 C 的第二個怪癖有關,它分為兩部分。第一部分是,您可能知道,C 中的字串表示為字符陣列。(這很好,它甚至算不上“怪癖”。)但是第二部分是,每當你在運算式中提到一個陣列并嘗試獲取它的值時,你得到的值就是指向陣列第一個元素的指標。 但這意味著什么?
首先,當您"hi"在程式中提到字串常量時,編譯器會自動為您創建一個小陣列。效果就像你說的一樣
char __string_constant = { 'h', 'i', '\0' };
(也許這是第三個怪癖)。
然后,當你說
s = "hi";
就像你寫的一樣
s = &__string_constant[0];
這是一個很長的答案,但如果你仍然和我在一起,最重要的是,當你使用指標時,你必須清楚指標和它所指向的內容之間的區別. 當你說
char *s = "hi";
您正在處理兩件事:您正在將指標p指向某個地方,并且您正在安排它指向的位置有一個 string "hi"。但是當你說
int *p = 123;
你讓指標p指向位置 123,但你沒有做任何事情來影響記憶體位置 123 的值——而且你肯定沒有p指向整數值 123。
說了這么多,您可能仍然想知道,是否有一種方法可以將整數指標設定為指向整數陣列,就像帶有s = “hi”的字符陣列一樣?
傳統上,沒有。但是 C99 引入了一種稱為陣列初始化器的東西,這正是您想要的。它看起來像這樣:
int *p = (int[]){ 123 };
這設定了與字串文字相同的情況。編譯器創建一個陣列并設定指向它的指標,就像你說的那樣
int __array initializer[] = { 123 };
int *p = &array initializer[0];
所以,總而言之,你可以說
int *p = (int[]){ 123 };
char *s = "hi";
而且,一旦你這樣做了,畢竟 int 指標和 char 指標之間沒有太大區別。
附錄:它并沒有真正出現在你的問題或這個答案中,但是有一個更密切相關的 C 怪癖值得一提。我說,“每當你在運算式中提到一個陣列并嘗試獲取它的值時,你得到的值就是指向陣列第一個元素的指標。” 我說當你寫的時候
s = "hi";
就好像你寫了
char string_constant = { 'h', 'i', '\0' };
s = &string_constant[0];
但如果你寫它也會起作用
s = string_constant;
乍一看,這看起來是錯誤的——你怎么能把一個像 的陣列string_constant,然后把它分配給一個像 的指標s?嗯,這是因為,正如我所說,當你在運算式中提到一個陣列并嘗試獲取它的值時,你得到的是一個指向陣列第一個元素的指標。或者,換句話說,
s = &string_constant[0];
uj5u.com熱心網友回復:
C 2018 6.5.16.1 1 指定了分配的約束。關于分配給指標的第一個約束是:
— 左運算元具有原子、限定或非限定指標型別,并且(考慮左運算元在左值轉換后將具有的型別)兩個運算元都是指向兼容型別的限定或非限定版本的指標,并且左指向的型別具有right 指向的型別的所有限定符;
在int *p=123;中,p已初始化,未賦值。但是,初始化規則說在 6.7.9 11. 中適用的賦值規則p是一個指標,但123它是一個int,而不是一個指標,所以它不滿足約束,編譯器會抱怨。
在char *s="hi";,"hi"是一個字符陣列。它用作提供初始值的運算式s。C 自動將用作運算式的陣列轉換為指向其第一個元素的指標。1因此,使用"hi",我們有一個指向字符的指標,即 a char *,并且上面的約束允許將 a 分配char *給 a char *,因此編譯器不會抱怨。
請注意,p = 123;不會將地址分配給123指標。它將嘗試將值分配給123指標。除非在已知特殊記憶體地址的特殊情況下(例如硬體介面的地址或作業系統設定的特殊地址),否則絕不應該這樣做。在這些情況下,我們將使用強制轉換將整數地址轉換為指標,就像p = (void *) 0x1000;. 相反,分配tos = "hi";的地址而不是分配其值的原因是因為陣列自動轉換為指標。"hi"s
為了完整起見,關于分配給指標的第二個約束是:
— 左運算元具有原子、限定或非限定指標型別,并且(考慮左運算元在左值轉換后將具有的型別)一個運算元是指向物件型別的指標,另一個是指向限定或非限定版本的指標of
void,并且left指向的型別具有right指向的型別的所有限定符;
這不適用,因為沒有void *涉及。
關于分配給指標的第三個約束是:
— 左運算元是原子的、合格的或不合格的指標,而右運算元是空指標常量;要么
此規則將允許您將整數分配給指標,因為一種空指標常量是值為 0 的整數常量運算式。因此,int *p = 0;或是int *p = 4-4;允許的。int *p = x-x;不會滿足這個約束,因為它不是一個常量運算式。
腳注
1sizeof當陣列是 的運算元、一元的運算元&或者是用于初始化陣列的字串文字時,不執行此轉換。對此的規則在 6.3.2.1 3 中。
uj5u.com熱心網友回復:
這兩個宣告
int *p=123;
char *s="hi";
有語意差異。
為了使它們在語意上相同,您需要撰寫
int *p=123;
char *s= 'h';
在這種情況下,指標由標量物件初始化。在第一個宣告中,指標p由整數常量初始化,123在第二個宣告中,指標s由整數字符常量初始化'h'。
所以編譯器應該發出警告。
或者你可以寫例如
int a[] = { 123 };
int *p = a;
char *s="hi";
在這種情況下,兩個指標都由陣列的第一個字符的地址初始化。
請注意,字串文字 "hi" 具有陣列型別 char[3] 并存盤在記憶體中,如{ 'h', 'i', '\0' }. 用作初始化陣列 a 和字串文字 "hi" 被隱式轉換為指向它們的第一個元素的指標,這些元素對應于型別 int *和char *。所以型別的指標int *和char *由相同型別的運算式初始化int *和char *。
uj5u.com熱心網友回復:
首先,您試圖將原始型別存盤到指標。由于指標期望指標是變數地址或陣列起點,因此它們不能真正直接保存變數。
當您以這種方式宣告變數“hi”時:您實際上創建了一個字符char *s = "hi";陣列,如果它不是全域的,則該陣列存盤在堆疊段中。但是,當您嘗試存盤 123 時,它是我提到的原始型別,因此您嘗試將變數存盤到期望地址的指標。
如果您嘗試做類似的事情,char s1[3] = {'h', 'i', '\0'};這將與“hi”宣告類似。你對你的代碼所做的就像char* s = 'h'它也會給出一個錯誤。
結果,指標需要存盤地址,因此不能在分配的右側使用原始型別。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/431673.html
