本文是通過幾篇轉帖的文章整理而成的,內容稍有修改:
一、
C語言中,為什么字符串可以賦值給字符指針變量
p=&a; //顯然是正確的,
p="abcd"; //但為什么也可以這樣賦值??
問:一直理解不了為什么可以將字串常量賦值給字符指針變量,請各位指點!
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++這樣就是不合法的,即不能進行數學運算,也不能使用*取值操作,想使用必須轉換為其它的類型
標題:對字符數組,字符指針,字符串常量
三、
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。