午夜精品久久影院蜜桃,久久国产成人精品麻豆,久久国产精品久久精品国产http://m.shnenglu.com/deercoder/category/11912.html身是菩提樹, 心如明鏡臺; 時(shí)時(shí)勤拂拭, 莫使惹塵埃zh-cnSat, 27 Mar 2010 14:30:49 GMTSat, 27 Mar 2010 14:30:49 GMT60cin讀取失敗后進(jìn)入死循環(huán)?-------剖析輸入流的條件http://m.shnenglu.com/deercoder/articles/110696.html劉暢劉暢Sat, 27 Mar 2010 13:59:00 GMThttp://m.shnenglu.com/deercoder/articles/110696.htmlhttp://m.shnenglu.com/deercoder/comments/110696.htmlhttp://m.shnenglu.com/deercoder/articles/110696.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/110696.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/110696.html

考慮如下代碼:

 

#include <iostream>
using namespace std;

int main()
{
  
int a;
  
while(cout<<"input a integer (1- 10) :",cin>>a,! (a>=1 && a<=10))
   cout
<<"try again!"<<endl;
return 0;
}




本意是讓用戶選擇一個(gè)1-10的數(shù),如果不是1-10的數(shù)則重新輸入。

分析:

如果用戶輸入了一個(gè)不在1-10的 int,那么,程序會正確的執(zhí)行,并且提示用戶重新輸入。

但是如果用戶錯(cuò)誤的輸入了一個(gè)字符char,那么,后果就是一直執(zhí)行while循環(huán)!

錯(cuò)誤分析:

當(dāng)cin嘗試將輸入的字符讀為int型數(shù)據(jù)失敗后,會產(chǎn)生一個(gè)錯(cuò)誤狀態(tài)--cin.fail().而要用cin讀取輸入流中的數(shù)據(jù),輸入流必須處于無錯(cuò)誤狀態(tài)。因此,由于錯(cuò)誤狀態(tài)的存在,會一直執(zhí)行while循環(huán)。

錯(cuò)誤修正:

#include <iostream>
using namespace std;

int main()
{
int a;
while(cout<<"input a integer (1-10) :",cin>>a,! (a>=1 && a<=10) || cin.fail())
{
   cout<<"try again!"<<endl;
   cin.clear(); //清除std::cin的錯(cuò)誤狀態(tài)
   cin.sync(); //清空輸入緩沖區(qū)
}
return 0;
}

加上判斷輸入是否成功的cin.fail()以及修正錯(cuò)誤輸入的cin.clear()和cin.sync();

其中std::cin.sync();這一句必不可少,因?yàn)樗袕臉?biāo)準(zhǔn)輸入設(shè)備輸入的數(shù)據(jù)都是先保存在緩沖區(qū)中,然后istream對象再從緩沖區(qū)中進(jìn)行提取。如果不清空緩存,下次在讀取數(shù)據(jù)的時(shí)候又會再次產(chǎn)生錯(cuò)誤,也會陷入死循環(huán)。







劉暢 2010-03-27 21:59 發(fā)表評論
]]>
C++中的tips http://m.shnenglu.com/deercoder/articles/105216.html劉暢劉暢Fri, 08 Jan 2010 16:20:00 GMThttp://m.shnenglu.com/deercoder/articles/105216.htmlhttp://m.shnenglu.com/deercoder/comments/105216.htmlhttp://m.shnenglu.com/deercoder/articles/105216.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/105216.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/105216.html 

    題記:一直以來都被一些細(xì)節(jié)忽略了,然后,遇到很多的問題就杯具了……從現(xiàn)在開始,覺得很有必要從小事做起,認(rèn)真的來看看曾經(jīng)犯過的錯(cuò)誤,做過的事情。特別是對于曾經(jīng)以為很習(xí)以為常的事情,要特別的重視,于是便有了tips,下面不斷的更新中…………

題外話:(如何在Office 2007中創(chuàng)建并發(fā)布博文?)今天發(fā)現(xiàn)可以在Word 2007中編輯博客,然后直接點(diǎn)擊發(fā)布,就可以發(fā)布到C++博客網(wǎng)上了,很貼心的服務(wù),不是嗎?其實(shí)這個(gè)也是Office的一個(gè)功能,在新建中,點(diǎn)擊新建博客文章,就可以創(chuàng)建一個(gè)新的blog,然后會提示你創(chuàng)建一個(gè)賬戶,估計(jì)這個(gè)是最關(guān)鍵的,需要選擇其他運(yùn)營商,然后下一步,會有個(gè)API讓你選擇,選擇下面的按個(gè)MetaWebLog,然后輸出你的賬號密碼,這里的昂URL,不是你的主頁的,開始的時(shí)候我也是選擇這個(gè),錯(cuò)誤,連接不上。需要的是:在你的主頁上,點(diǎn)擊管理,然后點(diǎn)擊選項(xiàng),下面有個(gè)configure選項(xiàng),進(jìn)入后,最下面就有一個(gè)你的那個(gè)URL了,添加進(jìn)去,OK!然后就可以發(fā)布了!

 

下面開始正式的tips:

  1. int到底占用多少個(gè)字節(jié)?

    一直以來都被這個(gè)問題糾結(jié),因?yàn)樯线^匯編和C的課,然后又看了《C++ Primer》一書,總是會出現(xiàn)各種的不一致,如果按照平時(shí)我們的認(rèn)為,都是4個(gè)Byte(字節(jié)),可是無奈很多書上講解不一致,所以造成了迷惑……今天找時(shí)間上網(wǎng)搜了下,發(fā)現(xiàn)答案很多,不過大致上都是一個(gè)解答,于是我也就憑借自己的思考,來說說了。

    答:首先摘錄網(wǎng)上的朋友們的留言:

int的字節(jié)長度是由CPU和操作系統(tǒng)編譯器共同決定的,一般情況下,主要是由操作系統(tǒng)決定,比如,你在64位AMD的機(jī)器上安裝的是32位操作系統(tǒng),那么,int默認(rèn)是32位的;如果是64位操作系統(tǒng),64位操作系統(tǒng)分為兩種,1種是int為32位long為64位,2種int long均為64位。之所以說int同時(shí)也與編譯器有關(guān),編譯器完全可以在32位系統(tǒng)模擬64位整數(shù),例如Unix/Linux下的long long和Windows下的__int64均為64位整數(shù)。

 

機(jī)器第一作用,編譯器第二作用.
現(xiàn)在新出的機(jī)器開始有64位的,編譯器也逐漸的要適應(yīng)這個(gè)改變,由32位向64位過渡.
如果機(jī)器是16位的,編譯器強(qiáng)制為32位的話,效率及其低下,沒那家廠商會做如此SB的事情,
我們現(xiàn)在的機(jī)器一般都是32位的,編譯器所以一般也是32位,但也支持64位的,
__int64  就是64字節(jié)的,
總之int 只是一個(gè)類型,沒有太大的意義,機(jī)器的位數(shù)才是關(guān)鍵所在!
離開機(jī)器,說有編譯器決定的人,實(shí)在不敢恭維.
難道要在8位機(jī)上實(shí)現(xiàn)64bit的編譯器?
機(jī)器進(jìn)步了,編譯器跟不上,就要被淘汰,
編譯器超前了,效率低下,也不會有市場,
所以不要單純的討論編譯器或者機(jī)器了。
OVER!

 


int僅是語言標(biāo)準(zhǔn)中的一項(xiàng)定義,定義了這類數(shù)據(jù)的性質(zhì)、范圍、操作等;
具體少長,標(biāo)準(zhǔn)并未硬性規(guī)定,而由編譯器自行決定,甚至編譯器也可由用戶根據(jù)需要自行設(shè)定。
基于此,可認(rèn)為還是受編譯器決定,所謂縣官不如現(xiàn)管。

 

綜合上面的說法,我也覺得對于int到底占用多少個(gè)字節(jié),其實(shí)是和機(jī)器以及編譯器都有關(guān)系。這么來說吧,我覺得機(jī)器起決定性作用,如果是32位機(jī),那么在這個(gè)平臺上面運(yùn)行的編譯器,可以使32位版本的,也可以是16位版本的(向下兼容,只不過浪費(fèi)了好機(jī)子,沒有充分發(fā)揮效果),這也就是說我們在網(wǎng)上下載驅(qū)動(dòng)的時(shí)候,有時(shí)候會發(fā)現(xiàn)官網(wǎng)上面有32位操作系統(tǒng)Windows版本和64位操作系統(tǒng)Windows的不同分類了。而決定這個(gè)位數(shù)的,就是你的CPU,也就是機(jī)器。

但是,編譯器沒有必要一定要這么做,對于TC 2.0這樣的16位軟件,曾經(jīng)在Dos下呼風(fēng)喚雨,但是到了32位機(jī)下,仍然是可以用的,這樣的話,也就是說,它運(yùn)算的結(jié)果是2個(gè)字節(jié),可是因?yàn)檐浖南拗疲荒軌蚰M出來16位機(jī),所以雖然可以在32位上面跑,但是不能反映實(shí)際的字長。但是,沒有多少人會在32位機(jī)子上面模擬出128位或者64位,因?yàn)檫@樣尋址的話效率很低下,硬件條件沒有達(dá)到,利用軟件來模擬是不夠的。于是,就是上面的那種說法。由于VC以及其他軟件更新很快,而且及時(shí)開發(fā)了Win 32下的版本,所以是32位軟件,也就是說,它計(jì)算處理的int是4個(gè)字節(jié)。

當(dāng)然,我們需要知道的是,標(biāo)準(zhǔn)并沒有規(guī)定int一定要是多少個(gè)字節(jié),而只是規(guī)定了一個(gè)范圍,只要你不越軌,那么肯定就沒有問題,至于實(shí)現(xiàn),那是你編譯器的開發(fā)的事情,跟我無關(guān)。同樣,CPU和機(jī)器的制造者,關(guān)心的是如何提高計(jì)算機(jī)的性能,讓尋址方式更大,范圍更長,也就是位數(shù)越多,(這也是為何目前的那些機(jī)器都朝著64位或者更高位發(fā)展的目的),而軟件開發(fā)者,面臨的則是需要來適應(yīng)硬件的更新,從而做出更好的軟件,充分利用到這個(gè)性能。

當(dāng)然,這個(gè)問題本身沒有多少討論的價(jià)值,不過,如果在面試或者考試中,遇到sizeof的操作,最好問清楚是在32位機(jī)子下還是16位或者是64位,這個(gè)能夠反映你的嚴(yán)謹(jǐn)和認(rèn)真。

注: 32位下:1int = 4 Byte            16位下:1 int = 2B        (當(dāng)然,一般都不會考慮那個(gè)TC 2.0的)

 

 

 

 

 

2).普通類的繼承中,如果派生類中的函數(shù)和基類函數(shù)同名時(shí),是否會覆蓋原來的那個(gè)函數(shù)?

    在java中,很明顯是會覆蓋的,因?yàn)槔^承的特性,注意同名函數(shù),要求還有原型相同,否則的話,調(diào)用的時(shí)候是可以區(qū)分開來的,而當(dāng)原型相同的時(shí)候,那么派生類會繼承基類的函數(shù),同時(shí)由于有一個(gè)自己的函數(shù),所以兩個(gè)同名,同原型的函數(shù),是會發(fā)生覆蓋的。

上代碼看之:

#include <iostream>

using namespace std;

class A

{

    public:

        void moveto(){cout << "1" << endl;}

};

class B:public A

{

    public:

        void moveto(){cout << "2" << endl;}

};

int main()

{

    A a;

    B b;

    A *pstra = &b;

    B *pstrb = (B *)&a;

    pstra->moveto();

    pstrb->moveto();

    a.moveto();

    b.A::moveto();

    return 0;

}

輸出的結(jié)果是:

        

從上面的結(jié)果可以看到,派生類的指針是調(diào)用派生類的對應(yīng)的函數(shù),配套的,而且還選擇作用域較小的,而這樣的優(yōu)先級就要高些。這里有一個(gè)強(qiáng)制類型轉(zhuǎn)換,因?yàn)槭且獙⑴缮惖闹羔樦赶蚧惖膶ο螅瑫l(fā)生截?cái)啵?span style="COLOR: red">一定要強(qiáng)制類型轉(zhuǎn)換,與此對比的是,基類的指針指向派生類的話不需要強(qiáng)制類型轉(zhuǎn)換也可以實(shí)現(xiàn)。從結(jié)果可以看到,如果實(shí)行了轉(zhuǎn)換,那么還是聲明時(shí)候的類型,所以抓住了這點(diǎn),聲明時(shí)是B類型的,那么就調(diào)用B的moveto,所以輸出時(shí)2,下面的另外一些的輸出,就都是1了,也可以看到,如果要顯示的調(diào)用被隱藏的基類,需要用作用域限定符來指示。

 

 2.隱藏的this指針不是實(shí)際的一個(gè)參數(shù),也就是說,實(shí)際調(diào)用函數(shù)中,不能夠使用這個(gè)參數(shù)。
上代碼:

#include <string.h>
#include 
<iostream>
using namespace  std;

class STRING{
    
char *str;
public:
    STRING(
char *s=0);
    STRING(
const STRING &);
    
virtual int strlen(voidconst;
    
virtual int strcmp(const STRING &const;
    
virtual STRING &operator+(const STRING &);
    
virtual STRING &operator=(const STRING &);
    
virtual STRING &operator+=(const STRING &);
    
virtual operator const char *(voidconst;
    
virtual ~STRING();
}
;

STRING::STRING(
char *s/*=0*/){
    str 
= new char[::strlen(s)+1];
    ::strcpy(str,s);
}

STRING::
~STRING(){
    delete str;
}

int STRING::strlen(voidconst{
    
return ::strlen(this->str)+5//采用字符串的通用表示,不含null
}

int STRING::strcmp(const STRING &b) const{
    
return ::strcmp(this->str,b.str);
}

STRING 
&STRING::operator+(const STRING &b){
    ::strcat(
this->str,b.str);
    
return *this;
}

STRING 
&STRING::operator=(const STRING &b){
    
this->str = b.str;
    
return *this;
}

STRING 
&STRING::operator+=(const STRING &b){
    ::strcat(
this->str,b.str);
    
return *this;
}

STRING::
operator const char *(voidconst{
    
return this->str;
}

int main()
{
    STRING s(
"Hello,world");
    STRING e(
"I Love C++");
    cout 
<< "STRING:" << s << "\t strlen:" << s.strlen()<< endl;
    cout 
<< "STRING:" << e << "\t strlen:" << strlen(e)<< endl;
    
    
return 0;

}

 

  僅僅看輸出,前面的一個(gè)是調(diào)用類函數(shù)strlen,沒有參數(shù),看到?jīng)]有,如果有參數(shù)的話,就是下面的那個(gè),那么此時(shí)就是調(diào)用的系統(tǒng)函數(shù)了。兩者的輸出不一樣,我特意在自定義的strlen函數(shù)上面加5以表示區(qū)別,最后果然,前者就是調(diào)用自己的函數(shù)。
得到的結(jié)論就是,類的成員函數(shù),都是有一個(gè)默認(rèn)的this指針的,這個(gè)沒有錯(cuò),不過這個(gè)是編譯器加的,我們手動(dòng)是不能自己占有的,也就是說不能理解為有一個(gè)參數(shù),然后放上面去,自己定義的是void參數(shù),就沒有參數(shù),編譯器加的是它的事情,那個(gè)地方提供給我們用戶使用的就是無參的函數(shù)。此點(diǎn)要銘記,而且,即使你調(diào)用無參的,在具體編譯器處理的時(shí)候,還是會給你加上,所以這部分你根本用不上,也不需要擔(dān)心。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



劉暢 2010-01-09 00:20 發(fā)表評論
]]>
C++中形參和數(shù)據(jù)成員同名的情況http://m.shnenglu.com/deercoder/articles/105124.html劉暢劉暢Thu, 07 Jan 2010 15:46:00 GMThttp://m.shnenglu.com/deercoder/articles/105124.htmlhttp://m.shnenglu.com/deercoder/comments/105124.htmlhttp://m.shnenglu.com/deercoder/articles/105124.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/105124.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/105124.html 

 

在C++中,如果出現(xiàn)函數(shù)的形參和類的數(shù)據(jù)成員同名的情況,一定不要為了圖簡省而使用下面的寫法。例:

#include <iostream>

using namespace std;

 

class A

{

    int x;

    int y;

    public :

        A(int x,int y);

        void print();

};

 

A::A(int x,int y)

{

    x = x;

    y = y;

}

void A::print()

{

    cout << "x:" << x << " y: " << y << endl;

}

int main()

{

    A *a = new A(34,56);

    a->print();

    delete a;

    return 0;

在紅色部分突出顯示的就是錯(cuò)誤的寫法,實(shí)際上,形參的值根本沒有傳入到這個(gè)函數(shù)中,而在你輸出的時(shí)候,根本沒有x,y的值,也就是一個(gè)隨機(jī)的。說明形參根本沒有傳入。

這個(gè)也是Java中遇到的,如果有這種情況,java中用到的是this引用來顯示的圈定范圍,而C++中,也需要用this指針來顯示的解決這個(gè)問題。或者用作用域限定符來解決。

更改后才會出現(xiàn)正確的結(jié)果,而以前,自己一直都沒有注意到。

為何會這樣呢?因?yàn)榘凑辗秶鷱男〉酱蟮捻樞颍螀⒌姆秶。蕴鎿Q的就是恒等式,那么自然不會顯示的來給成員賦值,因?yàn)轭惖某蓡T作用范圍更小,此處,要謹(jǐn)記。

而且對于Java也是一樣,都要顯示的來指出。

 



劉暢 2010-01-07 23:46 發(fā)表評論
]]>
特殊成員變量的初始化http://m.shnenglu.com/deercoder/articles/104790.html劉暢劉暢Mon, 04 Jan 2010 14:58:00 GMThttp://m.shnenglu.com/deercoder/articles/104790.htmlhttp://m.shnenglu.com/deercoder/comments/104790.htmlhttp://m.shnenglu.com/deercoder/articles/104790.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/104790.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/104790.html有些成員變量的數(shù)據(jù)類型比較特別,它們的初始化方式也和普通數(shù)據(jù)類型的成員變量有所不同。這些特殊的類型的成員變量包括:


a.       常量型成員變量

b.       引用型成員變量

c.        靜態(tài)成員變量

d.       整型靜態(tài)常量成員變量

e.       非整型靜態(tài)常量成員變量

 


對于常量型成員變量和引用型成員變量的初始化,必須通過構(gòu)造函數(shù)初始化列表的方式進(jìn)行。在構(gòu)造函數(shù)體內(nèi)給常量型成員變量和引用型成員變量賦值的方式是行不通的。

 

靜態(tài)成員變量的初始化也頗有些特別。

 

參考下面的代碼以及其中注釋:

// Initialization of Special Data Member

#include <iostream>

using namespace std;

 

class BClass

{

public:

         BClass() : i(1), ci(2), ri(i)   // 對于常量型成員變量和引用型成員變量,必須通過

         {                                             // 參數(shù)化列表的方式進(jìn)行初始化。在構(gòu)造函數(shù)體內(nèi)進(jìn)

         }                                             // 行賦值的方式,是行不通的。

 

         void print_values()

         {

                   cout << "i =\t" << i << endl;

                   cout << "ci =\t" << ci << endl;

                   cout << "ri =\t" << ri << endl;

                   cout << "si =\t" << si << endl;

                   cout << "csi =\t" << csi << endl;

                   cout << "csi2 =\t" << csi2 << endl;

                   cout << "csd =\t" << csd << endl;

         }

private:

         int i;                                                          // 普通成員變量

         const int ci;                                             // 常量成員變量

         int &ri;                                                      // 引用成員變量

         static int si;                                             // 靜態(tài)成員變量

         //static int si2 = 100;                             // error: 只有靜態(tài)常量成員變量,才可以這樣初始化

         static const int csi;                                // 靜態(tài)常量成員變量

         static const int csi2 = 100;                  // 靜態(tài)常量成員變量的初始化(Integral type)    (1)

         static const double csd;                      // 靜態(tài)常量成員變量(non-Integral type)

         //static const double csd2 = 99.9;      // error: 只有靜態(tài)常量整型數(shù)據(jù)成員才可以在類中初始化

};

 

// 靜態(tài)成員變量的初始化(Integral type)

int BClass::si = 0;

// 靜態(tài)常量成員變量的初始化(Integral type)

const int BClass::csi = 1;

// 靜態(tài)常量成員變量的初始化(non-Integral type)

const double BClass::csd = 99.9;

 

// 在初始化(1)中的csi2時(shí),根據(jù)Stanley B. Lippman的說法下面這行是必須的。

// 但在VC2003中如果有下面一行將會產(chǎn)生錯(cuò)誤,而在VC2005中,下面這行則可有可無,

// 這個(gè)和編譯器有關(guān)。

const int BClass::csi2;

 

int main(void)

{

         BClass b_class;

         b_class.print_values();

 

         return 0;

}

 

本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/pathuang68/archive/2009/11/25/4867323.aspx



劉暢 2010-01-04 22:58 發(fā)表評論
]]>
C/C++中的字符串字面值http://m.shnenglu.com/deercoder/articles/104213.html劉暢劉暢Sun, 27 Dec 2009 10:48:00 GMThttp://m.shnenglu.com/deercoder/articles/104213.htmlhttp://m.shnenglu.com/deercoder/comments/104213.htmlhttp://m.shnenglu.com/deercoder/articles/104213.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/104213.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/104213.html

C/C++中的字符串字面值

 

1.     字符串?dāng)?shù)組的大小如何得到。我們都知道,對于一個(gè)字符串?dāng)?shù)組,要得到它的大小,有多種辦法,常見的有下面的兩種。

.利用sizeof運(yùn)算符來實(shí)現(xiàn)。現(xiàn)在假設(shè)有一個(gè)字符數(shù)組str[] = “hello,world”,為了得到的它的長度,我們利用這種辦法

具體就是sizeof(str) /sizeof(char);結(jié)果是12,比較發(fā)現(xiàn),實(shí)際的字符串大小事11啊,為什么會得到12的結(jié)果呢?

有這種疑問,是因?yàn)閷ξ覀冏址麛?shù)組沒有一個(gè)更好的理解。上面初始化一個(gè)字符數(shù)組,是利用字符串來初始化的,

而字符串的話,默認(rèn)的是會有一個(gè)以null結(jié)尾的標(biāo)志,這個(gè)具體的就是’\0’,有很多人的程序有問題,可能都是因?yàn)檫@里

出現(xiàn)了問題,比如數(shù)組沒有封口,造成了指針非法訪問其他內(nèi)存單元,等等。所以實(shí)際上的長度就是12.

具體來說說這個(gè)原理吧:sizeof是表示計(jì)算大小,即字節(jié)數(shù)。這里以一個(gè)數(shù)組名來作為參數(shù),就是計(jì)算這個(gè)數(shù)組的大小。

上面說到,字符串?dāng)?shù)組的話是有一個(gè)默認(rèn)的null來封口的,所以,計(jì)算的大小事包括這個(gè)單元的。于是sizeof(str)的大小

就是12 個(gè)字節(jié),然后計(jì)算一個(gè)單元,即一個(gè)字符char類型的占據(jù)的字節(jié)數(shù),也就是1個(gè)字節(jié),兩者相除,就是12的結(jié)果。

.利用循環(huán)來實(shí)現(xiàn),這個(gè)部分是很多人都常常想到的。因?yàn)橐粋€(gè)字符串來定義的數(shù)組,默認(rèn)是以null即為的,所以,通過               循環(huán)遍歷的方式來實(shí)現(xiàn),這個(gè)方法還是可行的。定義一個(gè)變量i,然后利用for循環(huán),判斷str[i]不為’\0’,即可判斷              得到最后一個(gè)元素的下標(biāo),注意到的是這里是最后一個(gè)字符元素,但是不是最后一個(gè)真正的元素,有一個(gè)null,因此,還                要加上1才是真正的個(gè)數(shù),經(jīng)過這個(gè)過程,得到的就是總的長度。這個(gè)方法比較容易理解。但是容易忽略了最后一個(gè)的作用。                                                                                                實(shí)際上,我們使用的很多C提供的函數(shù),關(guān)于字符串操作的,都需要用到這個(gè)知識,我們在做計(jì)算的時(shí)候,很容易誤以為返                    回的。就是那個(gè)字符串的長度,從而導(dǎo)致了錯(cuò)誤。實(shí)際在我們拷貝字符串的時(shí)候,尤其要注意這一點(diǎn)。

 

2.  在《C++ Primer》一書中,將以null結(jié)束的字符數(shù)組稱為字符數(shù)組,標(biāo)準(zhǔn)庫提供一批處理這類字符數(shù)組的函數(shù),包括strlen,strcmp,strcat,strcpy,strncat,strncpy等等,而這些函數(shù)中的參數(shù),都是以null結(jié)束的字符數(shù)組名,也就是字符數(shù)組的指針,而得到長度,返回的就是一個(gè)長度,沒有包括null這個(gè)單元,所以實(shí)際上,上面的那個(gè)還有一個(gè)方法,可以調(diào)用此函數(shù)來實(shí)現(xiàn)。當(dāng)然,還有一點(diǎn)要注意的是,strcat連接函數(shù),將右邊的字符串連接到左邊,所以對于左邊的字符串,一定要準(zhǔn)確的計(jì)算出長度,如果不慎,就會溢出,出現(xiàn)嚴(yán)重的錯(cuò)誤。看看下面這個(gè)例子:

Char s1[25] = “hello,world”;  char s2[20] = “0123456789012345678”(剛好是20個(gè)字符,為何,因?yàn)橛幸粋€(gè)null).所以現(xiàn)在的那個(gè)s2[20] = “01234567890123456789”,此時(shí)你會發(fā)現(xiàn),字符串就有20個(gè),含有一個(gè)null,注意就溢出了,OK

下面,如果我將上面的那個(gè)采用字符串連接函數(shù)來做,就是strcat(s1,s2),那么又會出現(xiàn)錯(cuò)誤。因?yàn)橐绯隽恕?span lang=EN-US>

3.C++中提供了一個(gè)標(biāo)準(zhǔn)庫string來處理字符串,這個(gè)string類提供了很多操作,是一種更安全的做法,然而了,為了新舊代碼的兼容,需要進(jìn)行兩者之間的轉(zhuǎn)換,而把string的一個(gè)類函數(shù)c_str()返回值是C風(fēng)格字符串。const char *str = st2.c_str();其中st2是一個(gè)string類型的對象,返回值就是一個(gè)指向它的字符串。 此即為字符串的處理,作為C/C++的字符串值得注意的地方。                                                 



劉暢 2009-12-27 18:48 發(fā)表評論
]]>
C++中的引用http://m.shnenglu.com/deercoder/articles/104211.html劉暢劉暢Sun, 27 Dec 2009 10:47:00 GMThttp://m.shnenglu.com/deercoder/articles/104211.htmlhttp://m.shnenglu.com/deercoder/comments/104211.htmlhttp://m.shnenglu.com/deercoder/articles/104211.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/104211.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/104211.html閱讀全文

劉暢 2009-12-27 18:47 發(fā)表評論
]]>
[原創(chuàng)]操作系統(tǒng)的進(jìn)程http://m.shnenglu.com/deercoder/articles/102918.html劉暢劉暢Thu, 10 Dec 2009 06:41:00 GMThttp://m.shnenglu.com/deercoder/articles/102918.htmlhttp://m.shnenglu.com/deercoder/comments/102918.htmlhttp://m.shnenglu.com/deercoder/articles/102918.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/102918.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/102918.htmlLinux學(xué)習(xí)

1.操作系統(tǒng)的實(shí)驗(yàn)基本上都是以linux下面的系統(tǒng)編程為內(nèi)容,進(jìn)程的調(diào)度和創(chuàng)建都是很基本的內(nèi)容。很幸運(yùn)的是,linux為我們提供了很多的系統(tǒng)函數(shù),供我們來使用。通過熟悉這些函數(shù),使得我們可以能夠?qū)M(jìn)程和程序有一個(gè)更良好的認(rèn)識。

2.Fork函數(shù)的使用。下面引用自百度百科:

 fork()函數(shù),Linux系統(tǒng)調(diào)用

  頭文件:

  #include <unistd.h>

  函數(shù)定義:

  int fork( void );

  返回值:

  子進(jìn)程中返回0,父進(jìn)程中返回子進(jìn)程ID,出錯(cuò)返回-1

  函數(shù)說明:

  一個(gè)現(xiàn)有進(jìn)程可以調(diào)用fork函數(shù)創(chuàng)建一個(gè)新進(jìn)程。由fork創(chuàng)建的新進(jìn)程被稱為子進(jìn)程(child process)。fork函數(shù)被調(diào)用一次但返回兩次。兩次返回的唯一區(qū)別是子進(jìn)程中返回0值而父進(jìn)程中返回子進(jìn)程ID

  子進(jìn)程是父進(jìn)程的副本,它將獲得父進(jìn)程數(shù)據(jù)空間、堆、棧等資源的副本。注意,子進(jìn)程持有的是上述存儲空間的副本,這意味著父子進(jìn)程間不共享這些存儲空間,它們之間共享的存儲空間只有代碼段。

  示例代碼:

  #include <unistd.h>

  #include <stdio.h>

  int main(int argc, void ** argv )

  {

  int pid = fork();

  if(pid == -1 ) {

  // print("error!");

  } else if( pid = =0 ) {

  // print("This is the child process!");

  } else {

  // print("This is the parent process! child process id = %d", pid);

  }

  return 0;

  }

 

 

 

1.      下面來詳細(xì)的說說我的程序和問題,通過比較學(xué)習(xí),相信對于linux的學(xué)習(xí),特別是進(jìn)程和程序直接的關(guān)系有一個(gè)更好的理解。

Process2.c

Code

#include <stdio.h>

#include <unistd.h>

int main()

{

 int p1;

 p1=fork();

 if (p1 == 0)      //子進(jìn)程,輸出它的父進(jìn)程和當(dāng)前進(jìn)程的PID

 {

     printf("current PID is %d\n",getpid());

     printf("father PID is %d\n",getppid());

     printf("\n");

 }

 else   //這里是父進(jìn)程嗎?

 {

     printf("father PID is %d\n",getppid());

     printf("current PID is %d\n",getpid());

     printf("\n");

 }

 return 0;

}

分析:此進(jìn)程的運(yùn)行結(jié)果是?(思考后再回答)

是的,有兩種情況。首先,程序運(yùn)行到fork語句之后,創(chuàng)建進(jìn)程,此時(shí)有兩種情況,創(chuàng)建成功和不成功,而成功又有兩種情況,fork的返回值為0,則運(yùn)行的是創(chuàng)建的子進(jìn)程,fork的返回值為1,則運(yùn)行的是父進(jìn)程。用if語句來判斷,OK

那么問題就是,if語句中沒有問題,p10,說明創(chuàng)建成功,運(yùn)行的是子進(jìn)程,可是else語句來說,卻又兩種情況,如果創(chuàng)建不成功也在else語句中,那么此時(shí)當(dāng)前進(jìn)程就是那個(gè)原始的進(jìn)程,而它的父進(jìn)程卻是1PID,因?yàn)樗械倪M(jìn)程都是1號進(jìn)程的子進(jìn)程,這個(gè)是linux下面的結(jié)論。操作系統(tǒng)的知識。

所以,此時(shí)的結(jié)果應(yīng)該是:

father PID is 32667

current PID is 15254

 

current PID is 15255

father PID is 1

先放著,先解釋下面的這個(gè),注意,這個(gè)對上面是有幫助的。

(看完之后回頭過來看看),看到?jīng)]有,首先輸出的那個(gè)father PID是什么,剛好就是下面的那個(gè)32667,這個(gè)不是偶然的,說明什么,當(dāng)然,這個(gè)進(jìn)程就是父進(jìn)程,而創(chuàng)建它的那個(gè)進(jìn)程的PID32667,可以認(rèn)為是系統(tǒng)創(chuàng)建的。就是說這種情況下面是,先運(yùn)行的父進(jìn)程。而它創(chuàng)建失敗了,那么它沒有子進(jìn)程啊,那么那個(gè)下面的這個(gè)進(jìn)程的父進(jìn)程就不是上面的那個(gè)了,為何,從結(jié)果可以看到,它的PID1,而不會是15254,說明不是上面的那個(gè)創(chuàng)建。而創(chuàng)建卻是1,說明是系統(tǒng)創(chuàng)建的,所以fork語句之后,沒有創(chuàng)建成功,而是由系統(tǒng)來親自創(chuàng)建一個(gè)進(jìn)程,所以PID號當(dāng)然是下一個(gè),不過父進(jìn)程就是1號了。(有興趣的可以查閱下資料,我們也是剛學(xué),所以我還有繼續(xù)探究中!!

 

 

 

current PID is 15302 //子進(jìn)程

father PID is 15301[l1]      

 

father PID is 32667 //父進(jìn)程

current PID is 15301

 

PID = 13501

父進(jìn)程

PID = 13504

子進(jìn)程

系統(tǒng)創(chuàng)建了當(dāng)前的那個(gè)父進(jìn)程,就是程序運(yùn)行的進(jìn)程

PID = 32667

 

 

 

 

 

 

 

 

 

 

 

 

 


下面還想討論這樣一個(gè)問題,這個(gè)問題是從上面那個(gè)第一種情況引申來的。

如下:

Code

#include <stdio.h>

int main()

{

 int p1;

 while (( p1 = fork())!= -1);

 if (p1 == -1)

 {

    printf("current PID is %d\n",getpid());

    printf("father PID is %d\n",getppid());

 }

 return 0;

}[l2] 

 

 

結(jié)果如下,雖然比較多,不過很能說明問題:

current PID is 17600

father PID is 1

current PID is 17761

father PID is 1

current PID is 17602

father PID is 1

current PID is 18545

father PID is 1

current PID is 17608

father PID is 1

current PID is 17767

father PID is 1

current PID is 17624

father PID is 1

current PID is 17773

father PID is 1

current PID is 17616

father PID is 1

current PID is 18551

father PID is 1

current PID is 17630

father PID is 1

current PID is 18552

father PID is 1

current PID is 17636

father PID is 1

current PID is 17779

father PID is 1

current PID is 17642

father PID is 1

current PID is 17785

father PID is 1

current PID is 17648

father PID is 1

current PID is 17787

father PID is 1

current PID is 17654

father PID is 1

current PID is 17795

father PID is 1

current PID is 18510

father PID is 1

current PID is 17801

father PID is 1

current PID is 18511

father PID is 1

current PID is 17807

father PID is 1

current PID is 17656

father PID is 1

current PID is 17815

father PID is 1

current PID is 17384

father PID is 32667

current PID is 17819

father PID is 1

current PID is 17660

father PID is 1

father PID is 1

current PID is 17825

father PID is 1

current PID is 17668

father PID is 1

current PID is 18571

father PID is 1

father PID is 1

father PID is 1

father PID is 1

father PID is 1

current PID is 17672

father PID is 1

current PID is 18572

father PID is 1

current PID is 17678

father PID is 1

current PID is 17831

father PID is 1

father PID is 1

current PID is 17684

father PID is 1

current PID is 17839

father PID is 1

current PID is 17690

father PID is 1

current PID is 17845

father PID is 1

current PID is 17696

father PID is 1

current PID is 17851

father PID is 1

current PID is 17702

father PID is 1

current PID is 17857

father PID is 1

current PID is 17710

father PID is 1

current PID is 17863

father PID is 1

current PID is 18531

father PID is 1

current PID is 17786

father PID is 1

current PID is 17714

father PID is 1

current PID is 18566

father PID is 17786

current PID is 17720

father PID is 1

current PID is 18533

father PID is 1

current PID is 17728

father PID is 1

current PID is 18538

father PID is 1

current PID is 17730

father PID is 1

current PID is 18548

father PID is 17740

current PID is 17738

father PID is 1

current PID is 17794

father PID is 1

current PID is 17740

father PID is 1

current PID is 17804

father PID is 1

current PID is 17750

father PID is 1

current PID is 17810

father PID is 1

current PID is 17756

father PID is 1

current PID is 17814

father PID is 1

current PID is 17764

current PID is 17820

father PID is 1

father PID is 1

current PID is 17768

father PID is 1

current PID is 17826

father PID is 1

current PID is 17774

father PID is 1

current PID is 17832

father PID is 1

current PID is 17780

father PID is 1

current PID is 17838

father PID is 17913

father PID is 1

father PID is 17929

father PID is 17927

father PID is 1

father PID is 17941

father PID is 1

father PID is 17950

father PID is 1

 

看完了上面的那個(gè)例子,下面的這個(gè)例子就是小case了,當(dāng)然,其實(shí)上面的那個(gè)例子就是由下面的這個(gè)例子引申來的,是因?yàn)閷戝e(cuò)了代碼而引起的。當(dāng)然,根據(jù)錯(cuò)誤,我們收獲不小啊!

Code

#include <stdio.h>

#include <unistd.h>

int main()

{

 int p1;

 while ((p1=fork())==-1) ;

 if (p1 == 0)

 {

     printf("current PID is %d\n",getpid());

     printf("father PID is %d\n",getppid());

     printf("\n");

 }

 else

 {

     printf("father PID is %d\n",getppid());

     printf("current PID is %d\n",getpid());

     printf("\n");

 }

 return 0;

}

 

結(jié)果如下:

current PID is 20312[l3] 

father PID is 20311

 

father PID is 32667

current PID is 20311

 

----------------------------------------------------------------------------------------------------------------

father PID is 32667

current PID is 20309[l4] 

 

current PID is 20310

father PID is 1


 [l1]父進(jìn)程的PID剛好就是下面的那個(gè)父進(jìn)程的current PID,說明下面的那個(gè)進(jìn)程就是父進(jìn)程。

 [l2]看看這個(gè)程序,雖然比較流氓,不過可以根據(jù)結(jié)果知道,如果創(chuàng)建失敗的話,那么也會有一個(gè)當(dāng)前的PID,而且還是由一個(gè)1號進(jìn)程創(chuàng)建。

 [l3]當(dāng)前是子進(jìn)程,而它的父進(jìn)程恰好是下面的這個(gè)進(jìn)程!

 [l4]父進(jìn)程就是這個(gè)進(jìn)程,而它的那個(gè)子進(jìn)程卻是由系統(tǒng)創(chuàng)建的!


后來通過查閱資料知道,如果父進(jìn)程創(chuàng)建一個(gè)子進(jìn)程后其先結(jié)束,那么子進(jìn)程就變成了孤兒進(jìn)程,這樣的話,就會由初始化進(jìn)程,即1號進(jìn)程來領(lǐng)養(yǎng)。所以會顯示它的父進(jìn)程為1.至此,完美的解決了。。

最近在看《Linux程序設(shè)計(jì)》(第三版)一書,收獲頗豐。本書是Linux下面程序設(shè)計(jì)的經(jīng)典,值得一睹。。而且最近的感觸是,懂得linux的操作和會在Linux下面進(jìn)程程序設(shè)計(jì)完全是兩回事,就像你會用windows和會開發(fā)windows下面的程序一樣。我輩任重而道遠(yuǎn)啊。。


劉暢 2009-12-10 14:41 發(fā)表評論
]]>
[轉(zhuǎn)載]gcc使用http://m.shnenglu.com/deercoder/articles/102581.html劉暢劉暢Fri, 04 Dec 2009 16:45:00 GMThttp://m.shnenglu.com/deercoder/articles/102581.htmlhttp://m.shnenglu.com/deercoder/comments/102581.htmlhttp://m.shnenglu.com/deercoder/articles/102581.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/102581.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/102581.html

GCC(gcc) 的不斷發(fā)展完善使許多商業(yè)編譯器都相形見絀, GCC GNU 創(chuàng)始人 Richard Stallman首創(chuàng),是 GNU 的標(biāo)志產(chǎn)品,由于 UNIX 平臺的高度可移植性, GCC 幾乎在各種常見的 UNIX 平臺上都有,即使是 Win32/DOS 也有 GCC 的移植。 比如說 SUN Solaris 操作系統(tǒng)配置的編譯器就是GNU GCC

GNU 軟件包括 C 編譯器 GCC C++ 編譯器 G++ ,匯編器 AS ,鏈接器 LD ,二進(jìn)制轉(zhuǎn)換工具(OBJCOPY OBJDUMP) ,調(diào)試工具 (GDB GDBSERVER KGDB) 和基于不同硬件平臺的開發(fā)庫。

GNU GCC 支持下用戶可以使用流行的 C/C++ 語言開發(fā)應(yīng)用程序,滿足生成高效率運(yùn)行代碼、易掌握的編程語言的用戶需求。

這些工具都是按 GPL 版權(quán)聲明發(fā)布,任何人可以從網(wǎng)上獲取全部的源代碼,無需使用任何費(fèi)用。關(guān)于 GNU 和公共許可證協(xié)議的詳細(xì)資料, 讀者可以參看 GNU 網(wǎng)站的介紹,

http://www.gnu.org/home.html

GNU 開發(fā)工具都是采用命令行的方式,用戶掌握起來相對比較困難,不如基于 Windows 系統(tǒng)的開發(fā)工具好用,但是 GNU 工具的復(fù)雜性是由于它更貼近編譯器和操作系統(tǒng)的底層,并提供了更大的靈活性。一旦學(xué)習(xí)和掌握了相關(guān)工具后,就了解了系統(tǒng)設(shè)計(jì)的基礎(chǔ)知識。

運(yùn)行于 Linux 操作系統(tǒng)下的自由軟件 GNU gcc 編譯器,不僅可以編譯 Linux 操作系統(tǒng)下運(yùn)行的應(yīng)用程序,還可以編譯 Linux 內(nèi)核本身,甚至可以作交叉編譯,編譯運(yùn)行于其它 CPU 上的程序。所以,在進(jìn)行嵌入式系統(tǒng)應(yīng)用程序開發(fā)時(shí),這些工具得到了日益廣泛的應(yīng)用。

GCC GNU 組織的免費(fèi) C 編譯器, Linux 的很多發(fā)布缺省安裝的就是這種。很多流行的自由軟件源代碼基本都能在 GCC 編譯器下編譯運(yùn)行。 所以掌握 GCC 編譯器的使用無論是對于編譯系統(tǒng)內(nèi)核還是自己的應(yīng)用程序都是大有好處的。

下面通過一個(gè)具體的例子,學(xué)習(xí)如何使用 GCC 編譯器。

Linux 操作系統(tǒng)中,對一個(gè)用標(biāo)準(zhǔn) C 語言寫的源程序進(jìn)行編譯,要使用 GNU gcc 編譯器。

例如下面一個(gè)非常簡單的 Hello 源程序 (hello.c)

/*******************************************************

* Institute of Automation, Chinese Academy of Sciences

* File Name hello.c

* Description introduce how to compile a source file with gcc

* Author Xueyuan Nie

* Date

*******************************************************/

void main()

{

printf("Hello the world\n")

}

要編譯這個(gè)程序,我們只要在 Linux bash 提示符下輸入命令:

$ gcc -o hello hello.c

gcc 編譯器就會生成一個(gè) hello 的可執(zhí)行文件。在 hello.c 的當(dāng)前目錄下執(zhí)行 ./hello 就可以看到程序的輸出結(jié)果,在屏幕上打印出 “ Hello the world ” 的字符串來。

命令行中 gcc 表示是用 gcc 來編譯源程序;

-o outputfilename 選項(xiàng)表示要求編譯器生成文件名為 outputfilename 的可執(zhí)行文件,如果不指定 -o 選項(xiàng),則缺省文件名是 a.out 。在這里生成指定文件名為 hello 的可執(zhí)行文件,而 hello.c 是我們的源程序文件。

gcc 是一個(gè)多目標(biāo)的工具。 gcc 最基本的用法是:

gcc [options] file...

其中的 option 是以 - 開始的各種選項(xiàng), file 是相關(guān)的文件名。在使用 gcc 的時(shí)候,必須要給出必要的選項(xiàng)和文件名。 gcc 的整個(gè)編譯過程,實(shí)質(zhì)上是分四步進(jìn)行的,每一步完成一個(gè)特定的工作,

這四步分別是:預(yù)處理,編譯,匯編和鏈接。它具體完成哪一步,是由 gcc 后面的開關(guān)選項(xiàng)和文件類型決定的。

清楚的區(qū)別編譯和連接是很重要的。編譯器使用源文件編譯產(chǎn)生某種形式的目標(biāo)文件 (objectfiles) 。在這個(gè)過程中,外部的符號引用并沒有被解釋或替換,然后我們使用鏈接器來鏈接這些目標(biāo)文件和一些標(biāo)準(zhǔn)的頭文件,最后生成一個(gè)可執(zhí)行文件。在這個(gè)過程中,一個(gè)目標(biāo)文件中對別的文件中的符號的引用被解釋,并報(bào)告不能被解釋的引用,一般是以錯(cuò)誤信息的形式報(bào)告出來。

gcc 編譯器有許多選項(xiàng),但對于普通用戶來說只要知道其中常用的幾個(gè)就夠了。在這里為讀者列出幾個(gè)最常用的選項(xiàng):

-o 選項(xiàng)表示要求編譯器生成指定文件名的可執(zhí)行文件;

-c 選項(xiàng)表示只要求編譯器進(jìn)行編譯,而不要進(jìn)行鏈接,生成以源文件的文件名命名但把其后綴由 .c .cc 變成 .o 的目標(biāo)文件;

-g 選項(xiàng)要求編譯器在編譯的時(shí)候提供以后對程序進(jìn)行調(diào)試的信息;

-E 選項(xiàng)表示編譯器對源文件只進(jìn)行預(yù)處理就停止,而不做編譯,匯編和鏈接;

-S 選項(xiàng)表示編譯器只進(jìn)行編譯,而不做匯編和鏈接;

-O 選項(xiàng)是編譯器對程序提供的編譯優(yōu)化選項(xiàng),在編譯的時(shí)候使用該選項(xiàng),可以使生成的執(zhí)行文件的執(zhí)行效率提高;

-Wall 選項(xiàng)指定產(chǎn)生全部的警告信息。

如果你的源代碼中包含有某些函數(shù),則在編譯的時(shí)候要鏈接確定的庫,比如代碼中包含了某些數(shù)學(xué)函數(shù),在 Linux 下,為了使用數(shù)學(xué)函數(shù),必須和數(shù)學(xué)庫鏈接,為此要加入 -lm 選項(xiàng)。也許有讀者會問,前面那個(gè)例子使用 printf 函數(shù)的時(shí)候?yàn)楹螞]有鏈接庫呢?在 gcc 中對于一些常用函數(shù)的實(shí)現(xiàn), gcc 編譯器會自動(dòng)去鏈接一些常用庫,這樣用戶就沒有必要自己去指定了。有時(shí)候在編譯程序的時(shí)候還要指定庫的路徑,這個(gè)時(shí)候要用到編譯器的 -L 選項(xiàng)指定路徑。比如說我們有一個(gè)庫在/home/hoyt/mylib 下,這樣我們編譯的時(shí)候還要加上 -L/home/hoyt/mylib 。對于一些標(biāo)準(zhǔn)庫來說,沒有必要指出路徑。只要它們在起缺省庫的路徑下就可以了, gcc 在鏈接的時(shí)候會自動(dòng)找到那些庫的。

GNU 編譯器生成的目標(biāo)文件缺省格式為 elf(executive linked file) 格式,這是 Linux 系統(tǒng)所采用的可執(zhí)行鏈接文件的通用文件格式。 elf 格式由若干段 (section) 組成,如果沒有特別指明,由標(biāo)準(zhǔn) c 源代碼生成的目標(biāo)文件中包含以下段: .text( 正文段 ) 包含程序的指令代碼, .data( 數(shù)據(jù)段 ) 包含固定的數(shù)據(jù),如常量,字符串等, .bss( 未初始化數(shù)據(jù)段 ) 包含未初始化的變量和數(shù)組等。

讀者若想知道更多的選項(xiàng)及其用法,可以查看 gcc 的幫助文檔,那里有許多對其它選項(xiàng)的詳細(xì)說明。

當(dāng)改變了源文件 hello.c 后,需要重新編譯它:

$ gcc -c hello.c

然后重新鏈接生成:

$ gcc o hello.o

對于本例,因?yàn)橹缓幸粋€(gè)源文件,所以當(dāng)改動(dòng)了源碼后,進(jìn)行重新的編譯鏈接的過程顯得并不是太繁瑣,但是,如果在一個(gè)工程中包含了若干的源碼文件,而這些源碼文件中的某個(gè)或某幾個(gè)又被其他源碼文件包含,那么,如果一個(gè)文件被改動(dòng),則包含它的那些源文件都要進(jìn)行重新編譯鏈接,工作量是可想而知的。幸運(yùn)的是, GNU 提供了使這個(gè)步驟變得簡單的工具,就是下面要介紹給大家的 GNU Make 工具。

GNU Make

make 是負(fù)責(zé)從項(xiàng)目的源代碼中生成最終可執(zhí)行文件和其他非源代碼文件的工具。 make 命令本身可帶有四種參數(shù):標(biāo)志、宏定義、描述文件名和目標(biāo)文件名。

其標(biāo)準(zhǔn)形式為:

make [flags] [macro definitions] [targets]

Unix 系統(tǒng)下標(biāo)志位 flags 選項(xiàng)及其含義為:

-f file 指定 file 文件為描述文件,如果 file 參數(shù)為 '-' 符,那么描述文件指向標(biāo)準(zhǔn)輸入。如果沒有 '-f' 參數(shù),則系統(tǒng)將默認(rèn)當(dāng)前目錄下名為 makefile 或者名為 Makefile 的文件為描述文件。在Linux 中, GNU make 工具在當(dāng)前工作目錄中按照 GNUmakefile makefile Makefile 的順序搜索makefile 文件。

-i 忽略命令執(zhí)行返回的出錯(cuò)信息。

-s 沉默模式,在執(zhí)行之前不輸出相應(yīng)的命令行信息。

-r 禁止使用隱含規(guī)則。

-n 非執(zhí)行模式,輸出所有執(zhí)行命令,但并不執(zhí)行。

-t 更新目標(biāo)文件。

-q make 操作將根據(jù)目標(biāo)文件是否已經(jīng)更新返回 "0" 或非 "0" 的狀態(tài)信息。

-p 輸出所有宏定義和目標(biāo)文件描述。

-d Debug 模式,輸出有關(guān)文件和檢測時(shí)間的詳細(xì)信息。

Linux make 標(biāo)志位的常用選項(xiàng)與 Unix 系統(tǒng)中稍有不同,下面只列出了不同部分:

-c dir 在讀取 makefile 之前改變到指定的目錄 dir

-I dir 當(dāng)包含其他 makefile 文件時(shí),利用該選項(xiàng)指定搜索目錄。

-h help 文擋,顯示所有的 make 選項(xiàng)。

-w 在處理 makefile 之前和之后,都顯示工作目錄。

通過命令行參數(shù)中的 target ,可指定 make 要編譯的目標(biāo),并且允許同時(shí)定義編譯多個(gè)目標(biāo),操作時(shí)按照從左向右的順序依次編譯 target 選項(xiàng)中指定的目標(biāo)文件。如果命令行中沒有指定目標(biāo),則系統(tǒng)默認(rèn) target 指向描述文件中第一個(gè)目標(biāo)文件。

make 如何實(shí)現(xiàn)對源代碼的操作是通過一個(gè)被稱之為 makefile 的文件來完成的,在下面的小節(jié)里,主要向讀者介紹一下 makefile 的相關(guān)知識。

makefile 基本結(jié)構(gòu)

GNU Make 的主要工作是讀一個(gè)文本文件 makefile makefile 是用 bash 語言寫的, bash 語言是很像 BASIC 語言的一種命令解釋語言。這個(gè)文件里主要描述了有關(guān)哪些目標(biāo)文件是從哪些依賴文件中產(chǎn)生的,是用何種命令來進(jìn)行這個(gè)產(chǎn)生過程的。有了這些信息, make 會檢查磁盤的文件,如果目標(biāo)文件的日期 ( 即該文件生成或最后修改的日期 ) 至少比它的一個(gè)依賴文件日期早的話, make 就會執(zhí)行相應(yīng)的命令,以更新目標(biāo)文件。

makefile 一般被稱為 “makefile” 或 “Makefile” 。還可以在 make 的命令行中指定別的文件名。如果沒有特別指定的話, make 就會尋找 “makefile” 或 “Makefile” ,所以為了簡單起見,建議讀者使用這兩名字。如果要使用其他文件作為 makefile ,則可利用類似下面的 make 命令選項(xiàng)指定 makefile 文件:

$ make -f makefilename

一個(gè) makefile 主要含有一系列的規(guī)則,如下:

目標(biāo)文件名: 依賴文件名

(tab ) 命令

第一行稱之為規(guī)則,第二行是執(zhí)行規(guī)則的命令,必須要以 tab 鍵開始。

下面舉一個(gè)簡單的 makefile 的例子。

executable : main.o io.o

gcc main.o io.o -o executable

main.o : main.c

gcc -Wall -O -g -c main.c -o main.o

io.o : io.c

gcc -Wall -O -g -c io.c -o io.o

這是一個(gè)最簡單的 makefile make 從第一條規(guī)則開始, executable makefile 最終要生成的目標(biāo)文件。給出的規(guī)則說明 executable 依賴于兩個(gè)目標(biāo)文件 main.o io.o ,只要 executable 比它依賴的文件中的任何一個(gè)舊的話,下一行的命令就會被執(zhí)行。但是,在檢查文件 main.o io.o 的日期之前,它會往下查找那些把 main.o io.o 做為目標(biāo)文件的規(guī)則。 make 先找到了關(guān)于 main.o 的規(guī)則,該目標(biāo)文件的依賴文件是 main.c makefile 后面的文件中再也找不到生成這個(gè)依賴文件的規(guī)則了。此時(shí), make 開始檢查磁盤上這個(gè)依賴文件的日期,如果這個(gè)文件的日期比 main.o 日期新的話,那么這個(gè)規(guī)則下面的命令 gcc -c main.c o main.o 就會執(zhí)行,以更新文件 main.o 。同樣 make 對文件 io.o 做類似的檢查,它的依賴文件是 io.c ,對 io.o 的處理和 main.o 類似。現(xiàn)在, 再回到第一個(gè)規(guī)則處,如果剛才兩個(gè)規(guī)則中的任何一個(gè)被執(zhí)行,最終的目標(biāo)文件executable 都需要重建 ( 因?yàn)?span> executable 所依賴的其中一個(gè) .o 文件就會比它新 ) ,因此鏈接命令就會被執(zhí)行。

有了 makefile ,對任何一個(gè)源文件進(jìn)行修改后,所有依賴于該文件的目標(biāo)文件都會被重新編譯 ( 因?yàn)?span> .o 文件依賴于 .c 文件 ) ,進(jìn)而最終可執(zhí)行文件會被重新鏈接 ( 因?yàn)樗蕾嚨?span> .o 文件被改變了 ) ,再也不用手工去一個(gè)個(gè)修改了。



劉暢 2009-12-05 00:45 發(fā)表評論
]]>
C++中的默認(rèn)構(gòu)造函數(shù)http://m.shnenglu.com/deercoder/articles/102113.html劉暢劉暢Fri, 27 Nov 2009 13:42:00 GMThttp://m.shnenglu.com/deercoder/articles/102113.htmlhttp://m.shnenglu.com/deercoder/comments/102113.htmlhttp://m.shnenglu.com/deercoder/articles/102113.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/102113.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/102113.html#include <iostream>
using namespace std;
class test
{
public:
    test(int a,float b)
    {
        x =  a;
        y = b;
    }
    void show()
    {
        cout << "x = " << x << endl
            << "y = " << y << endl;
    }
private:
    int x ;
    float y;
};
void main()
{
    test b(1,3.2);
    b.show();
}
完全沒有問題,自己定義了一個(gè)構(gòu)造函數(shù),而不是系統(tǒng)默認(rèn)的構(gòu)造函數(shù),這樣就不會出錯(cuò)了!!
然后看看下面的這段代碼:
#include <iostream>
using namespace std;
class test
{
public:
    void show()
    {
        cout << "x = " << x << endl
            << "y = " << y << endl;
    }
private:
    int x;
    double y;
};
void main()
{
    test b(1,3.2);
    b.show();
}
然后就會出錯(cuò),為什么,因?yàn)槟J(rèn)的構(gòu)造函數(shù)是沒有形參,所以你傳遞進(jìn)去的兩個(gè)形參就會報(bào)錯(cuò)!更不可能把你的那兩個(gè)參數(shù)賦值給它的private內(nèi)容中的形參。
還可以定義一個(gè)函數(shù)專門進(jìn)行賦值化或者初始化,這樣就相當(dāng)于一個(gè)構(gòu)造函數(shù),可是實(shí)現(xiàn)起來的話就要顯示的調(diào)用set函數(shù)。
class test
{
public:
    void set(int a,float b)
    {
         x = a;
         y = b;
    }
    void show()
    {
         cout << "x = " << x << endl
            << "y = " << y << endl;
    }
private:
    int x;
    float y;
};

void main()
{
    test b;
    b.set(1,3.4);
    b.show();
}


劉暢 2009-11-27 21:42 發(fā)表評論
]]>
C++中的重載運(yùn)算符http://m.shnenglu.com/deercoder/articles/102110.html劉暢劉暢Fri, 27 Nov 2009 13:38:00 GMThttp://m.shnenglu.com/deercoder/articles/102110.htmlhttp://m.shnenglu.com/deercoder/comments/102110.htmlhttp://m.shnenglu.com/deercoder/articles/102110.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/102110.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/102110.html1.為何要使用重載運(yùn)算符?

因?yàn)槲覀兿M褂玫筋惖囊恍┎僮鳎沟靡粋€(gè)類更像內(nèi)置的數(shù)據(jù)類型(int,float…………)

比如說對于int類型的變量,自增運(yùn)算符是默認(rèn)的,即定義int i,即可以用到i++,而現(xiàn)在對于自定義

的一個(gè)類,如果沒有定義++操作,那么程序時(shí)不會找到這個(gè)操作的相應(yīng)定義的,更不會執(zhí)行相應(yīng)的操

作!

也就是說,實(shí)際上重載運(yùn)算符是給了++…………這些運(yùn)算符一些新的定義和操作,結(jié)果很明顯,我們

使用自定義類的時(shí)候可以像內(nèi)置類型一樣那么自然,簡單,符合我們的用戶習(xí)慣!

 

2.如何定義重載運(yùn)算符?

前面說到重載,對,對于一個(gè)運(yùn)算符,實(shí)際上語言的本身是定義默認(rèn)的操作的,比如說加法運(yùn)算,對于int類型可以知道是整數(shù)相加,對于float類型也是如此,這實(shí)際上也是一種重載,對于不同類型的數(shù)據(jù),可以用同一個(gè)運(yùn)算符來重載!

現(xiàn)在我們的問題是要增加自己定義的類的操作。

OK,言歸正傳,有兩種定義的方法,使用類內(nèi)置成員函數(shù)的方法和友元的方法來定義:

 

運(yùn)算符的重載形式有兩種:重載為類的成員函數(shù)和重載為類的友元函數(shù)。



劉暢 2009-11-27 21:38 發(fā)表評論
]]>
scanf函數(shù)詳解http://m.shnenglu.com/deercoder/articles/102100.html劉暢劉暢Fri, 27 Nov 2009 13:21:00 GMThttp://m.shnenglu.com/deercoder/articles/102100.htmlhttp://m.shnenglu.com/deercoder/comments/102100.htmlhttp://m.shnenglu.com/deercoder/articles/102100.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/102100.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/102100.htmlwhile(scanf("%d%d",&a,&b)!=EOF),這個(gè)語句就是說,當(dāng)讀取沒有到達(dá)文件尾的話,就不停的讀取。
scanf 函數(shù)的返回值反映的是按照指定的格式符正確讀入的數(shù)據(jù)的個(gè)數(shù)。如果輸入數(shù)據(jù)與指定格式不符,則會產(chǎn)生輸入錯(cuò)誤。遇到輸入錯(cuò)誤,scanf函數(shù)會立即終止,返回已經(jīng)成功讀取的數(shù)據(jù)的個(gè)數(shù)。所以,通過scanf函數(shù)的返回值和指定輸入數(shù)據(jù)的個(gè)數(shù)(由格式符決定)的比較,可以判斷數(shù)據(jù)輸入是否成功。

scanf("%d%d",&a,&b); 這個(gè)語句會去讀兩個(gè)整型到a, b。如果讀取成功,應(yīng)該返回2。就是它成功讀取變量的個(gè)數(shù)。


論壇上很多人對scanf的不太了解,導(dǎo)致程序出錯(cuò),我想把scanf的具體用法貼出來,希望大家可以共同進(jìn)步,有什么不對的地方可以提出來。
int scanf(char *format,...);
這應(yīng)該是scanf的標(biāo)準(zhǔn)形式。先說說關(guān)于他的返回值的問題。
庫函數(shù)幾乎都是有返回值的,有些人可能很奇怪,怎么很少人用過scanf的返回值呢?
scanf會返回成功接收到的變量數(shù)量的值。比如scanf("%d",&j"),與scanf("%d=",&j),如果接受成功的話返回值都是1
我用如下語句作了測試
#include <stdio.h>
int main (){
    int j;
    printf ("%d",scanf("%d\n",&j));
    return 0;
}
如果你開始就輸入回車,程序會繼續(xù)等待你輸入,因?yàn)樵谳斎霐?shù)字的時(shí)候,scanf會跳過空白字符。(the c programming language 上說,scanf實(shí)際上是用getchar()接受由數(shù)字組成的字符串,再轉(zhuǎn)換成數(shù)字)
如果我輸入ctrl-z(unix上是ctrl-d)則會返回-1(隨編譯器而定).這實(shí)際上就是常量EOF的值,也就是所謂的返回eof
如果我鍵入的不是數(shù)字返回值就是0。但是如果我輸入浮點(diǎn)數(shù),又會怎么樣呢?
我舉的例子中同樣會返回1,但是緩沖區(qū)會留下垃圾,如果是scanf("%d%d",&a,&b);則會出錯(cuò)。
這是可以使用一個(gè)庫函數(shù)fflush(stdin)來清除緩沖。不過貌似雨中飛燕大姐說這個(gè)用法是非標(biāo)準(zhǔn)的。K&R,只是說行為沒有定義,但我們可以使用while((c=getchar())!='\n'&&c!=EOF);同樣可以清除后面的垃圾
scanf的格式匹配還是比較簡單,一定要記住的就是普通變量一定要加上&,否則編譯器無法檢測錯(cuò)誤,但運(yùn)行肯定會段錯(cuò)誤。
    ┏━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
    ┃ 代  碼 │             意                          義            
    ┠────┼────────────────────────────┨
    ┃   %a   │讀浮點(diǎn)值(僅適用于 C99)                                 
    ┃   %A   │讀浮點(diǎn)值(僅適用于 C99)                                 
    ┃   %c   │讀單字符                                                
    ┃   %d   │讀十進(jìn)制整數(shù)                                            
    ┃   %i   │讀十進(jìn)制、八進(jìn)制、十六進(jìn)制整數(shù)                          
    ┃   %e   │讀浮點(diǎn)數(shù)                                                
    ┃   %E   │讀浮點(diǎn)數(shù)                                                
    ┃   %f   │讀浮點(diǎn)數(shù)                                                
    ┃   %F   │讀浮點(diǎn)數(shù)(僅適用于 C99)                                 
    ┃   %g   │讀浮點(diǎn)數(shù)                                                
    ┃   %G   │讀浮點(diǎn)數(shù)                                                
    ┃   %o   │讀八進(jìn)制數(shù)                                             
    ┃   %s   │讀字符串                                                
    ┃   %x   │讀十六進(jìn)制數(shù)                                            
    ┃   %X   │讀十六進(jìn)制數(shù)                                            
    ┃   %p   │讀指針值                                                
    ┃   %n   │至此已讀入值的等價(jià)字符數(shù)                                
    ┃   %u   │讀無符號十進(jìn)制整數(shù)                                      
    ┃  %[ ]  │掃描字符集合                                            
    ┃   %%   │讀 % 符號(百分號)                                       
    ┗━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
前面都很簡單,%p,%n很少用到,跳過。要輸入%必須要在前面再加一個(gè)%,
重點(diǎn)來談?wù)?s和%[]。%s是讀入一個(gè)數(shù)組,他與gets的區(qū)別就在于%s會以任何的空字符結(jié)束,而gets是回車結(jié)束。
同樣%s前可以加數(shù)字,表示只讀多少個(gè)。
ANSI C 標(biāo)準(zhǔn)向 scanf() 增加了一種新特性,稱為掃描集(scanset)。 掃描集定義一個(gè)字符集合,可由 scanf() 讀入其中允許的字符并賦給對應(yīng)字符數(shù)組。 掃描集合由一對方括號中的一串字符定義,左方括號前必須綴以百分號。 例如,以下的掃描集使 scanf() 讀入字符 A、B 和 C:
    %[ABC]
使用掃描集時(shí),scanf() 連續(xù)吃進(jìn)集合中的字符并放入對應(yīng)的字符數(shù)組,直到發(fā)現(xiàn)不在集合中的字符為止(即掃描集僅讀匹配的字符)。返回時(shí),數(shù)組中放置以 null 結(jié)尾、由讀入字符組成的字符串。
對于許多實(shí)現(xiàn)來說,用連字符可以說明一個(gè)范圍。 例如,以下掃描集使 scanf() 接受字母 A 到 Z:
%[A-Z]
重要的是要注意掃描集是區(qū)分大小寫的。因此,希望掃描大、小寫字符時(shí),應(yīng)該分別說明大、小寫字母。
對于%[]還可以用^+任意字符(包括eof)來結(jié)束字符串的輸入。比如%[^EOF]就是直到有EOF輸入,字符串才中止。
但一定要記住就是c語言是緩沖輸入,即使你%[^a],再你輸入回車之前輸入多少的a都是不可能結(jié)束的。
%s的輸入會跳過空白字符,但是%c則不會。
這也就是
scanf("%d",&h);
scanf("%c",&c);
如果這寫的話,變量c放的一定是回車。
如果想實(shí)現(xiàn)這種輸入,可以在兩個(gè)語句之間加入一個(gè)getchar(),他可以吃掉這個(gè)回車,
也可用scanf("%d %c",&h,&c);來做,再輸入數(shù)字后加一個(gè)空格。就可以了
但千萬別用scanf("%d\n",&h)!!!!!!!!k&r說的十分清楚,任何非格式化的字符都需要完全匹配。
意味著,只有輸入數(shù)字后面再加\n才是合法的。
還有就是*加在任何項(xiàng)的前面表示該項(xiàng)不符值,別的就沒什么好說的了


劉暢 2009-11-27 21:21 發(fā)表評論
]]>
數(shù)據(jù)結(jié)構(gòu)與STL(一)http://m.shnenglu.com/deercoder/articles/100748.html劉暢劉暢Wed, 11 Nov 2009 14:49:00 GMThttp://m.shnenglu.com/deercoder/articles/100748.htmlhttp://m.shnenglu.com/deercoder/comments/100748.htmlhttp://m.shnenglu.com/deercoder/articles/100748.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/100748.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/100748.html

總括:此部分寫的還是比較好,特別是第一章,介紹了面向?qū)ο缶幊痰奶貏e,關(guān)鍵是從一個(gè)解決問題的角度來分析,相比較而言,C++ Primer更顯得雜亂無章。通過對問題深入,根據(jù)解決問題的方法,來實(shí)現(xiàn)最合理化的得到目的,從而引出繼承等等一系列概念和手段。

 

1.     構(gòu)造器

e.g  Data(int monthln,int dayln,int yearln)

{

  Month = monthln;

  Day  =  dayln;

  Year  =  yearln;

}

Date thedate(7,1,2003);

Date thisdate;

第一個(gè)調(diào)用了用戶定義的構(gòu)造函數(shù)來初始化相應(yīng)的數(shù)據(jù),問題是,如果是0-參數(shù)的構(gòu)造器呢?就是下面的那種情況,沒有任何參數(shù),默認(rèn)的,編譯器會自動(dòng)生成一個(gè),這個(gè)就是缺省構(gòu)造函數(shù),但是如果用戶定義了一個(gè)0-參數(shù)的構(gòu)造器,那么就不會自動(dòng)生成這樣一個(gè)了。從安全性角度去看,定義類的時(shí)候,應(yīng)當(dāng)顯示的定義一個(gè)缺省構(gòu)造函數(shù)(如果有繼承的話,沒有定義基類的缺省構(gòu)造函數(shù)是會出現(xiàn)編譯錯(cuò)誤的。)

注意:一個(gè)構(gòu)造器,不管有沒有參數(shù),都不會自動(dòng)初始化類的字段,所以必須自己顯式的寫出代碼來初始化。

2.     繼承的細(xì)節(jié)

我們可能經(jīng)常遇到這樣的問題,在開發(fā)一個(gè)類B的時(shí)候,發(fā)現(xiàn)其他的一些類A的方法是非常有用的。一個(gè)可能性就是令BA繼承,也就是說,BA的子類,這樣就可以使用A的所有方法;還有一種方案,就是在B中定義一個(gè)字段,它的類型是A,通過這個(gè)字段調(diào)用A的方法。前者,就是“Is a(是一個(gè))的關(guān)系,而后者就是”Has a”(有一個(gè)的關(guān)系)。如何實(shí)現(xiàn)這種區(qū)分更好的來規(guī)劃呢,一般,如果類B分享了A的全部功能,那么BA繼承是更好的選擇,但是如果B中只有一個(gè)組件能從A中受益,那么就是Has a來實(shí)現(xiàn)了。。

 





劉暢 2009-11-11 22:49 發(fā)表評論
]]>
虛函數(shù)的幾點(diǎn)注意和重載運(yùn)算符的疑問http://m.shnenglu.com/deercoder/articles/97488.html劉暢劉暢Mon, 28 Sep 2009 13:36:00 GMThttp://m.shnenglu.com/deercoder/articles/97488.htmlhttp://m.shnenglu.com/deercoder/comments/97488.htmlhttp://m.shnenglu.com/deercoder/articles/97488.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/97488.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/97488.html以下是幾點(diǎn)學(xué)習(xí)C++后續(xù)內(nèi)容的體會和注意之處,更新ing…………

對于類的幾點(diǎn)認(rèn)識:
---------《以下的幾條規(guī)范是便于多文件管理,分別編譯的》---------
1.將類的聲明放在h文件中,注意是聲明,而不是定義,命名之,最好用類

名來給這個(gè)h文件命名。
2.將類的定義放在cpp文件中,單獨(dú)一個(gè)cpp文件中,以類名命名之。
3.將主函數(shù)放在單獨(dú)一個(gè)文件cpp中,文件名無所謂,不過取main的話便于

找到主體程序在哪里。

 

下面要說的是虛函數(shù):virtual
1.在類中要聲明virtual,但是放在對應(yīng)的定義cpp文件中的時(shí)候,要注意去

掉virtual,否則會報(bào)錯(cuò)。
2.注意聲明和定義的原型都必須完全一樣,比如聲明中有一個(gè)const,那么

在定義中必須也要有,否則的話就會報(bào)錯(cuò),說不認(rèn)識這個(gè)函數(shù)。
3.在類外定義的話一定要加上類型限定符,作用域限定符。

 下面寫點(diǎn)代碼:

student.h頭文件 
1
#ifndef STUDENT_H
 2#define STUDENT_H
 3
 4#include <iostream>
 5using std::string;
 6
 7
 8class Student
 9{
10public:
11    Student(const string& a="",double b=0.0,int c=0):score(b),name(a),number(c)
12    {}
13    ~Student(){}
14    //void show_score();
15    //void modify(Student& a);
16    friend Student operator+(Student& a,Student& b);
17    friend Student operator-(Student& a,Student& b);
18    friend std::ostream& operator<<(std::ostream &a,Student& b);  //改變狀態(tài)的一般都要定義為類成員,++,--及*
19    friend std::istream& operator>>(std::istream &a,Student& b);//進(jìn)行算術(shù)運(yùn)算和輸入輸出的就定義為友元
20    //Student& operator=(Student& a);
21private:
22    double score;
23    string name;
24    int number;
25}
;
26
27#endif

 

 1student.cpp文件,實(shí)現(xiàn)類函數(shù)
 2#include <iostream>
 3#include <string>
 4#include "Student.h"
 5using std::cout;
 6using std::cin;
 7using std::endl;
 8
 9Student operator+(Student& a,Student& b) //這里面又用到了<<這個(gè)運(yùn)算符的。而這個(gè)你已經(jīng)自定義了啊!
10{
11    cout << a.name << "" << b.name << "分?jǐn)?shù)之和為:" << a.score+ b.score;
12    cout << endl;
13    return a;
14}

15Student operator-(Student& a,Student& b)
16{
17    cout << a.name << " 比 " << b.name << "" << 
18        (a.score > b.score)?(a.score-b.score):(b.score - a.score);
19    cout << endl;
20    return a;
21}

22std::ostream& operator<<(std::ostream &a,Student& b)
23{
24    a << b.name<<"的分?jǐn)?shù):" << b.score << "\t 學(xué)號:" << b.number << endl;
25    return a;
26}

27std::istream& operator>>(std::istream &a,Student& b)
28{
29    a >> b.name >>b.number >> b.score  ; 
30    return a;
31}

32/*
33問題1,修改定義函數(shù)的時(shí)候沒有修改聲明,為什么這么粗心呢?
342.返回值,特別是對于有輸入輸出流的,要有返回值啊!
353.流的返回值和調(diào)用。
364.返回值怎么能為void呢?因?yàn)橐敵鯽+b,所以要返回一個(gè)值啊,相當(dāng)于opreator+(a,b)
37*/

 

主函數(shù):main.cpp 
1
#include <iostream>
 2#include "Student.h"
 3
 4using namespace std;
 5
 6int main()
 7{
 8    Student a,b;
 9    cout << "依次輸入姓名,學(xué)號,成績" << endl;
10    cin >> a >> b;
11    cout << a << b;
12    cout << endl;
13    return 0;
14}

15

 我想說的是自己遇到的幾個(gè)問題:
1.重載<< 和>>操作符:這兩個(gè)操作符只能作為友元而不是成員函數(shù)。作為重載運(yùn)算符,有兩種定義形式,即:成員函數(shù)形式和非成員函數(shù)形式,對于成員函數(shù)的形式,它的聲明方式是形參數(shù)要比操作數(shù)少一個(gè),為什么呢,因?yàn)槭÷粤四J(rèn)的形參this指針,而這個(gè)省略的形參正式左操作數(shù),即定義這樣的運(yùn)算符的時(shí)候,this指針指向的是左操作數(shù)。其次,就是非成員函數(shù),即友元聲明方式。友元聲明方式較簡單合理,形參數(shù)和操作數(shù)相同,顯示的是合理的調(diào)用函數(shù)的方式。
2.什么時(shí)候用成員函數(shù)的聲明方式,什么時(shí)候用友元函數(shù)的聲明方式?此部分需注意的是:1,對于=,(),【】,->這四個(gè)操作符只能作為成員函數(shù)來聲明,想想這個(gè)是為什么呢?
  (1)只能使用成員函數(shù)重載的運(yùn)算符有:=、()、[]、->、new、delete。
  (2)單目運(yùn)算符最好重載為成員函數(shù)。
  (3) 對于復(fù)合的賦值運(yùn)算符如+=、-=、*=、/=、&=、!=、~=、%=、>>=、<<=建議重載為成員函數(shù)。
  (4) 對于其它運(yùn)算符,建議重載為友元函數(shù)。
關(guān)于此問題的詳細(xì)討論,見:http://www.rupeng.com/forum/thread-4468-1-1-uid3573.html
在C++編程思想中有述,據(jù)網(wǎng)友而言,大意是:如果可以定義為全局變量的話,那么就可以定義為int operator =(int,mytype),而這種方式的話就不對了,因?yàn)橘x值=是和左邊的類緊密相連的,所以會定義為成員函數(shù)的……

 此部分詳細(xì)關(guān)注中………………

3.關(guān)于重載運(yùn)算符的返回值的問題:
我們是不是已經(jīng)習(xí)慣于重載運(yùn)算符有一個(gè)返回值類型呢?對,是的,我們經(jīng)常這么干,現(xiàn)在的問題是,如果我定義為void的話,會出錯(cuò)嗎?
不會的,將上面的換成void的返回值照樣不會出錯(cuò)!
為什么呢?因?yàn)槎x有返回值只是我們的一個(gè)習(xí)慣,為何有返回值,因?yàn)槲覀円祷刂担馨装V吧?可是事實(shí)就是這樣,想想這個(gè):
cout << a + b;  這個(gè)代碼很簡單是吧,a+b 實(shí)際上就是operator+(a,b)的一個(gè)等價(jià)形式,我要能夠cout ,那么必然有一個(gè)返回值,否則的話調(diào)用這個(gè)函數(shù)沒有返回值的話我怎么cout它呢?于是乎我們在定義加法的重載的時(shí)候就只能在前面加上a和b的類型了。
好,現(xiàn)在我沒有類型了,void,怎么辦?很簡單,直接用 a + b;  因?yàn)樗喈?dāng)于:operator+(a,b);也就是調(diào)用一個(gè)函數(shù),直接調(diào)用,沒有返回值。
當(dāng)然,結(jié)果也還是對的,各位可以看看…………

注意我想說的,學(xué)習(xí)計(jì)算機(jī)主要的是學(xué)習(xí)那個(gè)思想,而不是片面的記住語法或者其他,要想想為什么需要這樣,這樣我們的思維才會和計(jì)算機(jī)接近,才能更好的理解計(jì)算機(jī),上面的這個(gè)例子就是很好的說明,這個(gè)函數(shù)很另類了吧,可是它完全符合各種語法,能夠順利運(yùn)行。。
可是要記住的是,重載運(yùn)算符為什么要重載,就是為了使用和內(nèi)置類型一樣的自然,如果像上面我定義的void那樣,那么恐怕就很難懂了,如果將減號定義為加法的話,那么就更加的匪夷所思。也很難讀了,違背了初衷,也就是一種錯(cuò)誤的做法了,雖然理論上是可行的。。

計(jì)算機(jī)是一個(gè)很奇妙的東西,需要我們用心去體會……
                                                                                                                  ------------------------------<本章完>

 

 

 

 

 

 

 

 

 

 



劉暢 2009-09-28 21:36 發(fā)表評論
]]>
C++中的重載運(yùn)算符----------------由運(yùn)算符引起的一個(gè)BUGhttp://m.shnenglu.com/deercoder/articles/97357.html劉暢劉暢Sun, 27 Sep 2009 06:15:00 GMThttp://m.shnenglu.com/deercoder/articles/97357.htmlhttp://m.shnenglu.com/deercoder/comments/97357.htmlhttp://m.shnenglu.com/deercoder/articles/97357.html#Feedback0http://m.shnenglu.com/deercoder/comments/commentRss/97357.htmlhttp://m.shnenglu.com/deercoder/services/trackbacks/97357.html閱讀全文

劉暢 2009-09-27 14:15 發(fā)表評論
]]>
99久久精品无码一区二区毛片| 久久久久久久久无码精品亚洲日韩| 狠色狠色狠狠色综合久久| 999久久久国产精品| 久久久无码精品亚洲日韩蜜臀浪潮| 99久久夜色精品国产网站| 久久国产精品久久国产精品| 人妻少妇精品久久| www.久久99| 午夜精品久久久久久毛片| 精品久久久久久无码中文字幕| 思思久久精品在热线热| 亚洲午夜久久久影院伊人| 久久99精品国产麻豆蜜芽| 国产亚洲欧美精品久久久| 亚洲天堂久久久| 青草影院天堂男人久久| 日韩人妻无码精品久久久不卡| 无码精品久久一区二区三区| 久久狠狠色狠狠色综合| 久久这里只有精品18| 日产久久强奸免费的看| 亚洲国产精品久久久久久| 久久久噜噜噜久久中文福利| 超级碰碰碰碰97久久久久| 欧洲国产伦久久久久久久| 国产精品久久久久久久午夜片| 久久国产热精品波多野结衣AV| 一本一本久久A久久综合精品| 美女久久久久久| 久久久久久极精品久久久 | 国产精品久久久久a影院| 久久久久久亚洲精品不卡| 91精品国产高清久久久久久国产嫩草 | 波多野结衣AV无码久久一区| 无码国内精品久久人妻麻豆按摩| 国产三级精品久久| 国产精品美女久久久网AV| 国产精品青草久久久久福利99 | 色婷婷综合久久久久中文一区二区| 亚洲一级Av无码毛片久久精品|