1. 程式人生 > >C++語言中的外部變數引用和char[],char*的若干問題探究

C++語言中的外部變數引用和char[],char*的若干問題探究

今天覆習了一下C++的知識點,發現了一些有疑問的地方,查看了一些資料,得到一些結論,記錄下來。

1,如果在一個工程目錄下,存在多個頭檔案,這些標頭檔案裡面是否可以包含相同的變數名呢?

思考:標頭檔案給我們的資訊其實就是介面資訊,我們呼叫一個頭檔案其實就是呼叫跟它相關的原始檔,原始檔一般要封裝起來,因此,我們只能通過標頭檔案呼叫這個api介面。

那麼同一個工程下的兩個標頭檔案裡面如果都包含了標頭檔案,編譯器會認為有歧義,無法判斷到底呼叫了哪個標頭檔案中的變數。但是在開發過程中,難免會發生這種問題。解決辦法是是用名字空間namespace。C++為我們提供了名字空間,可以讓我們在特定的名字空間下尋找變數。

namespace A

{

    int a = 1;

};

namespace B

{

    int a = 2;

};

通過A::a和B::a來訪問兩個標頭檔案中的變數a。

值得注意的是,名字空間的名字不能相同,如果相同還是會出錯,使用名字空間的好處在於只需要修改名字空間名字就可以了,不需要修改所有重名的變數。

另外,如果想使用cpp檔案中的變數,需要在cpp檔案中的變數之前加上extern關鍵字,同時,在外部呼叫的時候也要加上extern關鍵字。

2,測試了char*和char[]的幾種情形:

//#define 和const變數記憶體分配測試
#define STRING "asdfghjkl"
const char p[] = "qwert";
const char *q = "qwert";
void test03(void)
{
//巨集定義的變數會在使用的地方分配記憶體,呼叫幾次分配幾次記憶體,而const 型別的變數只分配一次記憶體
cout<<STRING<<endl;//第一次呼叫,第一次分配記憶體
cout<<STRING<<endl;//第二次呼叫,第二次分配記憶體

//從記憶體的角度分析:
//p的含義:p指向的是這個陣列的首元素,但是它增長的步長為元素型別的大小,此處為1,即sizeof(char),
//&p也指向這個陣列的首元素,但是它指向的是整塊陣列的記憶體,所以它增長的步長為陣列的大小,此處為sizeof("qwert") = 1*6 = 6位元組
//結論:p和&p相等,但是意義不同。

cout<<"**************char p[] test****************"<<endl;
cout<<"p="<<p<<endl;//"qwert"列印p指向的記憶體空間的字串,注意直接列印p列印的是p指向記憶體的內容
cout<<"*p="<<*p<<endl;//"q"列印p指向的第一個元素的值
cout<<"p+1="<<p+1<<endl;//"wert"p往後挪了移位再列印
cout<<"p[0]="<<p[0]<<endl;        //"q" 按照下標訪問第一個元素
cout<<"&p[0]="<<&p[0]<<endl;//"qwert"先取第一個元素,再取地址,[]優先順序高於&
cout<<"p[1]="<<p[1]<<endl;//"w"    按照下標訪問第二個元素
cout<<"&p[1]+1="<<&p[1]+1<<endl;//"ert" 先取第二個元素,再取地址,再往後挪移位

cout<<"*p+1="<<*p+1<<endl;//"q"+1 = 114 ASCII碼 取第一個元素的值,再轉成ASCII碼再加1
cout<<"*(p+1)="<<*(p+1)<<endl;//"w"    先挪一位,再取元素的值

cout<<"&p="<<&p<<endl;//00CE7880
cout<<"&p+1="<<&p+1<<endl;//00CE7886,此處列印的為陣列的地址往後加1,sizeof(p) = 6

cout<<"**************char *q test****************"<<endl;

        //指標的操作根陣列相同,只有一處不同。

cout<<"q="<<q<<endl;
cout<<"*q="<<*q<<endl;
cout<<"q+1="<<q+1<<endl;
cout<<"&q[0]="<<&q[0]<<endl;
cout<<"q[1]="<<q[1]<<endl;
cout<<"&q[1]+1="<<&q[1]+1<<endl;

cout<<"*q+1="<<*q+1<<endl;
cout<<"*(q+1)="<<*(q+1)<<endl;

cout<<"&q="<<&q<<endl;//00CE9040
cout<<"&q+1="<<&q+1<<endl;//00CE9044,不同了。此處列印的為指標的地址往後加1,sizeof(q) = 4

}

分析:char[]和char*最大的區別在於char[]分配了記憶體,而char*沒有分配記憶體。

例如

char p[] = "1234567";

char*q = "1234567";

在編譯階段,p就分配了記憶體,而在執行階段,將全域性區的字串“1234567”的地址賦給q。

因此,sizeof(p) = 8,sizeof(q) = 4.

另外,通過將陣列首地址付給指標,又發現了新的問題:

char p[] = "1234567";//編譯階段就分配了記憶體
cout<<&p<<endl;
char *m = &p[0];    //現在m指向p的首地址了
cout<<m<<endl;    //“1234567”

char *s = p;    //s也指向p的首地址了,注意,p是個地址,其實這裡s指向字元p[0],即s為&p[0]

cout<<s<<endl;    //"1234567"

打印出的是s指向的記憶體的內容

那我如果想要通過指標m來列印指向的記憶體的地址,該怎麼辦呢?根同學討論了一下,哦,原來打印出來的結果跟型別相關。什麼意思?就是打印出來的值跟原來的型別是一樣的,這裡打印出s就是char*,不就是字串嘛,要是列印地址才怪了,又不是指向字元!因此如果想要打印出地址,就得控制列印的型別,由於地址在記憶體中是十六進位制的方式,因此控制輸出為16進位制即可輸出指標指向的陣列的地址了!

cout<<hex<<int(m)<<endl;

cout<<hex<<int(s)<<endl;

另外,之前還犯了一個錯誤,就是

char l[] = "123";

char *k = &l;

我想通過k得到字元陣列l的地址,結果給我報錯。一分析,原來還是之前那個問題,想用char*型別的指標得到地址,只能是字元!可是現在l的地址指向的是一個字元陣列,怎麼可能給你?怎麼辦?強轉!

char *k = (char *)&l;    //這裡將陣列的首地址強制轉換為陣列第一個元素的地址!兩個地址是相同的

如此以來k就指向了l[0],即k=&l[0]。其實跟之前那個m是一個道理,然後輸出的內容跟前面一樣了。

另外,之所以cout<<k<<endl;能夠輸出“123”首先是採用預設的char*字串格式,另外,又因為陣列是連續存放的,在碰到\0之前它不會停止。

今天就這樣了。