2020-11-30(为什么字符串可以赋值给字符指针变量)
今天帶著疑問去看了看字符指針,就一直在想為什么輸出一個指向字符的指針,它輸出的不是地址,而是字符,結果挖到了一個寶藏博客,然后結合了自身的想法,摘抄了大部分內容,最后我也把我自己的疑問給解決了,外加再做了一點點補充,湊出今天收獲,謝謝這位博主https://blog.csdn.net/yichu5074
一、
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++這樣就是不合法的,即不能進行數學運算,也不能使用*取值操作,想使用必須轉換為其它的類型
三、
標題:對字符數組,字符指針,字符串常量
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§ == 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; }六、后來又有看到下面這樣的說法可供讀者參考:
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賦值以上四種方法都能定義一個字符串,同時通過字符串在內存中的分布可以清楚地知道是什么情況
5.c語言中的字符串跟java或c++中的字符串不同。如char *p;其中p是一個指針,p中存儲一個內存緩沖區的首地址。所謂的內存緩沖區就是一段連續的內存地址,里面存放了一系列的字符。那系統又是如何判斷在哪里結束呢。那就是根據符號‘\0’。這個字符占一個字節,8位,每位的值都是0。
額外補充一點,在c++里面,用cout輸出a,應該輸出的是字符串的地址,但是為什么直接輸出字符呢?
char * a=“biaobiao”
cout<<a;
查看了一下源代碼后,哎呀,原來是操作符重載了呀,怪不得那么奇妙呢。。。
要想讓它不被重載的話,簡單呀,直接破壞后面的參數不就得了嘛,你說是不是呀???
cout方法使用字符串中的終止空字符來確定何時停止顯示字符。
對于其他類型的指針,C++將其對應于 void *,并打印地址的數值表示。
所以如果想要獲得字符串的地址,必須將其強制轉換為 void * 類型或 int * 類型
cout << (void *)a << endl;
總結
以上是生活随笔為你收集整理的2020-11-30(为什么字符串可以赋值给字符指针变量)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020-11-29(准备考试)
- 下一篇: 2020-12-1(带你理解32位二进制