久久一区国产,美女精品国产,亚洲一区欧美http://www.shnenglu.com/deercoder/category/11912.html身是菩提樹, 心如明鏡臺; 時時勤拂拭, 莫使惹塵埃zh-cnSat, 27 Mar 2010 14:30:49 GMTSat, 27 Mar 2010 14:30:49 GMT60cin讀取失敗后進入死循環?-------剖析輸入流的條件http://www.shnenglu.com/deercoder/articles/110696.html劉暢劉暢Sat, 27 Mar 2010 13:59:00 GMThttp://www.shnenglu.com/deercoder/articles/110696.htmlhttp://www.shnenglu.com/deercoder/comments/110696.htmlhttp://www.shnenglu.com/deercoder/articles/110696.html#Feedback0http://www.shnenglu.com/deercoder/comments/commentRss/110696.htmlhttp://www.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;
}




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

分析:

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

但是如果用戶錯誤的輸入了一個字符char,那么,后果就是一直執行while循環!

錯誤分析:

當cin嘗試將輸入的字符讀為int型數據失敗后,會產生一個錯誤狀態--cin.fail().而要用cin讀取輸入流中的數據,輸入流必須處于無錯誤狀態。因此,由于錯誤狀態的存在,會一直執行while循環。

錯誤修正:

#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的錯誤狀態
   cin.sync(); //清空輸入緩沖區
}
return 0;
}

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

其中std::cin.sync();這一句必不可少,因為所有從標準輸入設備輸入的數據都是先保存在緩沖區中,然后istream對象再從緩沖區中進行提取。如果不清空緩存,下次在讀取數據的時候又會再次產生錯誤,也會陷入死循環。







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

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

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

 

下面開始正式的tips:

  1. int到底占用多少個字節?

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

    答:首先摘錄網上的朋友們的留言:

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

 

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

 


int僅是語言標準中的一項定義,定義了這類數據的性質、范圍、操作等;
具體少長,標準并未硬性規定,而由編譯器自行決定,甚至編譯器也可由用戶根據需要自行設定。
基于此,可認為還是受編譯器決定,所謂縣官不如現管。

 

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

但是,編譯器沒有必要一定要這么做,對于TC 2.0這樣的16位軟件,曾經在Dos下呼風喚雨,但是到了32位機下,仍然是可以用的,這樣的話,也就是說,它運算的結果是2個字節,可是因為軟件的限制,只能夠模擬出來16位機,所以雖然可以在32位上面跑,但是不能反映實際的字長。但是,沒有多少人會在32位機子上面模擬出128位或者64位,因為這樣尋址的話效率很低下,硬件條件沒有達到,利用軟件來模擬是不夠的。于是,就是上面的那種說法。由于VC以及其他軟件更新很快,而且及時開發了Win 32下的版本,所以是32位軟件,也就是說,它計算處理的int是4個字節。

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

當然,這個問題本身沒有多少討論的價值,不過,如果在面試或者考試中,遇到sizeof的操作,最好問清楚是在32位機子下還是16位或者是64位,這個能夠反映你的嚴謹和認真。

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

 

 

 

 

 

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

    在java中,很明顯是會覆蓋的,因為繼承的特性,注意同名函數,要求還有原型相同,否則的話,調用的時候是可以區分開來的,而當原型相同的時候,那么派生類會繼承基類的函數,同時由于有一個自己的函數,所以兩個同名,同原型的函數,是會發生覆蓋的。

上代碼看之:

#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;

}

輸出的結果是:

        

從上面的結果可以看到,派生類的指針是調用派生類的對應的函數,配套的,而且還選擇作用域較小的,而這樣的優先級就要高些。這里有一個強制類型轉換,因為是要將派生類的指針指向基類的對象,會發生截斷,所以一定要強制類型轉換,與此對比的是,基類的指針指向派生類的話不需要強制類型轉換也可以實現。從結果可以看到,如果實行了轉換,那么還是聲明時候的類型,所以抓住了這點,聲明時是B類型的,那么就調用B的moveto,所以輸出時2,下面的另外一些的輸出,就都是1了,也可以看到,如果要顯示的調用被隱藏的基類,需要用作用域限定符來指示。

 

 2.隱藏的this指針不是實際的一個參數,也就是說,實際調用函數中,不能夠使用這個參數。
上代碼:

#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;

}

 

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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



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

 

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

#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;

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

這個也是Java中遇到的,如果有這種情況,java中用到的是this引用來顯示的圈定范圍,而C++中,也需要用this指針來顯示的解決這個問題?;蛘哂米饔糜蛳薅ǚ麃斫鉀Q。

更改后才會出現正確的結果,而以前,自己一直都沒有注意到。

為何會這樣呢?因為按照范圍從小到大的順序,形參的范圍更小,所以替換的就是恒等式,那么自然不會顯示的來給成員賦值,因為類的成員作用范圍更小,此處,要謹記。

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

 



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


a.       常量型成員變量

b.       引用型成員變量

c.        靜態成員變量

d.       整型靜態常量成員變量

e.       非整型靜態常量成員變量

 


對于常量型成員變量和引用型成員變量的初始化,必須通過構造函數初始化列表的方式進行。在構造函數體內給常量型成員變量和引用型成員變量賦值的方式是行不通的。

 

靜態成員變量的初始化也頗有些特別。

 

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

// Initialization of Special Data Member

#include <iostream>

using namespace std;

 

class BClass

{

public:

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

         {                                             // 參數化列表的方式進行初始化。在構造函數體內進

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

 

         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;                                             // 靜態成員變量

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

         static const int csi;                                // 靜態常量成員變量

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

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

         //static const double csd2 = 99.9;      // error: 只有靜態常量整型數據成員才可以在類中初始化

};

 

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

int BClass::si = 0;

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

const int BClass::csi = 1;

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

const double BClass::csd = 99.9;

 

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

// 但在VC2003中如果有下面一行將會產生錯誤,而在VC2005中,下面這行則可有可無,

// 這個和編譯器有關。

const int BClass::csi2;

 

int main(void)

{

         BClass b_class;

         b_class.print_values();

 

         return 0;

}

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/pathuang68/archive/2009/11/25/4867323.aspx



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

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

 

1.     字符串數組的大小如何得到。我們都知道,對于一個字符串數組,要得到它的大小,有多種辦法,常見的有下面的兩種。

.利用sizeof運算符來實現?,F在假設有一個字符數組str[] = “hello,world”,為了得到的它的長度,我們利用這種辦法

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

有這種疑問,是因為對我們字符數組沒有一個更好的理解。上面初始化一個字符數組,是利用字符串來初始化的,

而字符串的話,默認的是會有一個以null結尾的標志,這個具體的就是’\0’,有很多人的程序有問題,可能都是因為這里

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

具體來說說這個原理吧:sizeof是表示計算大小,即字節數。這里以一個數組名來作為參數,就是計算這個數組的大小。

上面說到,字符串數組的話是有一個默認的null來封口的,所以,計算的大小事包括這個單元的。于是sizeof(str)的大小

就是12 個字節,然后計算一個單元,即一個字符char類型的占據的字節數,也就是1個字節,兩者相除,就是12的結果。

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

 

2.  在《C++ Primer》一書中,將以null結束的字符數組稱為字符數組,標準庫提供一批處理這類字符數組的函數,包括strlen,strcmp,strcat,strcpy,strncat,strncpy等等,而這些函數中的參數,都是以null結束的字符數組名,也就是字符數組的指針,而得到長度,返回的就是一個長度,沒有包括null這個單元,所以實際上,上面的那個還有一個方法,可以調用此函數來實現。當然,還有一點要注意的是,strcat連接函數,將右邊的字符串連接到左邊,所以對于左邊的字符串,一定要準確的計算出長度,如果不慎,就會溢出,出現嚴重的錯誤??纯聪旅孢@個例子:

Char s1[25] = “hello,world”;  char s2[20] = “0123456789012345678”(剛好是20個字符,為何,因為有一個null).所以現在的那個s2[20] = “01234567890123456789”,此時你會發現,字符串就有20個,含有一個null,注意就溢出了,OK。

下面,如果我將上面的那個采用字符串連接函數來做,就是strcat(s1,s2),那么又會出現錯誤。因為溢出了。

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



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

劉暢 2009-12-27 18:47 發表評論
]]>
[原創]操作系統的進程http://www.shnenglu.com/deercoder/articles/102918.html劉暢劉暢Thu, 10 Dec 2009 06:41:00 GMThttp://www.shnenglu.com/deercoder/articles/102918.htmlhttp://www.shnenglu.com/deercoder/comments/102918.htmlhttp://www.shnenglu.com/deercoder/articles/102918.html#Feedback0http://www.shnenglu.com/deercoder/comments/commentRss/102918.htmlhttp://www.shnenglu.com/deercoder/services/trackbacks/102918.htmlLinux學習

1.操作系統的實驗基本上都是以linux下面的系統編程為內容,進程的調度和創建都是很基本的內容。很幸運的是,linux為我們提供了很多的系統函數,供我們來使用。通過熟悉這些函數,使得我們可以能夠對進程和程序有一個更良好的認識。

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

 fork()函數,Linux系統調用

  頭文件:

  #include <unistd.h>

  函數定義:

  int fork( void );

  返回值:

  子進程中返回0,父進程中返回子進程ID,出錯返回-1

  函數說明:

  一個現有進程可以調用fork函數創建一個新進程。由fork創建的新進程被稱為子進程(child process)。fork函數被調用一次但返回兩次。兩次返回的唯一區別是子進程中返回0值而父進程中返回子進程ID。

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

  示例代碼:

  #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.      下面來詳細的說說我的程序和問題,通過比較學習,相信對于linux的學習,特別是進程和程序直接的關系有一個更好的理解。

Process2.c

Code

#include <stdio.h>

#include <unistd.h>

int main()

{

 int p1;

 p1=fork();

 if (p1 == 0)      //子進程,輸出它的父進程和當前進程的PID

 {

     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;

}

分析:此進程的運行結果是?(思考后再回答)

是的,有兩種情況。首先,程序運行到fork語句之后,創建進程,此時有兩種情況,創建成功和不成功,而成功又有兩種情況,fork的返回值為0,則運行的是創建的子進程,fork的返回值為1,則運行的是父進程。用if語句來判斷,OK。

那么問題就是,if語句中沒有問題,p10,說明創建成功,運行的是子進程,可是else語句來說,卻又兩種情況,如果創建不成功也在else語句中,那么此時當前進程就是那個原始的進程,而它的父進程卻是1PID,因為所有的進程都是1號進程的子進程,這個是linux下面的結論。操作系統的知識。

所以,此時的結果應該是:

father PID is 32667

current PID is 15254

 

current PID is 15255

father PID is 1

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

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

 

 

 

current PID is 15302 //子進程

father PID is 15301[l1]      

 

father PID is 32667 //父進程

current PID is 15301

 

PID = 13501

父進程

PID = 13504

子進程

系統創建了當前的那個父進程,就是程序運行的進程

PID = 32667

 

 

 

 

 

 

 

 

 

 

 

 

 


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

如下:

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] 

 

 

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

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

 

看完了上面的那個例子,下面的這個例子就是小case了,當然,其實上面的那個例子就是由下面的這個例子引申來的,是因為寫錯了代碼而引起的。當然,根據錯誤,我們收獲不小啊!

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;

}

 

結果如下:

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]父進程的PID剛好就是下面的那個父進程的current PID,說明下面的那個進程就是父進程。

 [l2]看看這個程序,雖然比較流氓,不過可以根據結果知道,如果創建失敗的話,那么也會有一個當前的PID,而且還是由一個1號進程創建。

 [l3]當前是子進程,而它的父進程恰好是下面的這個進程!

 [l4]父進程就是這個進程,而它的那個子進程卻是由系統創建的!


后來通過查閱資料知道,如果父進程創建一個子進程后其先結束,那么子進程就變成了孤兒進程,這樣的話,就會由初始化進程,即1號進程來領養。所以會顯示它的父進程為1.至此,完美的解決了。。

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


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

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

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

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

這些工具都是按 GPL 版權聲明發布,任何人可以從網上獲取全部的源代碼,無需使用任何費用。關于 GNU 和公共許可證協議的詳細資料, 讀者可以參看 GNU 網站的介紹,

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

GNU 開發工具都是采用命令行的方式,用戶掌握起來相對比較困難,不如基于 Windows 系統的開發工具好用,但是 GNU 工具的復雜性是由于它更貼近編譯器和操作系統的底層,并提供了更大的靈活性。一旦學習和掌握了相關工具后,就了解了系統設計的基礎知識。

運行于 Linux 操作系統下的自由軟件 GNU gcc 編譯器,不僅可以編譯 Linux 操作系統下運行的應用程序,還可以編譯 Linux 內核本身,甚至可以作交叉編譯,編譯運行于其它 CPU 上的程序。所以,在進行嵌入式系統應用程序開發時,這些工具得到了日益廣泛的應用。

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

下面通過一個具體的例子,學習如何使用 GCC 編譯器。

Linux 操作系統中,對一個用標準 C 語言寫的源程序進行編譯,要使用 GNU gcc 編譯器。

例如下面一個非常簡單的 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") ;

}

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

$ gcc -o hello hello.c

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

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

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

gcc 是一個多目標的工具。 gcc 最基本的用法是:

gcc [options] file...

其中的 option 是以 - 開始的各種選項, file 是相關的文件名。在使用 gcc 的時候,必須要給出必要的選項和文件名。 gcc 的整個編譯過程,實質上是分四步進行的,每一步完成一個特定的工作,

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

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

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

-o 選項表示要求編譯器生成指定文件名的可執行文件;

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

-g 選項要求編譯器在編譯的時候提供以后對程序進行調試的信息;

-E 選項表示編譯器對源文件只進行預處理就停止,而不做編譯,匯編和鏈接;

-S 選項表示編譯器只進行編譯,而不做匯編和鏈接;

-O 選項是編譯器對程序提供的編譯優化選項,在編譯的時候使用該選項,可以使生成的執行文件的執行效率提高;

-Wall 選項指定產生全部的警告信息。

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

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

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

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

$ gcc -c hello.c

然后重新鏈接生成:

$ gcc o hello.o

對于本例,因為只含有一個源文件,所以當改動了源碼后,進行重新的編譯鏈接的過程顯得并不是太繁瑣,但是,如果在一個工程中包含了若干的源碼文件,而這些源碼文件中的某個或某幾個又被其他源碼文件包含,那么,如果一個文件被改動,則包含它的那些源文件都要進行重新編譯鏈接,工作量是可想而知的。幸運的是, GNU 提供了使這個步驟變得簡單的工具,就是下面要介紹給大家的 GNU Make 工具。

GNU Make

make 是負責從項目的源代碼中生成最終可執行文件和其他非源代碼文件的工具。 make 命令本身可帶有四種參數:標志、宏定義、描述文件名和目標文件名。

其標準形式為:

make [flags] [macro definitions] [targets]

Unix 系統下標志位 flags 選項及其含義為:

-f file 指定 file 文件為描述文件,如果 file 參數為 '-' 符,那么描述文件指向標準輸入。如果沒有 '-f' 參數,則系統將默認當前目錄下名為 makefile 或者名為 Makefile 的文件為描述文件。在Linux 中, GNU make 工具在當前工作目錄中按照 GNUmakefile 、 makefile Makefile 的順序搜索makefile 文件。

-i 忽略命令執行返回的出錯信息。

-s 沉默模式,在執行之前不輸出相應的命令行信息。

-r 禁止使用隱含規則。

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

-t 更新目標文件。

-q make 操作將根據目標文件是否已經更新返回 "0" 或非 "0" 的狀態信息。

-p 輸出所有宏定義和目標文件描述。

-d Debug 模式,輸出有關文件和檢測時間的詳細信息。

Linux make 標志位的常用選項與 Unix 系統中稍有不同,下面只列出了不同部分:

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

-I dir 當包含其他 makefile 文件時,利用該選項指定搜索目錄。

-h help 文擋,顯示所有的 make 選項。

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

通過命令行參數中的 target ,可指定 make 要編譯的目標,并且允許同時定義編譯多個目標,操作時按照從左向右的順序依次編譯 target 選項中指定的目標文件。如果命令行中沒有指定目標,則系統默認 target 指向描述文件中第一個目標文件。

make 如何實現對源代碼的操作是通過一個被稱之為 makefile 的文件來完成的,在下面的小節里,主要向讀者介紹一下 makefile 的相關知識。

makefile 基本結構

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

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

$ make -f makefilename

一個 makefile 主要含有一系列的規則,如下:

目標文件名: 依賴文件名

(tab ) 命令

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

下面舉一個簡單的 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

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

有了 makefile ,對任何一個源文件進行修改后,所有依賴于該文件的目標文件都會被重新編譯 ( 因為 .o 文件依賴于 .c 文件 ) ,進而最終可執行文件會被重新鏈接 ( 因為它所依賴的 .o 文件被改變了 ) ,再也不用手工去一個個修改了。



劉暢 2009-12-05 00:45 發表評論
]]>
C++中的默認構造函數http://www.shnenglu.com/deercoder/articles/102113.html劉暢劉暢Fri, 27 Nov 2009 13:42:00 GMThttp://www.shnenglu.com/deercoder/articles/102113.htmlhttp://www.shnenglu.com/deercoder/comments/102113.htmlhttp://www.shnenglu.com/deercoder/articles/102113.html#Feedback0http://www.shnenglu.com/deercoder/comments/commentRss/102113.htmlhttp://www.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();
}
完全沒有問題,自己定義了一個構造函數,而不是系統默認的構造函數,這樣就不會出錯了!!
然后看看下面的這段代碼:
#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();
}
然后就會出錯,為什么,因為默認的構造函數是沒有形參,所以你傳遞進去的兩個形參就會報錯!更不可能把你的那兩個參數賦值給它的private內容中的形參。
還可以定義一個函數專門進行賦值化或者初始化,這樣就相當于一個構造函數,可是實現起來的話就要顯示的調用set函數。
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 發表評論
]]>
C++中的重載運算符http://www.shnenglu.com/deercoder/articles/102110.html劉暢劉暢Fri, 27 Nov 2009 13:38:00 GMThttp://www.shnenglu.com/deercoder/articles/102110.htmlhttp://www.shnenglu.com/deercoder/comments/102110.htmlhttp://www.shnenglu.com/deercoder/articles/102110.html#Feedback0http://www.shnenglu.com/deercoder/comments/commentRss/102110.htmlhttp://www.shnenglu.com/deercoder/services/trackbacks/102110.html1.為何要使用重載運算符?

因為我們希望使用到類的一些操作,使得一個類更像內置的數據類型(int,float…………)

比如說對于int類型的變量,自增運算符是默認的,即定義int i,即可以用到i++,而現在對于自定義

的一個類,如果沒有定義++操作,那么程序時不會找到這個操作的相應定義的,更不會執行相應的操

作!

也就是說,實際上重載運算符是給了++…………這些運算符一些新的定義和操作,結果很明顯,我們

使用自定義類的時候可以像內置類型一樣那么自然,簡單,符合我們的用戶習慣!

 

2.如何定義重載運算符?

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

現在我們的問題是要增加自己定義的類的操作。

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

 

運算符的重載形式有兩種:重載為類的成員函數和重載為類的友元函數。



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

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


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


劉暢 2009-11-27 21:21 發表評論
]]>
數據結構與STL(一)http://www.shnenglu.com/deercoder/articles/100748.html劉暢劉暢Wed, 11 Nov 2009 14:49:00 GMThttp://www.shnenglu.com/deercoder/articles/100748.htmlhttp://www.shnenglu.com/deercoder/comments/100748.htmlhttp://www.shnenglu.com/deercoder/articles/100748.html#Feedback0http://www.shnenglu.com/deercoder/comments/commentRss/100748.htmlhttp://www.shnenglu.com/deercoder/services/trackbacks/100748.html

總括:此部分寫的還是比較好,特別是第一章,介紹了面向對象編程的特別,關鍵是從一個解決問題的角度來分析,相比較而言,C++ Primer更顯得雜亂無章。通過對問題深入,根據解決問題的方法,來實現最合理化的得到目的,從而引出繼承等等一系列概念和手段。

 

1.     構造器

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

{

  Month = monthln;

  Day  =  dayln;

  Year  =  yearln;

}

Date thedate(7,1,2003);

Date thisdate;

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

注意:一個構造器,不管有沒有參數,都不會自動初始化類的字段,所以必須自己顯式的寫出代碼來初始化。

2.     繼承的細節

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

 





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

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

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

找到主體程序在哪里。

 

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

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

在定義中必須也要有,否則的話就會報錯,說不認識這個函數。
3.在類外定義的話一定要加上類型限定符,作用域限定符。

 下面寫點代碼:

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);  //改變狀態的一般都要定義為類成員,++,--及*
19    friend std::istream& operator>>(std::istream &a,Student& b);//進行算術運算和輸入輸出的就定義為友元
20    //Student& operator=(Student& a);
21private:
22    double score;
23    string name;
24    int number;
25}
;
26
27#endif

 

 1student.cpp文件,實現類函數
 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) //這里面又用到了<<這個運算符的。而這個你已經自定義了??!
10{
11    cout << a.name << "" << b.name << "分數之和為:" << 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<<"的分數:" << b.score << "\t 學號:" << 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,修改定義函數的時候沒有修改聲明,為什么這么粗心呢?
342.返回值,特別是對于有輸入輸出流的,要有返回值啊!
353.流的返回值和調用。
364.返回值怎么能為void呢?因為要輸出a+b,所以要返回一個值啊,相當于opreator+(a,b)
37*/

 

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

15

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

 此部分詳細關注中………………

3.關于重載運算符的返回值的問題:
我們是不是已經習慣于重載運算符有一個返回值類型呢?對,是的,我們經常這么干,現在的問題是,如果我定義為void的話,會出錯嗎?
不會的,將上面的換成void的返回值照樣不會出錯!
為什么呢?因為定義有返回值只是我們的一個習慣,為何有返回值,因為我們要返回值,很白癡吧?可是事實就是這樣,想想這個:
cout << a + b;  這個代碼很簡單是吧,a+b 實際上就是operator+(a,b)的一個等價形式,我要能夠cout ,那么必然有一個返回值,否則的話調用這個函數沒有返回值的話我怎么cout它呢?于是乎我們在定義加法的重載的時候就只能在前面加上a和b的類型了。
好,現在我沒有類型了,void,怎么辦?很簡單,直接用 a + b;  因為它相當于:operator+(a,b);也就是調用一個函數,直接調用,沒有返回值。
當然,結果也還是對的,各位可以看看…………

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

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

 

 

 

 

 

 

 

 

 

 



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

劉暢 2009-09-27 14:15 發表評論
]]>
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            久久国产视频网站| 亚洲视频在线二区| 久久香蕉国产线看观看av| 中日韩美女免费视频网址在线观看| 欧美成人一品| 亚洲婷婷综合色高清在线| 一区二区三区波多野结衣在线观看| 欧美日本中文| 欧美一区二区女人| 久久国产成人| 亚洲日本va午夜在线电影| 日韩午夜激情av| 国产精品一区三区| 欧美成人一区二区在线| 欧美日韩成人一区二区| 性欧美暴力猛交另类hd| 老司机亚洲精品| 亚洲午夜视频在线观看| 尤妮丝一区二区裸体视频| 亚洲日本电影在线| 亚洲一区二区三区色| 精品1区2区| 99精品视频免费观看视频| 国产亚洲视频在线| 亚洲精品久久在线| 国产精品白丝av嫩草影院| 久久亚洲高清| 欧美午夜欧美| 欧美国产日本高清在线| 国产精品亚洲成人| 亚洲国产精品va在看黑人| 国产九九视频一区二区三区| 欧美国产激情| 国产日韩欧美在线观看| 亚洲精品欧洲精品| 在线观看亚洲精品视频| 亚洲欧美日韩国产一区二区| 日韩一区二区福利| 久久久一本精品99久久精品66| 亚洲小少妇裸体bbw| 久久婷婷激情| 久久精品亚洲精品国产欧美kt∨| 欧美精品成人在线| 蜜臀av性久久久久蜜臀aⅴ| 国产精品免费网站| 亚洲精品极品| 亚洲国产一区二区三区在线播 | 91久久国产综合久久| 午夜免费日韩视频| 亚洲综合日韩| 欧美视频在线不卡| 亚洲全黄一级网站| 亚洲欧洲免费视频| 老司机午夜精品视频| 蜜桃av一区| 在线观看一区二区视频| 久久精品免费电影| 久久久久久有精品国产| 国产午夜精品一区二区三区欧美| 中文精品在线| 亚洲女人天堂av| 国产精品久久久久久久久久免费看 | 欧美大尺度在线| 欧美激情亚洲另类| 亚洲国产欧美不卡在线观看| 久久在线免费观看视频| 老司机午夜精品视频| 影音先锋在线一区| 免费观看亚洲视频大全| 亚洲成人在线网| 亚洲激情黄色| 欧美精品性视频| 一本一道久久综合狠狠老精东影业| 一区二区三区免费在线观看| 欧美日韩一区二区三区在线视频| 亚洲精品国产精品国自产在线| 亚洲国产三级| 欧美日韩ab| 亚洲免费网站| 免费欧美视频| 99精品国产一区二区青青牛奶| 亚洲欧美日韩在线| 欧美色图麻豆| 久久精品国产999大香线蕉| 校园春色国产精品| 激情文学综合丁香| 欧美激情一区| 亚洲在线不卡| 欧美成人按摩| 亚洲欧美日本国产有色| 国产欧美在线观看一区| 麻豆freexxxx性91精品| 亚洲日本在线观看| 香蕉久久一区二区不卡无毒影院| 国产综合欧美| 欧美日韩国产综合新一区| 亚洲欧美在线免费观看| 亚洲成色777777女色窝| 亚洲欧美精品| 亚洲黄色有码视频| 国产日韩精品一区观看| 欧美刺激性大交免费视频| 亚洲欧美日韩第一区| 亚洲福利在线视频| 久久国产免费看| 亚洲成人在线| 国产精品美女一区二区在线观看 | 欧美电影在线播放| 亚洲欧美视频在线观看视频| 亚洲黄一区二区| 国产日韩一区二区三区在线| 欧美激情第9页| 久久精品视频免费| 亚洲欧美清纯在线制服| 亚洲精品视频在线播放| 久热精品在线视频| 欧美在线看片| 亚洲伊人第一页| 91久久精品国产91性色| 国产综合婷婷| 国产精品一卡二卡| 欧美三区在线| 欧美激情一区二区三区在线| 久久久之久亚州精品露出| 性久久久久久久久久久久| 一区二区三区国产在线观看| 91久久精品一区二区别| 欧美成人免费视频| 老司机免费视频一区二区| 久久精品二区亚洲w码| 艳妇臀荡乳欲伦亚洲一区| 亚洲国产欧美一区二区三区久久| 欧美激情视频网站| 久久青草欧美一区二区三区| 欧美在线亚洲综合一区| 亚洲欧美中文另类| 亚洲一区二区动漫| 亚洲系列中文字幕| 亚洲伊人网站| 欧美一区二区播放| 欧美制服丝袜第一页| 欧美一级黄色录像| 久久精品成人| 久久亚洲综合色| 麻豆久久久9性大片| 久久久久91| 裸体歌舞表演一区二区| 欧美成人久久| 亚洲欧洲日本mm| 夜久久久久久| 亚洲欧美国产三级| 久久精品国产久精国产思思| 久久久久综合网| 免费成人性网站| 欧美日韩精品二区| 欧美午夜宅男影院| 国产农村妇女精品一二区| 亚洲欧洲一级| 亚洲图片激情小说| 亚洲美女福利视频网站| 一区二区三区精品视频| 午夜精品久久久久久久蜜桃app | 久久免费视频网| 亚洲电影激情视频网站| 99re8这里有精品热视频免费| 国产精品99久久久久久有的能看| 亚洲永久免费av| 久久久视频精品| 欧美精品一区二区在线观看 | 欧美乱人伦中文字幕在线| 欧美日韩综合| 一区在线播放| 久久久噜噜噜| 欧美天堂亚洲电影院在线观看 | 可以免费看不卡的av网站| 欧美日韩国产精品成人| 国产农村妇女精品| 亚洲精品久久久久久下一站| 亚洲一级免费视频| 免费在线成人| 亚洲性夜色噜噜噜7777| 免费成人高清| 国产欧美 在线欧美| 亚洲精品极品| 久久久久一本一区二区青青蜜月| 亚洲国产网站| 久久精品在这里| 国产精品九九| 亚洲老板91色精品久久| 欧美在线视频一区二区三区| 最新日韩av| 久久久久久久久久码影片| 国产精品国产三级国产a| 亚洲经典三级| 另类国产ts人妖高潮视频| 亚洲一区不卡| 欧美体内she精视频在线观看| 亚洲国产人成综合网站| 久久影院亚洲| 先锋影音一区二区三区|