C++輸入輸出基礎(chǔ)、函數(shù)重載、引用、字符串基礎(chǔ)、對齊要求
// windows中c++源文件后綴為.cpp,編譯源文件后產(chǎn)生目標(biāo)文件,后綴為.o,鏈接器將 目標(biāo)代碼文件 和 庫代碼(library code)、啟動(dòng)代碼(startup code) 鏈接/合并,生成可執(zhí)行文件.exe,unix中可執(zhí)行文件為.out
#include <iostream> //提供c++輸入輸出流函數(shù)
#include <iomanip> //提供c++io格式控制,manipulate操作操縱
using namespace std; //使c++標(biāo)準(zhǔn)庫內(nèi)容可直接訪問(namespace名稱空間,后續(xù)詳細(xì)介紹)
int main1() //c++中函數(shù)形參列表不填相當(dāng)于(void)
{
???? cout << "輸入整數(shù):"; //cout對象的作用類似c的printf,輸出內(nèi)容,插入運(yùn)算符<<將右側(cè)內(nèi)容插入左側(cè)cout(輸出流),輸出流從程序流出
???? int a;
???? cin >> a; //cin的作用類似c的scanf,抽取運(yùn)算符>>從左側(cè)cin(輸入流)抽取值賦給變量a,根據(jù)a的類型進(jìn)行解釋,輸入流流入程序
???? cout << "a = " << a << endl; //<<運(yùn)算符從左往右運(yùn)算,cout<<"a = "將字符串插入標(biāo)準(zhǔn)輸出流,cout<<...返回cout本身,繼續(xù)執(zhí)行cout<<a,默認(rèn)根據(jù)變量a的類型轉(zhuǎn)換字符串,后再次返回cout本身,繼續(xù)cout<<endl,即endline換行,endl使輸出換行并刷新緩沖/將字符串發(fā)送出程序
???? int b = 5;
???? (a = b) = 10; //c中a=b表達(dá)式的值為a的值(備份)5,表達(dá)式5=10報(bào)錯(cuò),但在c++中a=b表達(dá)式的值為a本身,即先將b的值賦給a,返回a本身,繼續(xù)執(zhí)行a=10,最終a為10,b為5
???? cout << "輸入2個(gè)整數(shù):";
???? cin >> a >> b; //cin先流入a再流入b,類似scanf("%d%d",a,b),兩個(gè)數(shù)字之間通過空白符號隔開
???? double d = 1.234567;
???? cout << d << endl; //相當(dāng)于%g
???? cout << d << setprecision(5) << endl; //相當(dāng)于%5g,整數(shù)加小數(shù)總共5位有效數(shù)字
???? cout << setprecision(5) << setiosflags(ios::fixed) << d << endl; //相當(dāng)于%.5f
???? cout << setw(10) << b << endl; //相當(dāng)于%10d
???? cout << setw(10) << setfill('x') << b << endl; //用x填充空位
???? cout << setw(10) << setiosflags(ios::left) << b << endl; //左對齊相當(dāng)于%-10d,setw只對單次打印生效,setfill和setiosflags為全局設(shè)置
???? cout << hex << a << oct << a << dec << a << endl; //hex相當(dāng)于%x,oct相當(dāng)于%o,dec相當(dāng)于%d
???? cout << setbase(16) << a << endl; //setbase設(shè)置進(jìn)制基數(shù),設(shè)為16等價(jià)于hex
???? char ch;
???? cin >> ch; //讀取會(huì)跳過空白符號,相當(dāng)于scanf(" %c",&ch)
???? string s1 = "c++使用string對象取代char[]數(shù)組表示字符串";
???? cout << s1;
???? return 0;
}
int Add(int a, int b)
{
????return a + b;
}
int Add(int a, int b, int c)
{
???? return a + b + c;
???? //c++函數(shù)重載,函數(shù)名相同,形參列表不同,構(gòu)成重載,在編譯階段會(huì)發(fā)生傾軋,將每種add函數(shù)更換為不同的名稱
???? //形參列表不同是重載的充要條件,與返回值無關(guān)(因?yàn)槌绦驘o法得知用戶想要哪種返回值的函數(shù)),形參的數(shù)量不同、類型不同,類型順序不同都可以構(gòu)成重載,形參的標(biāo)識符不同不構(gòu)成重載
}
float Add(float a, float b)
{
????return a + b;
}
int main2()
{
???? cout << Add(1, 3) << endl; //程序根據(jù)1和3的類型(int)匹配add(int,int)函數(shù)
???? //cout << Add(1.0, 3.0) << endl; //1.0和3.0都為double類型,沒有add(double,double)函數(shù)的時(shí)候會(huì)進(jìn)行隱式類型轉(zhuǎn)換,將double截?cái)酁閕nt或者轉(zhuǎn)換為float,但因?yàn)橥瑫r(shí)存在add(int,int)和add(float,float),程序不知道轉(zhuǎn)哪個(gè)而報(bào)錯(cuò)
???? //同理也有int轉(zhuǎn)long或double的二義性報(bào)錯(cuò)
???? return 0;
}
extern "C" int Minus(int a, int b); //在c中沒有c++的傾軋,所有c函數(shù)需要包在extern "C" 語句中避免傾軋
extern "C"
{ //復(fù)合語句用大括號
???? int Minus(int a, int b) //函數(shù)聲明和函數(shù)定義要都傾軋或都不傾軋(保持一致)
???? {
???? ????return a - b;
???? }
}
struct xy { int x; int y; };
int main3()
{
???? xy s1 = { 4,3 }; //c++中聲明結(jié)構(gòu)變量不需要寫struct,結(jié)構(gòu)別名和變量共享名稱空間
???? xy s2 = { 2,3 };
???? xy s3 = { s1.x - s2.x,s1.y - s2.y }; //錯(cuò)誤s3=s1-s2 結(jié)構(gòu)不能直接相減?
???? return 0;
}
xy operator-(xy s1, xy s2) //運(yùn)算符重載
{
???? xy s3 = { s1.x - s2.x,s1.y - s2.y };
???? return s3;
}
int main4()
{
???? xy s1 = { 4,3 };
???? xy s2 = { 2,3 };
???? xy s3 = s1 - s2; //減號實(shí)際調(diào)用operator-(s1,s2)
???? return 0;
}
int Times(int a, int b = 5) //默認(rèn)參數(shù)b
{
????return a * b;
}
int main5()
{
???? cout << Times(2) << endl << Times(2, 3) << endl; //只傳一個(gè)參數(shù)時(shí)參數(shù)2使用默認(rèn)值
???? return 0;
}
//int Times(int a = 5, int b); 錯(cuò)誤,默認(rèn)參數(shù)必須全部寫在右側(cè)
//int Times(int a); 錯(cuò)誤,重載和默認(rèn)參數(shù)沖突,當(dāng)Times(a)一個(gè)參數(shù)時(shí)程序不知道使用一個(gè)參數(shù)的函數(shù)還是默認(rèn)參數(shù)的函數(shù)
int Times(int a, int b, int c = 5); //如果函數(shù)聲明和函數(shù)定義分開寫時(shí),默認(rèn)參數(shù)寫在函數(shù)聲明中,函數(shù)定義不寫,如頭文件中函數(shù)原型要寫默認(rèn)參數(shù)
int Times(int a, int b, int c) //Times(a,b,c=5)和Times(a,b=5)構(gòu)成重載,但是當(dāng)調(diào)用Times(a,b)時(shí)又出現(xiàn)二義性/沖突,所以如果使用默認(rèn)參數(shù),就只聲明一次函數(shù),如果使用重載,最好完全不使用默認(rèn)參數(shù)
{
????return a * b * c;
}
int main6()
{
???? int a = 5;
???? int& ra = a; //引用reference,ra被聲明為a的別名,只存在于編譯階段之前,經(jīng)過編譯后所有ra都是a,引用沒有創(chuàng)建空間,對ra的使用和對a的使用完全一樣
???? ra = 50; //等價(jià)于a=50
???? int b = 2;
???? ra = b; //等價(jià)于a=b,這個(gè)語句并不會(huì)讓ra轉(zhuǎn)為引用b,引用不可更改
???? //int& rb; 錯(cuò)誤,引用在聲明時(shí)必須初始化,所以引用必須有引用對象且不能更改
???? int& rra = ra; //等價(jià)于 int&rra = a;
???? //int& (&rra) = a; 錯(cuò)誤,引用本身并不存在(只存在于編譯之前),不能聲明引用的引用
???? //int& arr[5]; 錯(cuò)誤,同理,引用不占空間,不能作為數(shù)組的元素
???? int arr[5];
???? int(&rarr)[5] = arr; //數(shù)組的引用,需要保持類型、元素?cái)?shù)量一致,元素?cái)?shù)量不能省略,將 "int 變量名[size]" 的"變量名"改為 "(&引用名)" 即可聲明引用,rarr先和&結(jié)合成為引用再和int[5]結(jié)合成為數(shù)組的引用
???? int* ptr;
???? int*& rptr = ptr; //指針的引用,同理,先寫 int* 變量名,再改寫成int*(&引用名),括號省略
???? //int&* p; 錯(cuò)誤,同樣因?yàn)橐貌徽伎臻g,沒法用指針去指向引用
???? return 0;
}
int Divide(int& ra, int& rb) //形參使用引用,不創(chuàng)建空間
{
????return rb ? ra / rb : 0;
}
int main7()
{
???? int i1 = 3, i2 = 2;
???? cout << Divide(i1, i2) << endl; //函數(shù)調(diào)用時(shí)int&ra=i1,ra即是i1本身,不同于函數(shù)調(diào)用將實(shí)參的值(備份)傳給形參
???? //cout << Divide(5, 3) 錯(cuò)誤,int&ra無法引用常量
???? const double& r1 = i1; //聲明常量引用,實(shí)際內(nèi)部隱式聲明了一個(gè)double變量賦值(double)i1,再聲明引用r1,所以r1依然不占空間,但內(nèi)部創(chuàng)建了一個(gè)變量
???? const double& r2 = 5; //同樣隱式聲明了變量,值為5.0
???? const double& r3 = i1 + 5; //隱式聲明了(double)(i1+5)
???? //在某些實(shí)現(xiàn)中,引用通過 type* const 變量名 來實(shí)現(xiàn),即實(shí)際引用聲明了一個(gè)不可修改指向的指針,通過該指針可以修改指向的值,涉及引用的函數(shù)調(diào)用也會(huì)經(jīng)過編譯后變成主調(diào)函數(shù)傳遞變量地址,被調(diào)函數(shù)通過指針接收,以此來減少用戶主動(dòng)使用指針的情況
???? const int i3 = 4; //c++中const聲明必須初始化
???? //錯(cuò)誤 int* p = &i3; //c++中不能對const聲明的常量進(jìn)行任何直接、間接修改
???? int i4 = 4;
???? const int* p = &i4; //const常量p不可修改,但不影響i4
???? p = &i3; //和c一樣可以更改p的指向
???? return 0;
}
void mainreference2()
{
???? int a = 10;
???? //int& ref = a; int* const ptr = &a;引用的實(shí)質(zhì)是創(chuàng)建一個(gè)const指針(不能改變指向),通過*ptr來表示變量本身
???? const int& ra = a;
???? cout << "&a = " << &a << endl;
???? cout << "&ra = " << &ra << endl; //const引用并非任何時(shí)候都生成一個(gè)臨時(shí)變量
???? const int& ra2 = a + 2; //當(dāng)引用對象不是左值時(shí),const引用會(huì)生成一個(gè)臨時(shí)變量
???? cout << "&ra2 = " << &ra2 << endl;
???? const int& ra3 = ra2; //ra2是不可修改的左值
???? cout << "&ra3 = " << &ra3 << endl; //ra3和ra2地址相同,因?yàn)轭愋拖嗤?,且ra2是左值(無論可否修改),所以引用對象是左值就不會(huì)生成臨時(shí)變量,地址相同
???? double d = 1.0;
???? const int& rd = d; //當(dāng)引用對象的類型和引用類型不同時(shí),如果能夠類型轉(zhuǎn)換則不報(bào)錯(cuò)、const引用會(huì)生成一個(gè)臨時(shí)變量,這里rd為臨時(shí)變量(int)d的引用
???? cout << "&d = " << &d << endl;
???? cout << "&rd = " << &rd << endl;
}
const int& reference1(int& ri)
{
???? //函數(shù)的引用傳參和返回引用同聲明引用的用法,形參ri為實(shí)參變量的引用而非實(shí)參值,即int& ri = 主調(diào)函數(shù)變量
???? return ri; //返回的雖然是const引用,但引用與變量類型相同并且變量是左值,所以const返回依然是變量本身,只是附加了只讀的限制,實(shí)際通過類似const int*來實(shí)現(xiàn)
}
#include <string> //提供string類相關(guān)的方法
int mainother1()
{
???? auto cars = 15; //c++中auto關(guān)鍵字用法不同于c,c++中用auto代替類型名typename,使聲明的變量初始化為右值的類型和值,15是int常量,所以auto cars變?yōu)閕nt類型,值為15
???? auto iou = 150.37f; //iou為float類型
???? auto level = 'B'; //c++中字符常量為char類型,所以level為char,值為66
???? auto crat = U'\U00002155'; //char32_t類型,c++規(guī)定char32_t至少為32位,用來表示單字符32位的字符集,使用U'\U表示,后面跟8位16進(jìn)制數(shù)。而char16_t類型用于表示單字符16位的字符集,用u'\u小寫u表示Unicode字符
???? auto fract = 8.25f / 2.5; //8.25f為float,2.5為double,將float轉(zhuǎn)換為double統(tǒng)一類型進(jìn)行運(yùn)算,fract為double類型
???? auto sss = short(20) + short(2); //整型提升,長度小于int的類型會(huì)在運(yùn)算時(shí)被自動(dòng)轉(zhuǎn)換為int或unsigned int,c++規(guī)定int為系統(tǒng)運(yùn)算最快的整數(shù)類型,提升不會(huì)造成精度損失,所以兩個(gè)short先轉(zhuǎn)換為int運(yùn)算結(jié)果為int,auto根據(jù)結(jié)果的類型聲明變量的類型,所以變量類型為int而非short
???? int a1 = 10;
???? int a2 = 010; //八進(jìn)制
???? int a3 = 0x10; //十六進(jìn)制
???? double d1 = 2345.6;
???? double d2 = 2.3456e3; //e3乘以10的三次方/小數(shù)點(diǎn)右移三位
???? char ch = 65;
???? cout << ch << endl; //cout輸出char類型時(shí)解釋為字符而非編碼,結(jié)果為A
???? enum MyEnum //枚舉,定義符號常量
???? {
???? ???? one = 1,only=1,two //符號常量的值可重復(fù),不顯式賦值則比前一個(gè)符號常量大1
???? };
???? cout.put(97); //作用類似putchar()
???? ch = cin.get(); //作用類似getchar()
???? if (EOF == ch)
???? ???? cout << ".get()會(huì)返回eof,就像getchar一樣,輸入流讀到eof會(huì)設(shè)置eof位禁止繼續(xù)讀取,可通過cin.clear()重置流狀態(tài)恢復(fù)輸入";
???? cin.get(ch); //.get(char& )將下一個(gè)讀取的字符賦給主調(diào)函數(shù)的變量,讀到eof不會(huì)賦值,會(huì)設(shè)置eof位禁止繼續(xù)讀取,可通過cin.clear()重置流狀態(tài)恢復(fù)輸入
???? cin >> ch; //cin的>>抽取運(yùn)算符會(huì)跳過空白符號,所以cin>>ch會(huì)將空白符號以外的第一個(gè)字符賦值給ch
???? char temp[80];
???? cin.getline(temp, 80); //從輸入流中讀取一行,存儲(chǔ)到參數(shù)1char*的地址上,參數(shù)2限制最多讀取的數(shù)量-1個(gè)字符,這里為最多讀取79個(gè)字符,保留最后一位給\0,如果讀取到換行符會(huì)使用\0替代換行符結(jié)束讀取,如果讀取80個(gè)字符依然沒有讀取到換行符則會(huì)設(shè)置失效位(failbit)阻斷接下來的輸入,再次調(diào)用getline等任何讀取的函數(shù)都不再進(jìn)行讀取/掛起,可通過cin.clear()重置流狀態(tài)恢復(fù)輸入
???? cin.get(temp, 80); //作用類似getline,但會(huì)將換行符保留在輸入中。當(dāng)讀取到空行時(shí)會(huì)設(shè)置失效位
???? if (cin)
???? ???? cout << "任何讀取產(chǎn)生的錯(cuò)誤,通過設(shè)置失效位都會(huì)導(dǎo)致cin不能進(jìn)行讀取,這時(shí)cin在關(guān)系表達(dá)式中會(huì)轉(zhuǎn)換為false,而.getline()等返回cin的函數(shù)可直接放在if、while測試條件中通過cin轉(zhuǎn)換的bool值判斷是否讀取成功";
???? else
???? ???? cin.clear(); //cin轉(zhuǎn)換為true表示讀取正常,轉(zhuǎn)換為false時(shí)需要.clear()重置狀態(tài)才能繼續(xù)讀取
???? cin.get(temp, 80).get(); //如果一行長度小于80,則這句和getline()效果相同,cin.get(temp, 80)返回cin,繼續(xù)調(diào)用.get()讀取換行符
???? string strobj; //定義字符串對象
???? getline(cin, strobj); //全局函數(shù)getline,參數(shù)1傳cin,參數(shù)2傳string對象,將讀取一行輸入存儲(chǔ)到string對象中
???? cin >> strobj; //讀取一個(gè)單詞保存到strobj中
???? "字符串";
???? L"寬字符字符串";
???? u"char16_t或unicode字符串";
???? U"char32_t字符串";
???? R"(生字符串,不使用轉(zhuǎn)義字符,如\n表示為\和n兩個(gè)字符)"; //生字符串以R開頭,后跟"(? 內(nèi)容? )",即用 R"( 標(biāo)識生字符串的開始,用 )" 標(biāo)識字符串的結(jié)束,如果需要在生字符串內(nèi)部使用)"連用的情況,可在開頭"和(之間添加一些字符,在結(jié)尾的)和"之間添加對應(yīng)的字符
???? R"ab(這個(gè)生字符串就以R"ab(開頭,結(jié)尾添加對應(yīng)的字符順序相同而非對稱)ab";
???? //c++標(biāo)準(zhǔn)允許main()函數(shù)不寫返回值,不寫等同于return 0
???? if (10 == a1)
???????? cout << "value==variable ,將常量放在左側(cè),這樣如果少寫等號變?yōu)橘x值語句時(shí)會(huì)報(bào)錯(cuò)提示";
???? //因?yàn)槌槿∵\(yùn)算符>>跳過空白符號,所以如果程序的所有輸入都使用抽取運(yùn)算符,即可免去處理換行符的麻煩,每次使用>>和.get().getline()等交替時(shí)都需要處理換行符
???? int i1;
???? while (cin >> i1)
???? ???? cout << "成功抽取到int類型值:" << i1 << endl;
???? cout << "抽取遇到非數(shù)字字符,cin判斷為false退出循環(huán),在調(diào)用cin.clear()之前無法再通過.get() >> 等方法讀取字符" << endl;
???? ????//cin>>抽取失敗時(shí)不會(huì)改變變量的值,且cin在條件判斷中轉(zhuǎn)換為false,需要.clear()清除錯(cuò)誤狀態(tài)才能繼續(xù)讀取
???? return 0;
}
void mainforeach()
{
???? int arr[] = { 1,2,3,4,5,6,7 };
???? for (int i : arr) //foreach
???? {
???????? i++; //i僅為迭代目標(biāo)中元素的備份,i改變不會(huì)影響元素的值
???? }
???? for (int& i : arr) //獲得每個(gè)元素的引用
???? {
???????? cout << i++ << endl; //引用遞增即元素本身遞增
???? }
???? for (auto& i : { 3,6,8,1,2 }) //復(fù)合字面量,auto自動(dòng)匹配類型,i會(huì)逐一引用
???? {
???????? cout << i << endl;//i作為字面量/常量元素的引用(const int&),不可修改
???? }
}
void mainalignof()
{
???? cout << "alignof(char)" << alignof(char) << endl; //alignof(type)返回類型的對齊要求,char的對齊要求為1
???? cout << "alignof(int)" << alignof(int) << endl; //int的對齊要求為4,這不僅要求int變量的位置必須為4的倍數(shù),也要求變量的大小必須為4的倍數(shù)
???? cout << "alignof(double)" << alignof(double) << endl; //double的對齊要求為8,即double類型必須放置在8的倍數(shù)位置,且double類型大小必須占用8的倍數(shù)的空間
???? struct MyStruct
???? {
???????? char c; //1字節(jié)
???????? double d; //8字節(jié)
???????? int i; //4字節(jié)
???? };
???? cout << "alignof(MyStruct)" << alignof(MyStruct) << endl; //結(jié)構(gòu)的對齊要求為8,即位置必須為8的倍數(shù)且必須占用8的倍數(shù)的空間
???? cout << "sizeof(MyStruct)" << sizeof(MyStruct) << endl; //這個(gè)結(jié)構(gòu)的大小為24,遠(yuǎn)大于1+4+8,分析:首先結(jié)構(gòu)必須在8的倍數(shù)的位,所以第一個(gè)成員char在8n位占用一個(gè)字節(jié)(對齊要求為1),下一個(gè)成員double對齊要求8所以不能放在8n+1位,后移至8n+8位放置,前七個(gè)字節(jié)空出,double占用了8個(gè)字節(jié),下一個(gè)成員int根據(jù)對齊要求可以放在8n+16位置,占用4個(gè)字節(jié),但結(jié)構(gòu)的對齊要求為8,所以需要再占4字節(jié)空位湊夠8的倍數(shù),所以總共占用了3*8字節(jié)
}