4.1 表達式5+10*20/2的值是多少?
105
4.2 在下述表達式的合理位置添加括號,使得添加括號后運算對象的組合順序與添加前一致。
(a) *vec.begin() --> *(vec.begin())
(b) vec.begin() + 1 --> (vec.begin() + 1)
4.4 求下列表達式的值。
int result = 12 / 3 * 4 + 5 * 15 +24 % 4 / 2;
cout << result << endl; // 91
4.5 寫出下列表達式的求值結果。
int result1 = -30 * 3 + 21 / 5; // -86
int result2 = -30 + 3 * 21 / 5; // -18
int result3 = 30 / 3 * 21 % 5; // 0
int result4 = -30 / 3 * 21 % 4; // -2
4.6 寫出一個表達式,確定一個整數是奇數還是偶數。
int num = 4;
num % 2 == 1 ? cout << "odd" << endl : cout << "even" << endl;
4.7 溢出是何含義,寫出三條會溢出的表達式。
溢出,當計算結果超出該類型所能表示的范圍就會產生溢出。
short c = (short)65538; // print c = 2, overflow
4.8 說明在邏輯與、邏輯或及相等運算符中運算對象求值的順序。
相等運算符先求值,邏輯與、邏輯或運算優先級相同,按照從左到右的順序求值。
4.9 解釋下面的if語句中條件部分的判斷過程。
const char *cp = "Hello World";
if (cp && *cp);
// cp是一個地址,不為0。再取*cp的值,為一個字符串,不為0.最后兩者相與,值> 為真。
4.10 為while循環寫一個條件,使其從標准輸入中讀取整數,遇到42時停止。
void test410()
{
int number = 0;
while (cin >> number)
{
if (42 == number)
{
break;
}
}
}
4.11 書寫一條表達式用於測試4個值a, b, c, d的關系,確保a大於b,b大於c,c大於d。
if ((a>b) && (b>c) && (c>d))
4.12 假設i,j和k是三個整數,說明表達式i != j < k。
先取i值,再計算j < k是否成立,成立為大於0的數,否則為0。再計算i != (j < k) 。
4.13 下述語句中,賦值完i和d的值分別是多少?
d = i = 3.5; // i = 3, d = 3; i = 3.5, 將3.5隱式轉化為整型。
i = d = 3.5; // d = 3.5, i = 3.4.14
4.14 執行下述if語句會發生什么情況。
if (42 = i) //報錯,不能對常量42賦值。
if (i = 42) // 將42賦值給i,if條件為真。
4.15 下面的賦值是非法的,為什么,應該如何修改?
double dval;
int ival;
int *pi;
dval = ival = pi = 0; // pi存的是地址,給pi賦值為0,即要訪問地址為0的內存?
// 應改為:
dval = ival = *pi = 0;
4.16 盡管下面的語句合法,但它們實際執行的行為可能和預期不一樣,為什么?應如何修改?
if (p = getPtr != 0){} // 可能會先判斷!=, 再把結果賦值給p,改為if ((p = getPtr) != 0)
if (i = 1024) // 會把1024賦值給i,再進行判斷,改為if (1024 == i)
4.17 說明前置遞增運算符和后置遞增運算符的區別。
4.18
會先向后移動一個元素,再輸出移動后的值。輸出范圍為第二個元素到最后一個元素的下一個元素。由於最后一個元素的下一個元素未知,所以會訪問到未知內存。
4.19 假設ptr的類型是指向int的指針,vec的類型是vector
ptr != 0 && *ptr++; //判斷ptr是否為空,並且ptr指向的值是否為0;
ival ++ && ival; // 判斷ival及ival++是否為空。運算的順序為,先計算++,在進行&&
vec[ival++] <= vec[ival]; // 運算順序未知,比如ival = 1,編譯器有可能先算vec[ival++],得到ival=2,再計算vec[ival],這樣就得不到預期結果。因此改成vec[ival+1] <= vec[ival];
4.20 假設iter的類型是vector
*iter++; // 合法,先對iter+1,再返回iter初始值的副本,再對該副本進行解引用
(*iter)++; // 不合法,不能對string類型進行++操作。
*iter.empty(); // 不合法,不能對指針指向的值判空。可改為(*iter).empty();
iter->empty(); // 合法
++*iter; // 不合法,先求*iter,在進行++操作,不能對string類型做++操作,可改為 *(++iter);
iter++->empty(); //合法,++和->的優先級一樣,所以遵循自左向右結合,先算iter++的值,返回iter初始值的副本,再進行empty()判斷。iter->empty等價於(*iter).empty
4.21 編寫程序,使用條件運算符從vector
void test421()
{
vector<int> ivec = {1, 2, 3, 4, 5, 6};
for (auto &iter : ivec) {
iter = (iter % 2 == 1) ? iter * 2 : iter;
cout << iter << " ";
}
cout << endl;
}
4.22
void test422()
{
int grade;
string finalgrade;
while (cin >> grade)
{
// approach 1
finalgrade = (grade > 90) ? "high pass" : ((grade < 60) ? "fail" : ((grade < 75) ? "low pass" : "pass"));
// approch 2
if (grade > 90)
{
finalgrade = "hign pass";
}
else if (grade < 60)
{
finalgrade = "fail";
}
else if (grade < 75)
{
finalgrade = "low pass";
}
else
{
finalgrade = "pass";
}
cout << finalgrade << endl;
}
}
4.23 指出下面表達式的問題,並說明如何修改。
string s = "word";
// string p1 = s + s[s.size()-1] == 's' ? "" : "s";
// 條件語句優先級較低,要給其加括號。
string p1 = s + (s[s.size()-1] == 's' ? "" : "s");
4.24 假如條件運算符滿足的是左結合律,求值過程會是怎樣的?
4.25 如果一台機器上int占32位,char占8位,用的是Latin-1字符集,其中‘q’ 的二進制形式是01110001,那么表達式'q' << 6的值是什么?
'q' << 6運算會把結果隱式轉化為int類型,因為6是int類型。因此結果的二進制碼為:
0000 0000 0000 0000 0001 1100 0100 0000,轉化為10進制為7232.
4.26 測驗成績的例子中,如果使用unsigned int作為quiz1的類型會發生什么情況。
> 在某些系統上,int占16為,因此如果用unsigned int來存儲的話,在這些機器上就會發生位數不夠用的情況。而unsigned long則可以保證在任何機器上都至少有32位。
4.27 下列表達式的結果是什么?
unsigned long ul1 = 3, ul2 = 7;
cout << (ul1 & ul2) << endl; // 0011 & 0111 = 0011 = 3
cout << (ul1 | ul2) << endl; // 0011 | 0111 = 0111 = 7
cout << (ul1 && ul2) << endl; // 3 && 7 = 1
cout << (ul1 || ul2) << endl;4.28 // 3 || 7 = 1
4.28 編寫一段程序,輸出每一種內置類型所占空間的大小。
void test428()
{
cout << sizeof (int) << endl; // 4
cout << sizeof (char) << endl; // 1
cout << sizeof (short) << endl; // 2
cout << sizeof (long) << endl; // 8
cout << sizeof (float) << endl; // 4
cout << sizeof (double) << endl; // 8
cout << sizeof (long double) << endl; // 16
cout << sizeof (long long) << endl; // 8
}
4.29 推斷下面代碼的輸出,說明原因。
void test429()
{
int x[10];
int *p = x;
cout << sizeof(x)/sizeof(*x) << endl; // 10, sizeof(x) = 10*4, sizeof(*x) = 4;
cout << sizeof(p) / sizeof (*p) << endl; // 2, sizeof(p)的含義:p是一個int *類型,因此得出的大小應該是指針的大小。
// sizeof(*p)的含義:*p已經對p解引用了,*p實際就是int類型,因此sizeof(*p)得到的是一個int型的大小。
}
4.30 在下面的表達式的適當位置加上括號,使得加上括號之后表達式的含義與原來的含義相同。
sizeof x + y; // sizeof(x) + y;
sizeof p->mem[i]; // sizeof (p->mem[i]);
sizeof a < b; // sizeof (a) < b;
sizeof f(); // sizeof (f());
4.31 使用遞增和遞減運算符時,為什么要用前置版本而不用后置版本,要想使用后置版本需要做哪些改動?
在for循環中使用前置版本和后置版本都能得到相同的結果。這里使用前置版本的原因,就是4.5節中的建議所述:“前置版本的遞增運算符避免了不必要的工作,它把值加1后直接返回改變了運算對象。與之相比,后置版本需要將原始值存儲下來以便於返回這個未修改的內容,如果我們不需要修改前的值,那么后置版本的操作就是一種浪費。”
for ( init; condition; increment )
{
statement(s);
}
for循環的執行流程:
1、先執行init,且只執行一次。
2、判斷condition,如果為真,則執行循環主體。
3、循環主體執行完之后再執行increment,更新循環控制變量。
改為后置版本如下:
void test431()
{
vector<int> ivec(10, 0);
vector<int>::size_type cnt = ivec.size();
for (vector<int>::size_type ix = 0; ix != ivec.size(); ix++, cnt++)
{
cout << "ix = " << ix << " cnt = " << cnt << endl;
ivec[ix] = cnt;
}
}
4.32 解釋下面這個循環的含義。
constexpr int size = 5;
int ia[size] = {1, 2, 3, 4, 5};
for (int *ptr = ia, ix = 0; ix != size && ptr != ia+size; ++ix, ++ptr)
{
/* ... */
}
循環遍歷ia數組。ix和ptr的作用相同,一個使用下標遍歷,一個使用指針遍歷。
4.33 說明下面表達式的含義。
someValue ? ++x, ++y : --x, --y;
因為逗號表達式的優先級最低,按照預期,如果someValue為真,冒號后面的語句不會再執行了,但實際上,編譯器會認為冒號后面的--x屬於三目運算符中的語句,而--y屬於一個單獨的語句。也就是( someValue ? ++x, ++y : --x), --y; 因此,如果someValue為真,則執行++x,++y,--y,最后得到的結果是y本身。如果someValue為假,則執行--x,--y,最終的結果是--y的值。
4.34 說明下面的表達式中將會發生什么樣的類型轉化。
if (fval) // float類型轉化為bool類型
dval = fval + ival; // int先轉化為float,然后賦值給dval時再轉化為double類型
dval + ival * cval; // char先轉化為int,然后int轉化為double
4.35 假設有如下定義,則下面表達式中是否發生了隱式轉化?
char cval;
int ival;
unsigned int ui;
float fval;
double dval;
cval = 'a' + 3; // ‘a’先轉化為int進行計算,得到的結果再轉化為char
fval = ui - ival * 1.0; // ival先轉化為double,ui也轉化為double,double再截為float
dval = ui * fval; // unsigned int先提升為float,float再轉為double
cval = ival + fval + dval; // ival先轉為float,float再轉為double,最后double轉為char
4.36 假設i是int類型,d是double類型,書寫表達式i *= d使其執行整數類型的乘法而非浮點數的乘法。
i *= static_cast<int> (d);
4.37 用命名的強制轉化類型改寫下列舊式的轉化語句。
int i;
double d;
const string *ps;
char *pc;
void *pv;
pv = (void *)ps; // pv = const_cast<string *>(ps); 去除底層const屬性。
i = int(*pc); // i = static_cast<int>(*pc);
pv = &d; // pv = static_cast<void *>(&d);
pc = (char *)pv; // pc = reinterpret_cast<char *> (pv);
4.38 說明下面這條表達式的含義。
double slope = static_cast<double>(j/i);
將j/i的結果轉化為double類型,再賦值給slope。
