天天看點

刨析《C語言》進階付費知識【完結】動态記憶體開辟return資料的存儲檔案預編譯針對無具體類型的操作記憶體

動态記憶體開辟

malloc

隻是從記憶體池中提取一塊合适的記憶體,并不會初始化,如果需要初始化,要麼手動,要麼使用calloc函數

動态開辟的空間,2種收回方式
  1. 主動釋放
  2. 程式結束
  1. 對NULL指針的解引用

    if(p==NULL) { printf("錯誤"); return ; }

  2. 對動态開辟空間的記憶體越界通路
  3. 使用free釋放非動态開辟的空間
  4. 使用free釋放動态的記憶體中的一部分
  5. 對同一塊動态開辟的空間,多次釋放
  6. 動态開辟的空間忘記釋放,會造成記憶體洩露
  7. 手動把p置成空

    p=NULL;

Void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);

}還差
free(str);
str=NULL;
void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL)
{
strcpy(str, “world”);
printf(str);
}
}//記憶體通路錯誤,因為free了,雖沒有消失,但找不到記憶體位址了,沒有通路權限;還是會進入if,因為沒有手動置空
           

以上例題來源于《高品質的C/C++程式設計》

realloc

增容函數:把原先那塊記憶體的内容複制到新塊上,是以,不能再使用指向舊記憶體的指針,而是使用realloc所傳回的新指針

return

在函數return 隻能傳回堆上的記憶體 比如malloc申請的堆記憶體,而

char p[]="hello world"; return p;//err

例:

int *p(void)
{
    
    int x= 10;
    return (&x);
}
           

資料的存儲

大端和小端

刨析《C語言》進階付費知識【完結】動态記憶體開辟return資料的存儲檔案預編譯針對無具體類型的操作記憶體

整形提升

#include<stdio.h>
int main()
{
	//unsigned char 0-255

	unsigned char a =200;
	//00000000000000000000000011001000	-char類型占一個位元組就是8位
	//11001000
	unsigned char b =100;
	//00000000000000000000000001100100
	//01100100

	unsigned char c=0 ;
	//a和b整形提示
	//00000000000000000000000011001000
	//00000000000000000000000001100100
	//00000000000000000000000100101100

	c = a + b;//整形先提升再相加  會截斷
	//00101100
	//00000000000000000000000000101100
	//
	printf("%d %d ",a+b, c);
	//			300			44

	return 0;
}
           

檔案

**#include<檔案名>**到系統提供的指定路徑下,找檔案,如果找不到,就報錯

**#include"檔案名"**先到目前路徑找下檔案,找不到就執行#include<檔案名>的過程,如果還找不到,就報錯

如果找到檔案後,就将檔案的内容複制粘貼帶#include預處理指令出現的位置

系統指定路徑在:

gcc -E test.c -o test.i -v
           

#include "..." 搜尋從這裡開始:

#include <...> 搜尋從這裡開始:

/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include

/usr/local/include

/usr/include

搜尋清單結束。

藍色部分就是系統指定路徑,可cd /usr/include檢視

标準錯誤

0:标準輸入

1:标準輸出

2:标準錯誤輸出

“>”表示重定向,&2表示标準錯誤輸出的通道,是以1>2&表示标準輸出重定向到标準錯誤輸出通道;

而1>2表示标準輸出重定向到檔案名為2的檔案中。

編譯檔案過程

1.預處理:對源檔案進行預處理生成預處理檔案,預處理CPP根據預處理指令(如#include,#define等)所包含的檔案内容插入程式中

gcc -E test.c -o test.i//可檢視編譯過程,用vim test.i 。最後面有詳細過程
printf("ARE=%.2f\n",ARE(3+2));==> printf("ARE=%.2f\n",3.14 *(3+2)*(3+2));
           

2.編譯:根據預處理檔案,編譯為彙編語言,調用彙程式設計式生成彙編代碼(.s檔案)

gcc -S test.s -o test.o
           

3.彙編:調用彙程式設計式,翻譯成機器語言,生成目标檔案(.o檔案)

gcc -c test.s -o test.o
           

4.連結:将test.o和運作時檔案,庫函數進行連結,調用連接配接器,将程式中用到的函數加到程式中,生成可執行檔案

gcc test.o -o test
           

預編譯

注釋一般用#if 0..else ..#endif 用來儲存到預處理檔案中

預編譯又叫預處理。預編譯不是編譯,而是編譯前的處理。這個操作是在正式編譯之前由系統自動完成的**。預編譯又叫預處理。預編譯不是編譯,而是編譯前的處理。這個操作是在正式編譯之前由系統自動完成的。

#define 定義一個預處理宏

#undef 取消宏的定義

#if 編譯預進行中的條件指令,相當于C文法中的if語句

#ifdef 判斷某個宏是否被定義,若已定義,執行随後的語句

#ifndef 與#ifdef相反,判斷某個宏是否未被定義

#elif 若#if, #ifdef, #ifndef或前面的#elif條件不滿足,則執行#elif之後的語句,相當于C文法中的else-if

#else #if, #ifdef, #ifndef對應, 若這些條件不滿足,則執行#else之後的語句,相當于C文法中的else

#endif #if, #ifdef, #ifndef這些條件指令的結束标志.

defined  與#if, #elif配合使用,判斷某個宏是否被定義

define與typedef的取别

你應該使用typedef而不是#define來建立新的類型,因為後者無法正确地處理指針類型
#define pro_char  char *
pro_char a,b;
//正确聲明了a,但是b卻被聲明了一個字元
           

針對無具體類型的操作

無具體類型不能對它解引用

刨析《C語言》進階付費知識【完結】動态記憶體開辟return資料的存儲檔案預編譯針對無具體類型的操作記憶體

記憶體

刨析《C語言》進階付費知識【完結】動态記憶體開辟return資料的存儲檔案預編譯針對無具體類型的操作記憶體

變量的生命周期

變量的生命周期就是從變量位址空間的配置設定到變量位址空間的釋放

程式是靜态存儲在磁盤上的檔案,程式是指令的集合,程式不運作,就談不上變量位址空間的配置設定。

程式運作過程對計算機資源的使用的描述就是程序。同一個程序每一次運作就是一個程序。當我們在指令行鍵入./ 可執行檔案的時候,程式就開始運作了。

程式的運作分為兩個階段,分别是加載和執行。程式首先被加載到具體的位址空間,比如全局變量、靜态局部變量和函數…,我們将其稱為程式的符号。程式符号的具體位址,在加載階段就已經配置設定好了。我們稱這樣的存儲區為靜态存儲區。

程式加載完畢,找到main函數,然後開始執行程式,在程式執行階段,遇到定義自動局部變量的語句的時候,系統自動為這些自動局部變量配置設定空間,局部變量才誕生了。這些局部變量所在的區域稱為**動态存儲區。**這些變量的位址是系統自動配置設定的,當所在函數的複合語句結束的時候,自動釋放其位址空間。是以稱為靜态配置設定。還有一種,在程式執行的過程中,程式需要的位址空間的大小不确定,需要程式員根據實際情況,向系統提出申請,這樣的配置設定的位址空間稱為動态配置設定。

程式執行的時候,需要将可執行程式加載到記憶體中,CPU從記憶體中讀取程式的指令

資料類型:首先由變量的名字找到變量的位址,然後根據變量的類型通路位址空間裡的内容

static

修飾局部變量:局部變量的生命周期變長

修飾全局變量:改變了變量的作用域,讓靜态的全局變量隻能在自己所在的源檔案内部使用,出了源檔案就沒法再使用了。

修飾函數:改變了函數的連結屬性

外部連結屬性->内部連結屬性

#include<stdio.h>
void cout(void){
	
	int i =0;
	printf("cout i++=%d\n",i++);
	return ;
}
//靜态變量在編譯值賦一次初值,而自動變量賦初值是在函數調用時,每調用一次就要重新賦初值 
void cout_c(void){
	
	static int i =0;//靜态局部變量 
	printf("cout i++=%d\n",i++);
	return ;
}

int main()
{
	int i;
	for(i=0;i<5;i++)
	cout(); 
	for(i=0;i<5;i++)
	cout_c();
	return 0;
 } 
 /*
 運作結果:
 cout i++=0
cout i++=0
cout i++=0
cout i++=0
cout i++=0
cout i++=0
cout i++=1
cout i++=2
cout i++=3
cout i++=4
           

在count函數裡,變量i是自動局部變量,調用到函數count的時候,才為其配置設定位址空間,

并将初值設定為0,然後輸出的值,再将i的值自增,然後,函數調用完畢,變量i的位址空間也就到站了,

是以再次調用的時候,再為其配置設定,…。

是以一直輸出是0。countc函數裡的靜态局部變量,在程式加載的時候,程式執行之前就已經确定了其位址空間,

并将其初始值設定為0,調用這個函數的時候,先輸出i的值為0,然後再将i自增,i的值就變為1,函數調用完畢,

函數内的自動局部變量的位址空間釋放,但是靜态局部變量的位址空間并沒有釋放是以再次調用的時候i的值為1,然後自增…。

直到整個程式運作完畢的時候,才釋放靜态局部變量i的值。

靜态存儲和動态變量的異同**:

相同:都需要配置設定記憶體

不同:靜态變量是由系統自動配置設定,釋放,程式員無法在程式運作過程手動配置設定,也無法在程式運的過程中手動釋放,靜态變量是在棧中配置設定的

動态變量是由程式員手動配置設定,釋放,程式員可以在程式運作過程手動配置設定,也可以在程式運的過程中手動釋放,可以在函數地執行過程中的任何一個時刻手動釋放動态變量的空間,不需要等函數終止時候釋放,靜态變量是在堆中配置設定的(連結清單)

程式的運作分為兩個步驟:

1.加載,将程式從硬碟加載到位址空間

2.執行,找到main函數開始執行

在程式執行之前,就已經配置設定了位址空間的變量或常量,存放在靜态存儲器

程式運作期間一直存在

在 程式執行期間,執行那條語句的時候再為變量配置設定位址空間,存放在動态存儲區,系統自動管理

satic聲明之後為内部,隻在源檔案中使用,也不允許調用内部函數

extern擴充變量的作用域

存儲方式

為其配置設定,…。

是以一直輸出是0。countc函數裡的靜态局部變量,在程式加載的時候,程式執行之前就已經确定了其位址空間,

并将其初始值設定為0,調用這個函數的時候,先輸出i的值為0,然後再将i自增,i的值就變為1,函數調用完畢,

函數内的自動局部變量的位址空間釋放,但是靜态局部變量的位址空間并沒有釋放是以再次調用的時候i的值為1,然後自增…。

直到整個程式運作完畢的時候,才釋放靜态局部變量i的值。

靜态存儲和動态變量的異同**:

相同:都需要配置設定記憶體

不同:靜态變量是由系統自動配置設定,釋放,程式員無法在程式運作過程手動配置設定,也無法在程式運的過程中手動釋放,靜态變量是在棧中配置設定的

動态變量是由程式員手動配置設定,釋放,程式員可以在程式運作過程手動配置設定,也可以在程式運的過程中手動釋放,可以在函數地執行過程中的任何一個時刻手動釋放動态變量的空間,不需要等函數終止時候釋放,靜态變量是在堆中配置設定的(連結清單)

存儲方式