1. 程式人生 > 實用技巧 >《C++ Primer》筆記(04)

《C++ Primer》筆記(04)

筆記

第 4 章 表示式

基礎

左值右值

  • 右值:物件的值(內容);左值:物件的身份(位置)。
  • decltype關鍵字作用於結果為左值的表示式(不是變數),得引用型別;右值得指標型別。

求值順序

  • 除邏輯與&&邏輯或||條件?:逗號,運算子外,都沒有規定物件的求值順序,故不能在表示式中改變同一物件的狀態如cout << i << " " << ++i;,無法預知先計算i還是++i

算術運算子

  • 正負號 > 乘除、求餘 > 加減法。都滿足左結合。
  • 布林值納入計算時真為1,假為0計算;反過來時0為假,非0為真。
  • 整數的計算結果仍為整數,所有小數(無論正負)都直接捨棄(C++11新標準,向0靠攏)。
  • 取餘運算當-m未溢位時,(-m)/n = m/(-n) = -(m/n); m%(-n) = m%n; (-m)%n = -(m%n);

邏輯和關係運算符

  • 短路求值:邏輯與&&和邏輯或||都先計算左側,該特性可以避免下標越界等問題(將和大小的比較放在左邊)。

賦值運算子

  • 右側運算物件會被轉換為左側運算物件的型別,但用花括號的初始值列表不允許窄化且左側為內建型別時只能包含一個值。int k; k = {3.14}; // 錯誤,窄化轉換
  • 滿足右結合律,a = b = c = 1;則三個變數都為1,要求三個變數可以按結合律進行型別轉換(不論最右側的值單獨賦值的時候是否合法)。
  • 賦值運算子優先順序低,賦值語句作為條件時應括號框起來。
  • 複合賦值運算子對左側運算物件只求一次值(後者),普通賦值運算子求兩次,一次作為右側子表示式的一部分求值,另一次作為賦值運算左側物件。

遞增和遞減運算子

  • 前置版返回遞增後的值,後置版返回遞增前的值。一般而言使用前置版本,進行普通的運算。
  • 後置版本用於在一條語句中同時實現變數的遞增和對其原值的使用(即既需要遞增又需要使用原值)*pbeg++
  • 未規定求值順序,故不能有i = i++;

成員訪問運算子

  • ptr->mem等價於(*ptr).mem,箭頭運算子作用於指標型別,點運算子作用於物件。

條件運算子

  • 允許巢狀;由於條件運算子優先順序低,使用時要帶上括號(包括整體的括號)。
  • 條件運算子滿足右結合,a?b:c?d:e
    等價於a?b:(c?d:e),而非(a?b:c)?d:e
  • 條件運算子規定了求值順序為a->bc,取決於a的結果。

參考部落格

位運算子

  • 小整型進行運算時會被提升為大整型。
  • 位操作可能影響符號位,其影響和環境有關,故僅將位運算子用於無符號型別。
  • 一位置1用位或,將對應位為1其他位為0的數進行位或運算;同理,一位置0用位與;判斷位用位與。
  • 移位運算子滿足左結合,其優先順序低於算數,高於關係、賦值和條件,故後者要加上括號。

sizeof運算子

  • sizeof *p不會真正解引用,即使p是空指標也能找到其所指物件的型別。
  • 可以使用作用域運算子獲得類成員的大小,即使沒有例項。sizeof Sales_data::revenue;
  • sizeof 陣列會計算整個陣列佔空間大小,而非轉換為指標,sizeof(ia)/sizeof(*ia)得到陣列大小,該式可以用constexpr關鍵詞修飾。

逗號運算子

  • 常用於for迴圈中多個值遞增/遞減,求值從左向右。

型別轉換

隱式轉換

  • 小整型提升,小於int提升為int,大於的提升為可容納的最小型別。
  • 條件中非布林轉換為布林。
  • 初始化和賦值,轉換為左側型別。
  • 算術運算,如果包含無符號和有符號,且無符號不小於有符號,則有符號轉為無符號(會產生符號轉換問題,導致受限於環境的結果);無符號小於有符號則受限於環境進行轉換(如果可容納則轉換為有符號,不可容納則轉換為無符號)。
  • 函式呼叫。
  • 特殊型別:
    • 陣列轉換為指標,在decltype關鍵字引數,取地址符、sizeoftypeid運算物件,使用引用初始化陣列時不會發生。
    • 常量整數值0或字面值nullptr轉換為指標,指向任意非常量的指標轉換為void*,指向任意物件的指標轉換為const void*
    • 指標轉換為布林型別,0false,否則為true
    • 指向非常量的指標轉換為指向相應常量的指標,反之不存在。
    • 類型別定義的轉換,如C風格字串和string型別

顯示轉換

  • 不包含底層const的都可以用static_cast,也可用於找回存在於void*指標中的值。
  • const_cast只能用於改變底層const,可以解除const屬性,不能改變型別。

練習程式碼

#include <iostream>
#include <vector>
#include <string>
using namespace std;

int main()
{
	/* 4.4*/
	cout << 12 / 3 * 4 + 5 * 15 + 24 % 4 / 2 << endl;

	/* 4.5*/
	cout << -30 * 3 + 21 / 5 << endl;
	cout << -30 + 3 * 21 / 5 << endl;
	cout << 30 / 3 * 21 % 5 << endl;
	cout << -30 / 3 * 21 % 4 << endl;

	/* 4.6*/
	int a;
	/*cin >> a;*/
	a = 10;
	if (a % 2 != 0) {
		cout << "Odd." << endl;
	}
	else {
		cout << "Even." << endl;
	}

	/* 4.9*/
	const char* cp = "Hello";
	if (cp && *cp) {
		cout << "True." << endl;
	}
	else {
		cout << "False." << endl;
	}

	/* 4.10*/
	int b;
	while (cin >> b) {
		b = 42;
		if (b == 42) {
			cout << "End." << endl;
			break;
		}
		else {
			cout << "B = " << b << endl;
		}
	}

	/* 4.11*/
	int c, d;
	/*cin >> c >> d;*/
	c = 2; d = 1;
	if (a > b && b > c && c > d) {
		cout << "Yes." << endl;
	}
	else {
		cout << "No." << endl;
	}

	/* 4.13*/
	double f = 3.14;
	double g;
	g = b = f;
	cout << g << ";" << b << ";" << f << endl;

	/* 4.14*/
	// if (42 = a) { }
	if (a = 42) {
		cout << a << endl;
	}

	/* 4.15*/
	int* pi;
	// pi = a = f = 0;

	/* 4.19*/
	int ival = 10;
	vector<int> vec{ 2,1 };
	int* ptr = &ival;
	if (ptr != 0 && *ptr++) {
		cout << "*ptr = " << *ptr << endl;
		cout << "*--ptr = " << *--ptr << endl;
		cout << "*ptr++ = " << *ptr++ << endl;
	}
	if (ival++ && ival) {
		cout << "ival = " << ival << endl;
		cout << "--ival = " << --ival << endl;
	}
	ival = 0;
	if (vec[ival++] <= vec[ival]) {// 未定順序
		cout << "<=" << endl;
	}
	else {
		cout << ">" << endl;
	}

	/* 4.20*/
	vector<string> vStr{ "Hello","World","Hi","Yes" };
	auto iter = vStr.begin();
	cout << *iter++ << endl;// 輸出iter指向的物件,iter指向下一個(++ 比 * 優先順序高)
	cout << *iter-- << endl;
	// cout << (*iter)++ << endl;// string 沒有 ++ 運算
	// cout << *iter.empty() << endl;// string 沒有成員 empty,(. 比 * 優先順序高)
	cout << iter->empty() << endl;// 判斷iter指向物件是否為空,vector 有成員 empty
	// cout << ++ * iter << endl;// 錯誤
	cout << iter++->empty() << endl;// 判斷iter指向物件是否為空,iter指向下一個
	cout << iter->empty() << endl;

	/* 4.21*/
	vector<int> vecInt{ 1,2,3,4,5,6,7,8,9,0 };
	for (auto& v : vecInt) {
		v = (v % 2 == 0) ? (v) : (v * 2);
	}

	for (auto v : vecInt) {
		cout << v << " ";
	}
	cout << endl;

	/* 4.22*/
	vector<int> score{ 77,15,31,89,94,72,65,68,74,99 };
	for (auto v : score) {
		cout << ((v >= 90) ? "high score" : ((v < 60) ? "fail" : ((v < 75) ? "low pass" : "pass"))) << endl;
	}
	for (auto v : score) {
		if (v >= 90) {
			cout << "high score" << endl;
		}
		else if (v < 60) {
			cout << "fail" << endl;
		}
		else if (v < 75) {
			cout << "low pass" << endl;
		}
		else {
			cout << "pass" << endl;
		}
	}

	/* 4.23*/
	string s = "word";
	string pl = s + ((s[s.size() - 1] == 's') ? "" : "s");
	cout << pl << endl;

	/* 4.27*/
	unsigned long ul1 = 3, ul2 = 7;
	cout << (ul1 & ul2) << " " << (ul1 | ul2) << " " << (ul1 && ul2) << " " << (ul1 || ul2) << endl;

	/* 4.28*/
	bool ba;// 1
	char ca;// 1
	wchar_t wca;// 2
	char16_t c16a;// 2
	char32_t c32a;// 4
	short sa;// 2
	int ia;// 4
	long la;// 4
	long long lla;// 8
	float fa;// 4
	double da;// 8
	long double lda;// 8
	cout << sizeof(ba) << " " << sizeof(ca) << " " << sizeof(wca) << " " << sizeof(c16a) << " " << sizeof(c32a) << " " << sizeof(sa) << " " << sizeof(ia) << " " << sizeof(la) << " " << sizeof(lla) << " " << sizeof(fa) << " " << sizeof(da) << " " << sizeof(lda) << endl;

	/* 4.29*/
	int x[10];
	int* p = x;
	cout << sizeof(x) / sizeof(*x) << " " << sizeof(p) / sizeof(*p) << endl;
	cout << sizeof(p) << " " << sizeof(*p) << endl;// 8, 4

	/* 4.36*/
	int i36 = 5;
	double d36 = 3.5;
	i36 *= static_cast<int>(d36);
	int i37 = 5;
	i37 *= d36;
	cout << i36 << " " << i37 << endl;

	return 0;
}