主頁 > 軟體設計 > 10W+字C語言硬核總結(一),值得閱讀收藏!

10W+字C語言硬核總結(一),值得閱讀收藏!

2021-07-23 08:36:59 軟體設計

一. C語言概述

歡迎大家來到c語言的世界,c語言是一種強大的專業化的編程語言,

程式員必備硬核資料,點擊下載

1.1 C語言的起源

貝爾實驗室的Dennis Ritchie在1972年開發了C,當時他正與ken Thompson一起設計UNIX作業系統,然而,C并不是完全由Ritchie構想出來的,它來自Thompson的B語言,

1.2 使用C語言的理由

在過去的幾十年中,c語言已成為最流行和最重要的編程語言之一,它之所以得到發展,是因為人們嘗試使用它后都喜歡它,過去很多年中,許多人從c語言轉而使用更強大的c++語言,但c有其自身的優勢,仍然是一種重要的語言,而且它還是學習c++的必經之路,

  • 高效性,c語言是一種高效的語言,c表現出通常只有匯編語言才具有的精細的控制能力(匯編語言是特定cpu設計所采用的一組內部制定的助記符,不同的cpu型別使用不同的匯編語言),如果愿意,您可以細調程式以獲得最大的速度或最大的記憶體使用率,

  • 可移植性,c語言是一種可移植的語言,意味著,在一個系統上撰寫的c程式經過很少改動或不經過修改就可以在其他的系統上運行,

  • 強大的功能和靈活性,c強大而又靈活,比如強大靈活的UNIX作業系統便是用c撰寫的,其他的語言(Perl、Python、BASIC、Pascal)的許多編譯器和解釋器也都是用c撰寫的,結果是當你在一臺Unix機器上使用Python時,最終由一個c程式負責生成最后的可執行程式,

1.3 C語言標準

1.3.1 K&R C

起初,C語言沒有官方標準,1978年由美國電話電報公司(AT&T)貝爾實驗室正式發表了C語言,布萊恩?柯林漢(Brian Kernighan) 和 丹尼斯?里奇(Dennis Ritchie) 出版了一本書,名叫《The C Programming Language》,這本書被 C語言開發者們稱為K&R,很多年來被當作 C語言的非正式的標準說明,人們稱這個版本的 C語言為K&R C,

K&R C主要介紹了以下特色:結構體(struct)型別;長整數(long int)型別;無符號整數(unsigned int)型別;把運算子=+和=-改為+=和-=,因為=+和=-會使得編譯器不知道使用者要處理i = -10還是i =- 10,使得處理上產生混淆,

即使在后來ANSI C標準被提出的許多年后,K&R C仍然是許多編譯器的最準要求,許多老舊的編譯器仍然運行K&R C的標準,

1.3.2 ANSI C/C89標準

1970到80年代,C語言被廣泛應用,從大型主機到小型微機,也衍生了C語言的很多不同版本,1983年,美國國家標準協會(ANSI)成立了一個委員會X3J11,來制定 C語言標準,

1989年,美國國家標準協會(ANSI)通過了C語言標準,被稱為ANSI X3.159-1989 "Programming Language C",因為這個標準是1989年通過的,所以一般簡稱C89標準,有些人也簡稱ANSI C,因為這個標準是美國國家標準協會(ANSI)發布的,

1990年,國際標準化組織(ISO)和國際電工委員會(IEC)把C89標準定為C語言的國際標準,命名為ISO/IEC 9899:1990 - Programming languages -- C[5] ,因為此標準是在1990年發布的,所以有些人把簡稱作C90標準,不過大多數人依然稱之為C89標準,因為此標準與ANSI C89標準完全等同,

1994年,國際標準化組織(ISO)和國際電工委員會(IEC)發布了C89標準修訂版,名叫ISO/IEC 9899:1990/Cor 1:1994[6] ,有些人簡稱為C94標準,

1995年,國際標準化組織(ISO)和國際電工委員會(IEC)再次發布了C89標準修訂版,名叫ISO/IEC 9899:1990/Amd 1:1995 - C Integrity[7] ,有些人簡稱為C95標準,

1.3.3 C99標準

1999年1月,國際標準化組織(ISO)和國際電工委員會(IEC)發布了C語言的新標準,名叫ISO/IEC 9899:1999 - Programming languages -- C ,簡稱C99標準,這是C語言的第二個官方標準,

例如:

  • 增加了新關鍵字 restrict,inline,_Complex,_Imaginary,_Bool

  • 支持 long long,long double _Complex,float _Complex 這樣的型別

  • 支持了不定長的陣列,陣列的長度就可以用變數了,宣告型別的時候呢,就用 int a[*] 這樣的寫法,不過考慮到效率和實作,這玩意并不是一個新型別,

二、記憶體磁區

2.1 資料型別

2.1.1 資料型別概念

什么是資料型別?為什么需要資料型別? 資料型別是為了更好進行記憶體的管理,讓編譯器能確定分配多少記憶體,

我們現實生活中,狗是狗,鳥是鳥等等,每一種事物都有自己的型別,那么程式中使用資料型別也是來源于生活,

當我們給狗分配記憶體的時候,也就相當于給狗建造狗窩,給鳥分配記憶體的時候,也就是給鳥建造一個鳥窩,我們可以給他們各自建造一個別墅,但是會造成記憶體的浪費,不能很好的利用記憶體空間,

我們在想,如果給鳥分配記憶體,只需要鳥窩大小的空間就夠了,如果給狗分配記憶體,那么也只需要狗窩大小的記憶體,而不是給鳥和狗都分配一座別墅,造成記憶體的浪費,

當我們定義一個變數,a = 10,編譯器如何分配記憶體?計算機只是一個機器,它怎么知道用多少記憶體可以放得下10?

所以說,資料型別非常重要,它可以告訴編譯器分配多少記憶體可以放得下我們的資料,

狗窩里面是狗,鳥窩里面是鳥,如果沒有資料型別,你怎么知道冰箱里放得是一頭大象!

資料型別基本概念:

  • 型別是對資料的抽象;

  • 型別相同的資料具有相同的表示形式、存盤格式以及相關操作;

  • 程式中所有的資料都必定屬于某種資料型別;

  • 資料型別可以理解為創建變數的模具: 固定大小記憶體的別名;

    2.1.2 資料型別別名

typedef unsigned int u32;
typedef struct _PERSON{
 char name[64];
 int age;
}Person;

void test(){
 u32 val; //相當于 unsigned int val;
 Person person; //相當于 struct PERSON person;
}

2.1.3 void資料型別

void字面意思是”無型別”,void* 無型別指標,無型別指標可以指向任何型別的資料,

void定義變數是沒有任何意義的,當你定義void a,編譯器會報錯,

void真正用在以下兩個方面:

  • 對函式回傳的限定;

  • 對函式引數的限定;

//1. void修飾函式引數和函式回傳
void test01(void){
 printf("hello world");
}

//2. 不能定義void型別變數
void test02(){
 void val; //報錯
}

//3. void* 可以指向任何型別的資料,被稱為萬能指標
void test03(){
 int a = 10;
 void* p = NULL;
 p = &a;
 printf("a:%d\n",*(int*)p);
 
 char c = 'a';
 p = &c;
 printf("c:%c\n",*(char*)p);
}

//4. void* 常用于資料型別的封裝
void test04(){
 //void * memcpy(void * _Dst, const void * _Src, size_t _Size);
}

2.1.4 sizeof 運算子

sizeof 是 c語言中的一個運算子,類似于++、--等等,sizeof 能夠告訴我們編譯器為某一特定資料或者某一個型別的資料在記憶體中分配空間時分配的大小,大小以位元組為單位,

基本語法:

sizeof(變數);
sizeof 變數;
sizeof(型別);

sizeof 注意點

  • sizeof回傳的占用空間大小是為這個變數開辟的大小,而不只是它用到的空間,和現今住房的建筑面積和實用面積的概念差不多,所以對結構體用的時候,大多情況下就得考慮位元組對齊的問題了;

  • sizeof回傳的資料結果型別是unsigned int;

  • 要注意陣列名和指標變數的區別,通常情況下,我們總覺得陣列名和指標變數差不多,但是在用sizeof的時候差別很大,對陣列名用sizeof回傳的是整個陣列的大小,而對指標變數進行操作的時候回傳的則是指標變數本身所占得空間,在32位機的條件下一般都是4,而且當陣列名作為函式引數時,在函式內部,形參也就是個指標,所以不再回傳陣列的大小;

//1. sizeof基本用法
void test01(){
 int a = 10;
 printf("len:%d\n", sizeof(a));
 printf("len:%d\n", sizeof(int));
 printf("len:%d\n", sizeof a);
}

//2. sizeof 結果型別
void test02(){
 unsigned int a = 10;
 if (a - 11 < 0){
  printf("結果小于0\n");
 }
 else{
  printf("結果大于0\n");
 }
 int b = 5;
 if (sizeof(b) - 10 < 0){
  printf("結果小于0\n");
 }
 else{
  printf("結果大于0\n");
 }
}

//3. sizeof 碰到陣列
void TestArray(int arr[]){
 printf("TestArray arr size:%d\n",sizeof(arr));
}
void test03(){
 int arr[] = { 10, 20, 30, 40, 50 };
 printf("array size: %d\n",sizeof(arr));

 //陣列名在某些情況下等價于指標
 int* pArr = arr;
 printf("arr[2]:%d\n",pArr[2]);
 printf("array size: %d\n", sizeof(pArr));

 //陣列做函式函式引數,將退化為指標,在函式內部不再回傳陣列大小
 TestArray(arr);
}

2.1.5 資料型別總結

  • 資料型別本質是固定記憶體大小的別名,是個模具,C語言規定:通過資料型別定義變數;

  • 資料型別大小計算(sizeof);

  • 可以給已存在的資料型別起別名typedef;

  • 資料型別的封裝(void 萬能型別);

2.2 變數

2.1.1 變數的概念

既能讀又能寫的記憶體物件,稱為變數;

若一旦初始化后不能修改的物件則稱為常量,

變數定義形式: 型別  識別符號, 識別符號, … , 識別符號

2.1.2 變數名的本質

  • 變數名的本質:一段連續記憶體空間的別名;

  • 程式通過變數來申請和命名記憶體空間 int a = 0;

  • 通過變數名訪問記憶體空間;

  • 不是向變數名讀寫資料,而是向變數所代表的記憶體空間中讀寫資料;

修改變數的兩種方式:

  void test(){
 
 int a = 10;

 //1. 直接修改
 a = 20;
 printf("直接修改,a:%d\n",a);

 //2. 間接修改
 int* p = &a;
 *p = 30;

 printf("間接修改,a:%d\n", a);
}

2.3 程式的記憶體磁區模型

2.3.1 記憶體磁區

2.3.1.1 運行之前

我們要想執行我們撰寫的c程式,那么第一步需要對這個程式進行編譯, 1)預處理:宏定義展開、頭檔案展開、條件編譯,這里并不會檢查語法

2)編譯:檢查語法,將預處理后檔案編譯生成匯編檔案

3)匯編:將匯編檔案生成目標檔案(二進制檔案)

4)鏈接:將目標檔案鏈接為可執行程式

? 代碼區

存放 CPU 執行的機器指令,通常代碼區是可共享的(即另外的執行程式可以呼叫它),使其可共享的目的是對于頻繁被執行的程式,只需要在記憶體中有一份代碼即可,代碼區通常是只讀的,使其只讀的原因是防止程式意外地修改了它的指t令,另外,代碼區還規劃了區域變數的相關資訊,

? 全域初始化資料區/靜態資料區(data段)

該區包含了在程式中明確被初始化的全域變數、已經初始化的靜態變數(包括全域靜態變數和t)和常量資料(如字串常量),

? 未初始化資料區(又叫 bss 區)

存入的是全域未初始化變數和未初始化靜態變數,未初始化資料區的資料在程式開始執行之前被內核初始化為 0 或者空(NULL),

總體來講說,程式源代碼被編譯之后主要分成兩種段:程式指令(代碼區)和程式資料(資料區),代碼段屬于程式指令,而資料域段和.bss段屬于程式資料,

那為什么把程式的指令和程式資料分開呢?

  • 程式被load到記憶體中之后,可以將資料和代碼分別映射到兩個記憶體區域,由于資料區域對行程來說是可讀可寫的,而指令區域對程式來講說是只讀的,所以磁區之后呢,可以將程式指令區域和資料區域分別設定成可讀可寫或只讀,這樣可以防止程式的指令有意或者無意被修改;

  • 當系統中運行著多個同樣的程式的時候,這些程式執行的指令都是一樣的,所以只需要記憶體中保存一份程式的指令就可以了,只是每一個程式運行中資料不一樣而已,這樣可以節省大量的記憶體,比如說之前的Windows Internet Explorer 7.0運行起來之后, 它需要占用112 844KB的記憶體,它的私有部分資料有大概15 944KB,也就是說有96 900KB空間是共享的,如果程式中運行了幾百個這樣的行程,可以想象共享的方法可以節省大量的記憶體,

2.3.1.1 運行之后

程式在加載到記憶體前,代碼區和全域區(data和bss)的大小就是固定的,程式運行期間不能改變,然后,運行可執行程式,作業系統把物理硬碟程式load(加載)到記憶體,除了根據可執行程式的資訊分出代碼區(text)、資料區(data)和未初始化資料區(bss)之外,還額外增加了堆疊區、堆區,

? 代碼區(text segment)

加載的是可執行檔案代碼段,所有的可執行代碼都加載到代碼區,這塊記憶體是不可以在運行期間修改的,

? 未初始化資料區(BSS)

加載的是可執行檔案BSS段,位置可以分開亦可以緊靠資料段,存盤于資料段的資料(全域未初始化,靜態未初始化資料)的生存周期為整個程式運行程序,

? 全域初始化資料區/靜態資料區(data segment)

加載的是可執行檔案資料段,存盤于資料段(全域初始化,靜態初始化資料,文字常量(只讀))的資料的生存周期為整個程式運行程序,

? 堆疊區(stack)

堆疊是一種先進后出的記憶體結構,由編譯器自動分配釋放,存放函式的引數值、回傳值、區域變數等,在程式運行程序中實時加載和釋放,因此,區域變數的生存周期為申請到釋放該段堆疊空間,

? 堆區(heap)

堆是一個大容器,它的容量要遠遠大于堆疊,但沒有堆疊那樣先進后出的順序,用于動態記憶體分配,堆在記憶體中位于BSS區和堆疊區之間,一般由程式員分配和釋放,若程式員不釋放,程式結束時由作業系統回收,

2.3.2 磁區模型

2.3.2.1 堆疊區

由系統進行記憶體的管理,主要存放函式的引數以及區域變數,在函式完成執行,系統自行釋放堆疊區記憶體,不需要用戶管理,

#char* func(){
 char p[] = "hello world!"; //在堆疊區存盤 亂碼
 printf("%s\n", p);
 return p;
}
void test(){
 char* p = NULL;
 p = func();  
 printf("%s\n",p); 
}

2.3.2.2 堆區

由編程人員手動申請,手動釋放,若不手動釋放,程式結束后由系統回收,生命周期是整個程式運行期間,使用malloc或者new進行堆的申請,

char* func(){
 char* str = malloc(100);
 strcpy(str, "hello world!");
 printf("%s\n",str);
 return str;
}

void test01(){
 char* p = NULL;
 p = func();
 printf("%s\n",p);
}

void allocateSpace(char* p){
 p = malloc(100);
 strcpy(p, "hello world!");
 printf("%s\n", p);
}

void test02(){
 
 char* p = NULL;
 allocateSpace(p);

 printf("%s\n", p);
}

堆分配記憶體API:

#include <stdlib.h>
void *calloc(size_t nmemb, size_t size);

功能:

在記憶體動態存盤區中分配nmemb塊長度為size位元組的連續區域,calloc自動將分配的記憶體 置0,

引數:

nmemb:所需記憶體單元數量 size:每個記憶體單元的大小(單位:位元組)

回傳值:

成功:分配空間的起始地址

失敗:NULL

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

功能:

重新分配用malloc或者calloc函式在堆中分配記憶體空間的大小, realloc不會自動清理增加的記憶體,需要手動清理,如果指定的地址后面有連續的空間,那么就會在已有地址基礎上增加記憶體,如果指定的地址后面沒有空間,那么realloc會重新分配新的連續記憶體,把舊記憶體的值拷貝到新記憶體,同時釋放舊記憶體,

引數:

ptr:為之前用malloc或者calloc分配的記憶體地址,如果此引數等于NULL,那么和realloc與malloc功能一致

size:為重新分配記憶體的大小, 單位:位元組

回傳值:

成功:新分配的堆記憶體地址

失敗:NULL

void test01(){
 
 int* p1 = calloc(10,sizeof(int));
 if (p1 == NULL){
  return;
 }
 for (int i = 0; i < 10; i ++){
  p1[i] = i + 1;
 }
 for (int i = 0; i < 10; i++){
  printf("%d ",p1[i]);
 }
 printf("\n");
 free(p1);
}

void test02(){
 int* p1 = calloc(10, sizeof(int));
 if (p1 == NULL){
  return;
 }
 for (int i = 0; i < 10; i++){
  p1[i] = i + 1;
 }

 int* p2 = realloc(p1, 15 * sizeof(int));
 if (p2 == NULL){
  return;
 }

 printf("%d\n", p1);
 printf("%d\n", p2);

 //列印
 for (int i = 0; i < 15; i++){
  printf("%d ", p2[i]);
 }
 printf("\n");

 //重新賦值
 for (int i = 0; i < 15; i++){
  p2[i] = i + 1;
 }
 
 //再次列印
 for (int i = 0; i < 15; i++){
  printf("%d ", p2[i]);
 }
 printf("\n");

 free(p2);
}

2.3.2.3 全域/靜態區

全域靜態區內的變數在編譯階段已經分配好記憶體空間并初始化,這塊記憶體在程式運行期間一直存在,它主要存盤全域變數、靜態變數和常量,

注意:

(1)這里不區分初始化和未初始化的資料區,是因為靜態存盤區內的變數若不顯示初始化,則編譯器會自動以默認的方式進行初始化,即靜態存盤區內不存在未初始化的變數,

(2)全域靜態存盤區內的常量分為常變數和字串常量,一經初始化,不可修改,靜態存盤內的常變數是全域變數,與區域常變數不同,區別在于區域常變數存放于堆疊,實際可間接通過指標或者參考進行修改,而全域常變數存放于靜態常量區則不可以間接修改,

(3)字串常量存盤在全域/靜態存盤區的常量區,

int v1 = 10;//全域/靜態區
const int v2 = 20; //常量,一旦初始化,不可修改
static int v3 = 20; //全域/靜態區
char *p1; //全域/靜態區,編譯器默認初始化為NULL

//那么全域static int 和 全域int變數有什么區別?

void test(){
 static int v4 = 20; //全域/靜態區
}
char* func(){
 static char arr[] = "hello world!"; //在靜態區存盤 可讀可寫
 arr[2] = 'c';
 char* p = "hello world!"; //全域/靜態區-字串常量區 
 //p[2] = 'c'; //只讀,不可修改 
 printf("%d\n",arr);
 printf("%d\n",p);
 printf("%s\n", arr);
 return arr;
}
void test(){
 char* p = func();
 printf("%s\n",p);
}

2.3.2.4 總結

在理解C/C++記憶體磁區時,常會碰到如下術語:資料區,堆,堆疊,靜態區,常量區,全域區,字串常量區,文字常量區,代碼區等等,初學者被搞得云里霧里,在這里,嘗試捋清楚以上磁區的關系,

資料區包括:堆,堆疊,全域/靜態存盤區,

  • 全域/靜態存盤區包括:常量區,全域區、靜態區,

  • 常量區包括:字串常量區、常變數區,

  • 代碼區:存放程式編譯后的二進制代碼,不可尋址區,

可以說,C/C++記憶體磁區其實只有兩個,即代碼區和資料區,

2.3.3 函式呼叫模型

2.3.3.1 函式呼叫流程

堆疊(stack)是現代計算機程式里最為重要的概念之一,幾乎每一個程式都使用了堆疊,沒有堆疊就沒有函式,沒有區域變數,也就沒有我們如今能見到的所有計算機的語言,在解釋為什么堆疊如此重要之前,我們先了解一下傳統的堆疊的定義:

在經典的計算機科學中,堆疊被定義為一個特殊的容器,用戶可以將資料壓入堆疊中(入堆疊,push),也可以將壓入堆疊中的資料彈出(出堆疊,pop),但是堆疊容器必須遵循一條規則:先入堆疊的資料最后出堆疊(First In Last Out,FILO).

在經典的作業系統中,堆疊總是向下增長的,壓堆疊的操作使得堆疊頂的地址減小,彈出操作使得堆疊頂地址增大,

堆疊在程式運行中具有極其重要的地位,最重要的,堆疊保存一個函式呼叫所需要維護的資訊,這通常被稱為堆疊幀(Stack Frame)或者活動記錄(Activate Record).一個函式呼叫程序所需要的資訊一般包括以下幾個方面:

  • 函式的回傳地址;

  • 函式的引數;

  • 臨時變數;

  • 保存的背景關系:包括在函式呼叫前后需要保持不變的暫存器,

我們從下面的代碼,分析以下函式的呼叫程序:

int func(int a,int b){
 int t_a = a;
 int t_b = b;
 return t_a + t_b;
}

int main(){
 int ret = 0;
 ret = func(10, 20);
 return EXIT_SUCCESS;
}

程式員必備硬核資料,點擊下載

2.3.3.2 呼叫慣例

現在,我們大致了解了函式呼叫的程序,這期間有一個現象,那就是函式的呼叫者和被呼叫者對函式呼叫有著一致的理解,例如,它們雙方都一致的認為函式的引數是按照某個固定的方式壓入堆疊中,如果不這樣的話,函式將無法正確運行,

如果函式呼叫方在傳遞引數的時候先壓入a引數,再壓入b引數,而被呼叫函式則認為先壓入的是b,后壓入的是a,那么被呼叫函式在使用a,b值時候,就會顛倒,

因此,函式的呼叫方和被呼叫方對于函式是如何呼叫的必須有一個明確的約定,只有雙方都遵循同樣的約定,函式才能夠被正確的呼叫,這樣的約定被稱為”呼叫慣例(Calling Convention)”.一個呼叫慣例一般包含以下幾個方面:

函式引數的傳遞順序和方式

函式的傳遞有很多種方式,最常見的是通過堆疊傳遞,函式的呼叫方將引數壓入堆疊中,函式自己再從堆疊中將引數取出,對于有多個引數的函式,呼叫慣例要規定函式呼叫方將引數壓堆疊的順序:從左向右,還是從右向左,有些呼叫慣例還允許使用暫存器傳遞引數,以提高性能,

堆疊的維護方式

在函式將引數壓入堆疊中之后,函式體會被呼叫,此后需要將被壓入堆疊中的引數全部彈出,以使得堆疊在函式呼叫前后保持一致,這個彈出的作業可以由函式的呼叫方來完成,也可以由函式本身來完成,

為了在鏈接的時候對呼叫慣例進行區分,呼叫慣例要對函式本身的名字進行修飾,不同的呼叫慣例有不同的名字修飾策略,

事實上,在c語言里,存在著多個呼叫慣例,而默認的是cdecl.任何一個沒有顯示指定呼叫慣例的函式都是默認是cdecl慣例,比如我們上面對于func函式的宣告,它的完整寫法應該是:

int _cdecl func(int a,int b);

注意: cdecl不是標準的關鍵字,在不同的編譯器里可能有不同的寫法,例如gcc里就不存在_cdecl這樣的關鍵字,而是使用__attribute_((cdecl)).

2.3.3.2 函式變數傳遞分析

程式員必備硬核資料,點擊下載

2.3.4 堆疊的生長方向和記憶體存放方向

//1. 堆疊的生長方向
void test01(){

 int a = 10;
 int b = 20;
 int c = 30;
 int d = 40;

 printf("a = %d\n", &a);
 printf("b = %d\n", &b);
 printf("c = %d\n", &c);
 printf("d = %d\n", &d);

 //a的地址大于b的地址,故而生長方向向下
}

//2. 記憶體生長方向(小端模式)
void test02(){
 
 //高位位元組 -> 地位位元組
 int num = 0xaabbccdd;
 unsigned char* p = &num;

 //從首地址開始的第一個位元組
 printf("%x\n",*p);
 printf("%x\n", *(p + 1));
 printf("%x\n", *(p + 2));
 printf("%x\n", *(p + 3));
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/289657.html

標籤:其他

上一篇:【C/C++面試必備】volatile 關鍵字

下一篇:面試了一個 35 歲程式員,滿是辛酸與無奈,30歲以上的程式員如何玩轉職場?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more