本文是通過幾篇轉帖的文章整理而成的,內容稍有修改:
一、
C語言中,為什麼字串可以賦值給字元指標變數
char *p,a='5';
p=&a; //顯然是正確的,
p="abcd"; //但為什麼也可以這樣賦值??
問:一直理解不了為什麼可以將字串常量賦值給字元指標變數,請各位指點!
答:雙引號做了3件事:
1.申請了空間(在常量區),存放了字串
2. 在字串尾加上了'/0'
3.返回地址
你這裡就是 返回的地址 賦值給了 p
二、
char *p = “hello”;
上邊的運算式為什麼可以,而把p換成數組,然後再賦值就不行了
解釋:
字串常量"hello"出現在一個運算式中時,"hello"運算式使用的值就是這些字元所儲存的地址(在常量區),而不是這些字元本身。
所以,可以把字串賦值給指向字元的指標p,而不能把字串賦值給一個字元數組。
char a[10] = “hello”; //這樣可以,這種情況是c語言初始化所支援的
如果寫成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++這樣就是不合法的,即不能進行數學運算,也不能使用*取值操作,想使用必須轉換為其它的類型
三、
標題:對字元數組,字元指標,字串常量 原帖地址:http://anypath.blog.sohu.com/25069424.html
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*。
四、
C語言中字元數組和字串指標分析,該貼原址:http://www.cnblogs.com/gigikouyi/archive/2006/08/01/464737.html
這幾天搞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型指標,就是可以放任何類型變數的地址。
五、個人代碼以及注釋,純屬個人理解,定有不妥之處,望批評指正:
#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;
}
六、後來又有看到下面這樣的說法可供讀者參考:
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。