1、java
C語言中,爲何字符串能夠賦值給字符指針變量c++
char *p = 「hello」;數組
上邊的表達式爲何能夠,而把p換成數組,而後再賦值就不行了函數
解釋:測試
字符串常量"hello"出如今一個表達式中時,"hello"表達式使用的值就是這些字符所存儲的地址(在常量區),而不是這些字符自己。this
因此,能夠把字符串賦值給指向字符的指針p,而不能把字符串賦值給一個字符數組。 spa
char a[10] = 「hello」; //這樣能夠,這種狀況是c語言初始化所支持的.net
若是寫成char a[10]指針
而後 a = 「hello」 這樣就錯誤了。 對象
一樣是a數組,char a[10] = 「hello」;這種是數組的初始化,和a[0] = ‘h’ a[1] = ‘e’…是一個道理
可是換成char a [10]
而後a = 「hello」就不行了 「hello」賦值的值是一個地址,而a雖然也有地址,可是這與指針是不同的,指針的值是地址,而數組的值雖然也是地址,可是倒是一個常量,因此不能給常量賦值。
代碼測試
#include <stdio.h>
int main()
{
char *p = "hello";
printf("%s",p);
char a[10];
a = "hello";
return 0;
}
error C2440: '=' : cannot convert from 'char [6]' to 'char [10]'
There is no context in which this conversion is possible
看到這樣的錯誤提示,你是否會想到把char a[10]改爲char a[6]呢
試一下,
error C2106: '=' : left operand must be l-value
運算符的左邊應該是一個「左值」。所謂「左值」就是指在程序中佔用內存空間、能夠被修改的量,好比各類變量。
繼續擴展問題:
在使用指針的時候,指針能夠自增,而數組不能自增
編譯器給數組分配了空間,數組a的地址就是一個常量了,讓常量自增這確定是不行的。
繼續擴展:
在指針自增的時候,編譯器會自動識別類型,好比指針是指向int型的,想獲取下一個的地址時,指針直接p++就好了,不要畫蛇添足的p+4了
特別須要注意的是,在void指針使用的時候,不能使用指針運算,應爲void型編譯器不能識別類型的長度(即指針所指對象的體積),p++這樣就是不合法的,即不能進行數學運算,也不能使用*取值操做,想使用必須轉換爲其它的類型.
1.以字符串形式出現的,編譯器都會爲該字符串自動添加一個0做爲結束符,如在代碼中寫
"abc",那麼編譯器幫你存儲的是"abc\0"
2."abc"是常量嗎?答案是有時是,有時不是。
不是常量的狀況:"abc"做爲字符數組初始值的時候就不是,如
char str[] = "abc";
由於定義的是一個字符數組,因此就至關於定義了一些空間來存放"abc",而又由於
字符數組就是把字符一個一個地存放的,因此編譯器把這個語句解析爲
char str[3] = {'a','b','c'};
又根據上面的總結1,因此char str[] = "abc";的最終結果是
char str[4] = {'a','b','c','\0'};
作一下擴展,若是char str[] = "abc";是在函數內部寫的話,那麼這裏
的"abc\0"由於不是常量,因此應該被放在棧上。
是常量的狀況: 把"abc"賦給一個字符指針變量時,如
char* ptr = "abc";
由於定義的是一個普通字符指針,並無定義空間來存放"abc",因此編譯器得幫咱們
找地方來放"abc",顯然,把這裏的"abc"當成常量並把它放到程序的常量區是編譯器
最合適的選擇。因此儘管ptr的類型不是const char*,而且ptr[0] = 'x';也能編譯
經過,可是執行ptr[0] = 'x';就會發生運行時異常,由於這個語句試圖去修改程序
常量區中的東西。
記得哪本書中曾經說過char* ptr = "abc";這種寫法原來在c++標準中是不容許的,
可是由於這種寫法在c中實在是太多了,爲了兼容c,不容許也得容許。雖然容許,
可是建議的寫法應該是const char* ptr = "abc";這樣若是後面寫ptr[0] = 'x'的
話編譯器就不會讓它編譯經過,也就避免了上面說的運行時異常。
又擴展一下,若是char* ptr = "abc";寫在函數體內,那麼雖然這裏的"abc\0"被
放在常量區中,可是ptr自己只是一個普通的指針變量,因此ptr是被放在棧上的,
只不過是它所指向的東西被放在常量區罷了。
3.數組的類型是由該數組所存放的東西的類型以及數組自己的大小決定的。
如char s1[3]和char s2[4],s1的類型就是char[3],s2的類型就是char[4],
也就是說盡管s1和s2都是字符數組,但二者的類型倒是不一樣的。
4.字符串常量的類型能夠理解爲相應字符常量數組的類型,
如"abcdef"的類型就能夠當作是const char[7]
5.sizeof是用來求類型的字節數的。如int a;那麼不管sizeof(int)或者是sizeof(a)都
是等於4,由於sizeof(a)其實就是sizeof(type of a)
6.對於函數參數列表中的以數組類型書寫的形式參數,編譯器把其解釋爲普通
的指針類型,如對於void func(char sa[100],int ia[20],char *p)
則sa的類型爲char*,ia的類型爲int*,p的類型爲char*
7.根據上面的總結,來實戰一下:
對於char str[] = "abcdef";就有sizeof(str) == 7,由於str的類型是char[7],
也有sizeof("abcdef") == 7,由於"abcdef"的類型是const char[7]。
對於char *ptr = "abcdef";就有sizeof(ptr) == 4,由於ptr的類型是char*。
對於char str2[10] = "abcdef";就有sizeof(str2) == 10,由於str2的類型是char[10]。
對於void func(char sa[100],int ia[20],char *p);
就有sizeof(sa) == sizeof(ia) == sizeof(p) == 4,
由於sa的類型是char*, ia的類型是int*,p的類型是char*。
4、
這幾天搞Unix上的C程序,裏面用到了不少字符數組和字符串指針,我記得在學完C語言後至關一段時間裏,對指針這個東西仍是模模糊糊,後來工做也沒怎麼用到過C,雖然網上這類的文章也有不少,仍是決定本身在這作個小總結,也算加深下本身的印象,寫了下面的測試程序:
#include <stdio.h>
int main(int argc, char *argv[])
{
char day[15] = "abcdefghijklmn";
char* strTmp = "opqrstuvwxyz";
printf("&day is %x\n",&day);
printf("&day[0] is %x\n",&day[0]);
printf("day is %x\n",day);
printf("\n&strTmp is %x\n",&strTmp);
printf("&strTmp[0] is %x\n",&strTmp[0]);
printf("strTmp is %x\n",strTmp);
getchar();
return 0;
}
運行後屏幕上獲得以下結果:
其實看到結果估計不少東西就好明白了,
先看看前三個輸出也就是關於變量day的,在 char day[15] = "abcdefghijklmn"; 這個語句執行的時候,系統就分配了一段長15的內存,並把這段內存起名爲day,裏面的值爲"abcdefghijklmn",以下圖所示:
再看程序,第一個輸出,&day,&號是地址運算符,也就是day這個變量的內存地址,很明顯,在最前面,也就是a字符所在字節的地址;
對於第二個輸出也就好理解了,&day[0],就是day數組中第一個變量(也就是a)的地址,所以他們兩個是同樣的;
第三個輸出是day,對於數組變量,可使用變量名來索引變量中的內容,其實這裏的day能夠理解成數組變量退化的指針,而且指向數組的開頭,既然把它理解成指針,那麼它的值確定是地址了,因此他的值和上面兩個也同樣。
再看看後面三個輸出,關於字符串指針strTmp,在執行char* strTmp = "opqrstuvwxyz";後,內存的圖示以下:
如圖所示,內存分配了兩段內存,一個名爲strTmp,類型是一個字符指針,另一段是一個字符串常量,且strTmp裏面存放着字符常量的首地址,注意這裏沒法經過strTmp修改這段字符串,由於是常量;因而程序中的後面三個輸出就好理解了;
&strTmp:strTmp這個字符指針的地址
&strTmp[0]:strTmp所指字符常量第一個字符的地址
strTmp:strTmp這個字符指針的值,即字符常量的首地址
所以,最後兩個的值是同樣的。
指針能夠這樣理解,指針這種類型,和int,char,double等等是同樣的,只是它用來保存地址值的,而int變量保存整數,char變量保存字符,僅此而已,就char型指針或者int指針,本質是同樣的,都是存放的地址,只不過那個地址所裏面的變量類型不一樣而已,還有一種void型指針,就是能夠聽任何類型變量的地址。
5、我的代碼以及註釋,純屬我的理解,定有不妥之處,望批評指正:
#include <stdio.h>
int main(int argc, char *argv[])
{
char* strTmp = "abcd";
printf("strTmp is %s\n",strTmp);//將字符串常量"abcd"的地址所隱含的內容轉換成「string類型」
printf("strTmp is %d\n",strTmp);//將字符串常量"abcd"的地址轉換成int類型,這裏不一樣的機子不一樣的時間的運行結果可能會不同,由於地址可能會發生變化
printf("strTmp is %c\n",strTmp);//將字符串常量"abcd"的地址轉換成字符型,這裏不一樣的機子不一樣的時間的運行結果可能會不同,由於地址可能會發生變化
printf("*strTmp is %c\n",*strTmp);//將字符串常量"abcd"的地址所隱含的內容轉換成字符型,由下面註釋的這句會拋出異常可知,這裏並沒有截取字符串,*strTmp長度自己就是1
//printf("*strTmp is %s\n",*strTmp);//不能將字符轉換成字符串型
getchar();
return 0;
}
6、後來又有看到下面這樣的說法可供讀者參考:
1. C語言中沒有字符串類型,只有用字符數組來表示。這和c++中string是有區別的,C++中string是能夠直接賦值如string s;s="Hello world";可是C語言中的字符數組卻不能這樣。因此,這裏的strTmp能夠理解爲字符數組的首地址,也能夠用它表明整個字符數組,因此能輸出全部字符數組中的內容。
2.字符串就是字符數組或者是指針。 內存實現都同樣的。 數組名字就是一個指針。
char ch[100] ;
char *p;
p =ch;
3.定義的字符串方式舉例:
字符串定義其實很簡單在c/c++語言中定義一個字符串可使用以下的語法:
char *s1=「string1」;//定義字符串常量,指針形式
char s2[]=「string2」;//定義字符串常量,數組形式
char *s3=new char[10];//定義字符串變量並分配內存 指針形式
strcpy(s3,"string3");//爲s3賦值
char s4[10];//定義字符串變量,數組形式
strcpy(s4,"string4");//爲s4賦值
以上四種方法都能定義一個字符串,同時經過字符串在內存中的分佈能夠清楚地知道是什麼狀況
4. C語言中字符串賦值方法strcpy(char*d,char*s)其中s表明是源字符串,d表明目標字符串,也就是你要賦值的字符串。
5.c語言中的字符串跟Java或c++中的字符串不一樣。如char *p;其中p是一個指針,p中存儲一個內存緩衝區的首地址。所謂的內存緩衝區就是一段連續的內存地址,裏面存放了一系列的字符。那系統又是如何判斷在哪裏結束呢。那就是根據符號‘\0’。這個字符佔一個字節,8位,每位的值都是0。