C++ primer plus書之--C++函式和C語言字串, 結構體, string
函式和C風格字串
要將C風格字串作為引數傳遞給函式, 表示字串的方式有三種:
1.char陣列
2.用""擴起來的字串常量
3.被設定為字串地址的char指標
來看一個例子:
// c風格字串例子 #include "iostream" using namespace std; unsigned int c_in_str(const char* str, char ch); int main() { // char型陣列表示字串 char str1[10] = "qwertqwqt"; // char型指標 表示字串 char* p_str = "asdfasa"; unsigned int n_str1 = c_in_str(str1, 'q'); unsigned int n_p_str = c_in_str(p_str, 'a'); cout << str1 << " has " << n_str1 << " q " << endl; cout << p_str << " has " << n_p_str << " a " << endl; return 0; } unsigned int c_in_str(const char* str, char ch) { unsigned int count = 0; // 判斷是不是\0 空字元 while(*str) { if (*str == ch) count++; str += 1; } return count; }
程式執行結果:
返回c風格的字串的函式
c語言中沒有字串, 但是我們可以返回一個char型指標, 該指標指向字串的首地址即可, 看一個demo:
// c風格字串例子 #include "iostream" using namespace std; char* getstr(char ch, int n); int main() { int times; char ch; cout << "Enter a character: "; cin >> ch; cout << "Enter a num : "; cin >> times; char* ps1 = getstr(ch, times); cout << ps1 << endl; // 用完記得回收 delete ps1; ps1 = getstr('*', 10); cout << ps1 << endl; delete [] ps1; return 0; } char* getstr(char ch, int n) { // 要考慮\0字元 char* p_ch = new char[n + 1]; while (n-- > 0) { p_ch[n] = ch; } return p_ch; }
程式執行結果:
注意, 變數p_ch的作用域是在函式getstr函式內部, 當函式執行完畢後, p_ch(而不是字串)使用的記憶體將被釋放, 但由於函式返回了p_ch的值, 因此程式仍可以通過main()裡的指標ps1來訪問新建的字串, mian中當new建立的字串不再需要時通過delete釋放該字串佔用的記憶體.
函式和結構體
雖然結構變數和陣列一樣可以儲存多個數據項, 單在涉及到函式時, 結構變數的行為更接近於基本單值變數, 也就是說與陣列不同, 結構將其資料組成單個實體或資料物件, 該實體被視為一個整體. 按值傳遞結構, 就像普通變數那樣, 函式將使用原始結構的副本. 另外函式也可以返回結構. 與陣列名就是陣列的第一個元素的地址不同的是, 結構名只是結構的名稱, 要獲得結構的地址, 必須使用地址運算子&.
傳遞和返回結構
當結構比較小時, 按值傳遞結構最合理.
看個簡單的結構體做函式引數的demo:
// 函式和結構體
#include "iostream"
using namespace std;
struct travel_time
{
int hours;
int mins;
};
const int Mins_per_hour = 60;
travel_time sum(travel_time t1, travel_time t2);
void show(travel_time t);
int main() {
// 定義並初始化一個結構體變數
travel_time d1 = {2, 38};
travel_time d2 = {3, 46};
travel_time sum1 = sum(d1, d2);
// 列印的是sum1的首地址可以和sum方法中的地址進行比較, 可以看出來兩個地址不是同一個
// 也就是這種傳遞結構體的方式, 傳遞的是副本而不是地址
cout << "address of sum1 in main fun : " << &sum1 << endl;
cout << "two-day total : ";
show(sum1);
travel_time d3 = {6, 58};
cout << "Three-day total:";
show(sum(sum1, d3));
return 0;
}
travel_time sum(travel_time t1, travel_time t2)
{
travel_time total;
total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hour;
total.mins = (t1.mins + t2.mins) % Mins_per_hour;
// 列印total的首地址
cout << "address of total in sum fun : " << &total << endl;
return total;
}
void show(travel_time t)
{
cout << "hours : " << t.hours << ", mins : " << t.mins << endl;
}
執行結果為:
注意看程式碼中的註釋, 理解到底是傳遞的地址還是傳遞的是值的副本
再看一個簡單的demo, 就是座標系的轉換, 一種是記錄點到圓點的距離以及和x軸的夾角, 一種是記錄點在座標系中的x, y值:
// 函式和結構體
#include "iostream"
#include "cmath"
using namespace std;
struct polar
{
double distance;
double angle;
};
struct rect
{
double x;
double y;
};
// 函式原型
// 按值傳遞, 也就是函式內使用的是結構體的副本
polar rect_to_polar(rect xypos);
void show(polar polar);
int main() {
// 定義兩個結構體變數
rect rplace;
polar pplace;
cout << "Enter the x and y value : ";
// 藉助cin來判斷使用者輸入的是否合法, 因為x,y是double型別, 所以只有輸入double型別的時候才是合法的
while (cin >> rplace.x >> rplace.y)
{
pplace = rect_to_polar(rplace);
show(pplace);
cout << "Next x, y nums, q to quit : ";
}
cout << "******End********" << endl;
return 0;
}
polar rect_to_polar(rect xypos)
{
polar answer;
// 開方
answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);
// 計算角度
answer.angle = atan2(xypos.y, xypos.x);
return answer;
}
void show(polar dapos)
{
const double Rad_to_deg = 57.296;
cout << "distance = " << dapos.distance;
cout << ", angle = " << dapos.angle * Rad_to_deg;
cout << " degrees" << endl;
}
看執行結果:
程式碼沒有什麼特殊的地方是為了, 和傳遞結構體地址的函式做對比的程式碼, 只需要注意while迴圈判斷中的判斷方法即可
傳遞結構體的地址
假設要傳遞結構的地址而不是整個結構以節省時間和空間, 應該使用指向結構的指標, 需要修改以上程式碼的三個地方:
1.呼叫函式時, 將結構的地址(&pplace)而不是結構本身(pplace)傳遞給函式
2.將形參生命為指向polar的指標(polar *)型別, 如果函式內不需要修改結構體, 可以宣告成 const polar * 型別
3.由於形參是指標而不是結構, 因此應該使用間接成員運算子(->), 而不是成員運算子(.)
以下是修改之後的程式:
// 函式和結構體
#include "iostream"
#include "cmath"
using namespace std;
struct polar
{
double distance;
double angle;
};
struct rect
{
double x;
double y;
};
// 函式原型
// 宣告為接收結構體的地址, 這樣就不再是值傳遞了
polar rect_to_polar(rect* xypos);
// 需要用指標來接受結構體的地址
void show(const polar* polar);
void changeValue(polar* polar);
int main() {
// 定義兩個結構體變數
rect rplace;
polar pplace;
cout << "Enter the x and y value : ";
// 藉助cin來判斷使用者輸入的是否合法, 因為x,y是double型別, 所以只有輸入double型別的時候才是合法的
while (cin >> rplace.x >> rplace.y)
{
// 這裡要使用&傳遞地址
pplace = rect_to_polar(&rplace);
show(&pplace);
cout << "Next x, y nums, q to quit : ";
}
cout << "******End********" << endl;
cout << "測試是否是傳遞的地址" << endl;
cout << "before change : " << endl;
show(&pplace);
// 這裡將pplace的內容做了修改, 看是否傳遞的是地址, 也就是在changeValue裡修改, 在函式外是否也有效
changeValue(&pplace);
cout << "after change : " << endl;
show(&pplace);
return 0;
}
polar rect_to_polar(rect* xypos)
{
polar answer;
// 開方
// 注意這裡xypos已經是指標了, 所以不能使用.呼叫屬性, 而應該使用->
answer.distance = sqrt(xypos->x * xypos->x + xypos->y * xypos->y);
// 計算角度
answer.angle = atan2(xypos->y, xypos->x);
return answer;
}
void show(const polar* dapos)
{
const double Rad_to_deg = 57.296;
cout << "distance = " << dapos->distance;
cout << ", angle = " << dapos->angle * Rad_to_deg;
cout << " degrees" << endl;
}
void changeValue(polar* polar)
{
// 將資料都設定成10
polar->distance = 10;
polar->angle = 10;
}
看執行結果:
需要注意的地方都在註釋裡寫清楚了.
函式和string
雖然c風格字串和string物件的用途幾乎相同, 但與陣列相比, 寫法上string物件與結構題更為相似
// 函式和結構體
#include "iostream"
#include "string"
using namespace std;
const int Size = 3;
// 函式原型
void show(string sarr[], int n);
int main() {
// 聲明瞭一個字串陣列
string list[Size];
cout << "Enter your " << Size << " favorite num : ";
for (int i = 0; i < Size; i++)
{
cout << i + 1 << " : ";
getline(cin, list[i]);
}
cout << "Your list : " << endl;
show(list, Size);
// 重新列印list看看資料是否發生了改變
cout << "list[0]" << " : " << list[0] << endl;
cout << "*(list + 1)" << " : " << *(list + 1) << endl;
return 0;
}
void show(string sarr[], int n)
{
for(int i = 0; i < n ; i++)
{
// 兩種輸出方法均可
cout << i + 1 << " : " << sarr[i] << endl;
cout << i + 1 << " : " << *(sarr + i) << endl;
}
// 這裡做一個修改, 看看傳遞的是字串陣列的副本還是傳遞的是地址
sarr[0] = -1;
*(sarr + 1) = -2;
}
看一下輸出結果:
這裡傳遞的是string陣列, 所以陣列名仍然是指標, 也就是如果在方法內對資料進行了修改, 在外面的string陣列也響應的發生了改變