21-0002 牛客網刷題(第二天)

1.數組初始化問題

在這裏插入圖片描述
需要注意的點:

  • 數組只能夠省略第一維
  • char類型的數組,最後有一個’\0’,size是需要+1的
char a[]="hello";
char a[6]="hello";
char a[5]="hello";//error
char a[][5]={"this","is"};
char a[][]={"this","is"};//error

2.二進制的運算

Q:設 char 型變量 x 中的值爲10100111,則表達式(2+x)^(~3)的值是多少?

-00000000  //對齊操作
---------
 00000010  //2
+10100111  //原二進制數據
=10101001  //結果1
 
~00000011  //對3進行取反操作
=11111100  //結果2
 
 10101001  //result 1
^11111100  //result 2,異或操作,相同爲0,不同爲1
=01010101  //結果3,D is right.

備註:當時將’^'當成了冪運算,實際上這些運算應該是統一的,如果系統能夠確定是二進制運算,就不會再摻進其他類型的運算。

3.變量的內存分配【·】

Q:只有在使用時才爲該類型變量分配內存的存儲類說明是 autoregister

解釋:

  • auto 根據數據類型分配內存,
  • register 在使用到時分配CPU寄存器地址,
  • static 聲明的時候就會分配內存,不初始化系統會自動初始化,
  • extern 外部變量在程序被編譯時(程序運行之前)分配存儲。

相關鏈接:
1.C++ - 外部變量(extern)
2.extern和const和#ifndef和內存分配和C++的.h和.cpp
3.extern聲明變量詳解
4.C++中的static及內存分配

4.字符常量

'\007'//八進制轉義字符,表示響鈴
'\b'//ASCII碼,普通轉義字符,退格
'a'//字符a
"\09"//這是兩個字符,八進制中最多出現到7

5.靜態成員&非靜態成員

  • 靜態成員存在於內存,非靜態成員需要實例化纔會分配內存
  • 非靜態成員可以直接訪問類中靜態的成員
  • 靜態成員不能訪問非靜態的成員
  • 非靜態成員的生存期決定於該類的生存期,而靜態成員生存期則與程序生命期相同

解釋:靜態成員存在於內存,非靜態成員需要實例化纔會分配內存,所以靜態成員函數不能訪問非靜態的成員。因爲靜態成員存在於內存,所以非靜態成員函數可以直接訪問類中靜態的成員。[至少你要訪問的東西是在內存中的]

6.運算符重載

下面是C++運算符的優先級順序,【】內是不能夠重載的運算符

作用域 【::】
增量、對象、數組 -> 【.】 ++(後) - -(後) [ ]
增量、內存、取反 ~ ++(前) - -(前) +(正) -(負) *(指針) &(取址) new delete 【sizeof】
類成員指針 ->* 【.*】
算術 * / %
算術 + -
<< >>
關係 < <= >= >
關係 == !=
&
^
|
&& 邏輯
邏輯 ||
條件 【?:】
賦值 = += -= *= /= %= &= ^= |=
逗號 ,
  • .和.*是爲保護訪問成員功能而不允許被重載;
  • sizeof運算對象是類型而非變量,不具備重載特徵。

我覺得對於可以重載的運算符進行重載,需要單獨寫一篇文檔。

7.二進制表示中1的個數【·】

#include <stdio.h>
 
int fun(int i) 
{ 
int cnt = 0; 
while(i) 
{ 
cnt++; 
i = i&(i-1); //循環下來計算的是從右往左數1的個數。
} 
return cnt; 
} 
 
int main()
{ 
printf( "%d\n", fun(2017) );
return 0; 
}

//output:7

解釋:2017的二進制是11111100001,有7個1。

8.等概率輸出數字【·】

下面的程序可以從0…n-1中隨機等概率的輸出m個不重複的數。這裏我們假設n遠大於m,直接看代碼吧:

knuth(int n, int m)
{ 
    srand((unsigned int)time(0)); 
    for (int i = 0; i < n; i++) { 
        if (rand()%(n-i)<m) { //原題目中需要填寫
            cout << i << endl;
            m--;//原題目中需要填寫
        }
     }
}

原理解釋:

由這個for循環循環n次,且在滿足條件時才輸出 i 可知,輸出m個不同值的要求已滿足,因爲每次輸出的都是 i 值,而 i 值每次都是不一樣的,m-- 保證了程序在輸出了m個值後就停止循環。
.
在i=0時,rand()%(n-i)的取值範圍爲0到n-1,共n個數,此時要輸出0只需要rand()%(n-i)小於m,故i=0被輸出的概率爲m/n;
.
在i=1時,rand()%(n-i)的取值範圍爲0到n-2,共n-1個數,

若i=0沒有被輸出,則m–未被執行,此時i=1被輸出的概率爲m/(n-1),
.
若i=0已經被輸出了,則m變爲m-1,此時i=1被輸出的概率爲(m-1)/(n-1);

由概率論的知識,可知此時i=1被輸出的概率爲
P=(1-m/n)(m/(n-1))+m/n((m-1)/(n-1))=m/n;
以此類推,可知每個數被輸出的概率都爲m/n.

備註:

  • 這裏輸出的是 i,m只是用來判斷個數的,並不會輸出的數字逗比m小,i 是逐漸遞增的,隨機數是用來判斷的。
  • 使用隨機數作爲判斷的條件,來輸出隨機數,做法比較高明

9.進制轉換

源碼:

auto fn = [](unsigned char a){
    cout << std::hex << (int)a << endl;
};
fn(-1);
//output:ff

題中形式爲C++11標準裏引入的lambda表達式,一個lambda表達式表示一個可調用的代碼單元,也可將其理解爲一個未命名的內聯函數

其基本形式如下:

  • [capture list ] ( parameter list ) -> return type { function body }
  • [捕獲列表] ( 參數列表 ) -> 返回類型 { 函數體 }
  • capture list (捕獲列表)是一個lambda所在函數中定義的局部變量的列表(通常爲空,寫爲 [] ),空捕獲列表即表明此lambda不使用它所在函數中的任何局部變量;
  • " -> " 表明lambda使用了尾置返回類型;
  • 可以忽略參數列表和返回類型,但必須永遠包含捕獲列表和函數體

比如:

auto f = [ ] { return 42; }; // 定義了一個可調用對象f,它不接受參數,返回42。
即常見形式如: 
[ ] { 函數體 }
[ ] ( int n ) { 函數體 } 

回到上面的問題:

 1. int型-1的存儲方式爲補碼(32位,4字節),1111 1111 1111 1111 1111 1111 1111 1111
 2. 轉換爲unsigned char(8位,1字節)發生字節截斷,取最後八位爲1111 1111
 3. 再轉換爲int後爲0000 0000 0000 0000 0000 0000 1111 1111
 4. 輸出爲ff [hex爲16進制,四個四個的看]

這個題目中我需要知道:

  • 原碼、反碼、補碼
  • 計算機爲什麼使用補碼存儲數據
  • C++11的lambda表達式有什麼好處
  • 正數的原碼、反碼和補碼都相同。
  • 負數原碼2反碼:符號位不變,數值位按位取反。
  • 負數原碼2補碼:符號位不變,數值位按位取反,末位再加1。

相關鏈接:
1.c++ 補碼與整數互相轉換
2.計算機爲什麼使用補碼來存儲數據
3.計算機中帶符號的整數爲何採用二進制的補碼進行存儲?
4.C++原碼、反碼、補碼詳解
5.原碼反碼補碼的相互轉換
6.C++ 11 Lambda表達式 通俗易懂

與此類似的還有一道題目:

short int i=32769printf("%d\n",i)//output:-32767

解釋:

計算機中整數以補碼的形式進行存儲,第一位是符號位
shout int 只有2字節
but 32769 is 1000 0000 0000 0001,由於最高位是1,表示複數
由上述補碼,求原碼
1000 0000 0000 0001//補碼
1111 1111 1111 1110//按位取反,臨時反碼
1111 1111 1111 1111//末位加1,原碼
=-32767

10.對象的創建

Q1:假定CSomething是一個類,執行下面這些語句之後,內存裏創建了 6 個CSomething對象。

CSomething a();//函數聲明
CSomething b(2);//構造函數調用,+1
CSomething c[3];//含有三個對象的數組,+3
CSomething &ra = b;//引用
CSomething d=b;//拷貝構造函數,+1
CSomething *pA = c;//單純是個指針
CSomething *p = new CSomething(4);//有對象生成,+1

Q2:在C++中,爲了讓某個類只能通過new來創建(即如果直接創建對象,編譯器將報錯),應該將析構函數設爲私有。

解釋: 編譯器在爲類對象分配棧空間時,會先檢查類的析構函數的訪問性,其實不光是析構函數,只要是非靜態的函數,編譯器都會進行檢查。如果類的析構函數是私有的,則編譯器不會在棧空間上爲類對象分配內存。 因此, 將析構函數設爲私有,類對象就無法建立在棧(靜態)上了,只能在堆上(動態new)分配類對象 。

11.struct、class

不同點:

struct class
默認繼承權限 public private
默認數據訪問控制 public private
模板參數 不能定義 可以用於定義模板參數

相同點:
可以有數據成員,方法,構造函數等。

相關鏈接:
1.C++中Struct與Class的區別與比較

12.指針數組,數組指針

這個概念有點像是馬原裏面的唯物辯證法&辯證唯物論,看誰是定語,誰是主語,相關解釋如下:

int *p[n];	[]優先級高,與p先結合成數組,是個指針數組,有n個指針類型的元素
int(*p)[n];	()優先級高,與*先結合成指針,是個數組指針,指向一個整形一維數組

同樣的,還有指針常量,常量指針:

int * const p;//指針常量,重點是個常量,這個指針是常量,指針指向的位置不能改變,也就是說不能亂指,可以亂碰指向區域的東西
const int * p;//常量指針,重點是個指針,指向常量的指針,指向的數據區內容不能變,也就是說可以亂指,但別亂碰指向區域的東西
int const * p;//同上,一個道理,const在*的前面

相關鏈接:
1.數組指針和指針數組的區別
2.指針常量和常量指針

13.結尾~~~~

總結一下,這些題目是C++比較基礎的知識,但是由於學習C++時記住了,但後來太多的依靠各種搜索引擎,以至於只是掌握的不牢固,該背的內容還是要背的,畢竟寫代碼的時候不能總靠搜索,靠API,雖說是在使用C++進行開發,但是重點在使用,而不是查!!!!
【2020年3月19日01:36:39】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章