c語言:從放棄到入門

9-3變量生命週期和修飾符

生命週期

函數的聲明週期

  起於調用,結束於調用結束

局部變量的生命週期

   起於調用,結束於調用結束

main函數的生命週期

   main開始  == 進程的開始   main函數的結束  == 進程的結束

全局變量的生命週期

	起於main調用,結束於main調用結束

修飾符

auto

      含義:只能用於修飾局部變量,表示該變量存儲於棧stack
      特點: 隨用隨開,用完消失
      C中默認的局部變量,就是auto類型,所以通常將其省略   c++ auto自動類型

register

    含義:只能修飾局部變量,原則上,將內存中的變量升級到CPU寄存器中存儲,這樣訪問速度會更快,但由於CPU寄存器數量有限,通常會在程序優化階段,被優化爲普通auto類型,可以通過彙編代碼來查看,優化過程(與平臺和編譯有關)
    特點:可以避免cpu與內存的頻繁交互 
    一般程序從內存讀取數據 放到寄存器 進行運算 運算結果寫入到內存

關於各個存儲部件中讀寫速度

		硬盤:7200轉
	    內存:頻率1333MHZ 1600MHZ  帶寬 = 頻率*64/8
    	緩存:
    	CPU:

extern

	含義:只能用於修飾全局變量,全局變量本身是全局可用的,但是由於文件是單個完成編譯,
	並且編譯是自上而下的,
	所以說,對於不是在本範圍內定義的全局變量,要想使用必須用extern 進行申明,
	如果不加上可能會造成重定義。
	
    特點:
	1.可以省略,聲明在前,定義在後,不論局部還是全局。	
	   main.c  :   int a;  other.c : int a =200;編譯不會報錯
	 2.外延性 某文件定義的全局變量 可在其他文件使用

c語言編譯是跨文件的

      編譯過程:單文件編譯 單文件每個編譯成xxx.o
      					鏈接所有.o文件和lib.so文件
      					生成可執行文件xxx.out

9-4 static

修飾局部變量

   特點:1.初始化值爲0,且只初始化一次
   			 2.生命週期等同於程序

修飾全局變量

	特點:1.全局的外延性消失,變成文件內部的全局變量  也適用於函數
			   2.存儲在data段

小結問題:

  1. extern int a;
    int a= 200;
    是不是同一個a?

  2. 局部變量和全局變量儲存的位置有什麼不同?

  3. static修飾的變量值所保存的位置在哪裏 才導致是累計變化的?
    保存在data段中

9-5 字符串常量——9-7字符串的輸入輸出

定義

	是雙引號括起的任意字符序列

字符串大小

	看到的大小,比我們實際字面量要多一個,
	最後的字符’\0’,我們稱爲字符串結束字符,是系統對雙引號引起的字符串自動加設 的,而非手動干預。

字符串存儲

	 字符串的存儲,不像其它普通類型的常量自變量的存儲一樣,普通類型自變量通常存儲 在代碼段(text),
	 而字符串,則存儲在數據段,且是隻讀數據段。
	  也就是通常意義上的常量區,但是常量區這種說法,是不太精確的,也不提倡。 

拓展

   棧區(stack):臨時變量,局部變量,生命週期結束即銷燬
   堆區(heap):一些new出來的變量 存儲的地方 特定銷燬時間統一銷燬,析構函數
   數據段(data):用來存放程序中已初始化的全局變量的一塊內存區域,屬於靜態內存分配。
   代碼段(text):用來存放程序執行代碼的一塊內存區域,只讀,且不可修改。可能包含一些只讀的常數變量,例如字符串常量等。 
   bbs段:bss段(bss segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區域

在這裏插入圖片描述

字符數組和字符串區別

	1.字符串數組(棧區)是將text區的常量字符串 拷貝到棧區 字符串值的改變實際上是棧的值改變
	   字符串指針 是直接指向 text區常量字符串地址 故不能改變text區的值
	2.相同點:字符數組長度大於等於字符串長度

字符串的輸入輸出

    puts函數:printf的自帶換行
    scanf函數:默認遇到空格結束輸入,scanf("%[\n]s")遇到回車結束輸入,但不檢查輸入長度,不安全
    gets函數:空格也可以讀入,但不檢查輸入長度,不安全。
    fgets函數:參數列表(接收變量,長度,輸入流)

tips

    1.printf(“”) 空串有換行功能
    2.gets();不檢查預留存儲區是否能夠容納實際輸入的數據,
    換句話說,如果輸入的字符數目大於數組的長度,gets 無法檢測到這個問題,就會發生內存越界,所以編程時建議使用 fgets()。
    char *fgets(char *s, int size, FILE *stream);
    fgets() 雖然比 gets() 安全,但安全是要付出代價的,
    代價就是它的使用比 gets() 要麻煩一點,有三個參數。
    它的功能是從 stream 流中讀取 size 個字符存儲到字符指針變量 s 所指向的內存空間。它的返回值是一個指針,指向字符串中第一個字符的地址。
	3. c中允許 c++不允許 
	 char a[2] = "china";
     printf("%s",a);
     輸出結果:ch
     4.char * p = "china";
	   printf("p = %p  p+1 = %p  p[0]=%c 0[p] =%c\n", p, p + 1, p[0], 0[p]);
	   printf("  = %p      = %p      =%c      =%c\n", "china", "china"+1, "china"[0], 0["china"]);
	   輸出結果:地址不相等 與視頻不符合

問題?

  1.視頻說   " 字符串,則存儲在數據段"

這篇文章說了
在代碼段中,也有可能包含一些只讀的常數變量,例如字符串常量等

 2.字符串指針和字符數組的區別?
    字符數組可修改內容,有個拷貝過程,data段常量區拷貝到棧區,字符數組實際是棧區
    字符串指針如果指向data段字符串常量 是不可修改地址的,若是指向字符數組反而可以

10-2 從字符串常量到字符數組

10-3 原生字符串處理

strlen(char *) 字符串長度

strcat(char *head,char *tail) 連接兩個字符串

注意:被連接的串必須有足夠空間
but vs 可行…

char firstName[] = "jim";
char familyName[30] = "tim";
strcat(firstName, familyName);
printf("%s", firstName);
輸出:jimtim

優化操作:

原本代碼
   while(*p) p++;
   char *p,*q;
   while(1){
     p * =q*;
     if(p* == '\0'){
		break;	
	 }
	 p++;q++;
   }
   優化後:
   while(*p) p++;
   while(*p++ = *q++);

Tip

  1. int* 和 char* ,float * 長度 是多少?
    指針統一長度爲4byte

        int a = 1;
    	int* ap = &a;
    	printf("%d \n", sizeof(ap));
    	char * c = "123";
    	printf("%d \n", sizeof(c));
    	float d = 1.0f;
    	float * dp = &d;
    	printf("%d \n", sizeof(dp));
    

    輸出:4
    4
    4

  2. char *ac = “123”;
    printf("%p \n", &ac);
    printf("%p \n", “123”);

    爲什麼輸出地址不同?

3.字符串長度和大小是不同的
長度是指字符長度且不包含\0,大小是指字節大小包含\0

10-7 字符串指針數組入門

指針數組的本質是數組,數組指針的本質是指針。

Tips

1.運行這段代碼 設置字符串池優化
char * pa = “china”; char pb = “america”; charpc = “canada”; char*pd = “japan”;
char * cpArr[4] = { pa,pb,pc,pd };
printf(“pa = %p \n”, pa);
printf(“pb = %p \n”, pb);
printf(“pc = %p \n”, pc);
printf(“pd = %p \n”, pd);

		for (int i = 0; i < 4; i++)
		{
			printf("%p \n",cpArr[i]);
		}
	
		printf("----------------\n", pd);
	
		char * cpArr2[4] = {"china","america","canada", "japan" };
	
		for (int i = 0; i < 4; i++)
		{
			printf("%p \n", cpArr2[i]);
		}
  1. NULL ,nullptr,0區別

11-1 棧內存和堆內存的基本概念

概念

源程序:源代碼
程序:可執行文件
進程:時間概念可執行性文件被拉起,到結束的這一段過程,稱爲進程
進程空間:可執行文件 被拉起以後 在內存中的分佈情況
內存空間

棧內存

棧存儲的特點

    棧中存放任意類型的變量,但必須是 **auto** 類型修飾的,即自動類型的局部變量,隨用隨開,用完即消。
    內存的分配和銷燬系統自動完成,不需要人工干預。
    分配順序,由高地址到低地址。

棧的大小

	 棧的大小並不大,他的意義並不在於存儲大數據,而在於數據交換。

常見棧溢出

	局部變量申請過多,過大,char[1024*1024*10];
	遞歸層數太多 例如10000層遞歸

堆內存

棧存儲的特點

	堆內存可以存放任意類型的數據,但需要自己申請與釋放。
	分配順序,由低地址到高地址。

堆大小

	堆大小,想像中的無窮大,對於棧來說,大空間申請,唯此,無它耳。但實際使用中,受限於實際內存的大小和內存是否連續性。(基本最大爲用戶空間大小)

Tips

1.memset(pm,1,10*sizeof(int);

   爲每個字節賦值爲1;一個int 實際4個字節 4個0x01
   printf("%#x");
   %#表示的輸出提示方式,如果是8進制,在前面加0,如果是十進制,不加任何字符,如果是十六進制,會加上0x 

2.calloc和malloc

   calloc在動態分配完內存後,自動初始化該內存空間爲零,而malloc不做初始化,分配到的空間中的數據是隨機數據

3.realloc

void *realloc(void *ptr, size_t size);
功能:擴容(縮小)原有內存的大小。通常用於擴容,縮小會會導致內存縮去的部分數據丟失。
參數:
void *ptr:ptr 表示待擴容(縮小)的指針, ptr 爲之前用 malloc 或 者 calloc 分配的內存地址。或 ptr==NULL,則該函數等同於 malloc。
size_t:size 表示擴容(縮小)後內存的大小。
返回值:
成功返回非空指針指向申請的空間 ,失敗返回 NULL。返回的指針,可能與 ptr 的值相同,也有可能不同。若相同,則說明在原空間後面申請,否則,則可能後續空間不足,重新申請的新的連續空間,原數據拷貝到新空間,原有空間自動釋放。
原理:先判斷當前的指針是否有足夠的連續空間,如果有,擴大mem_address指向的地址,並且將mem_address返回,如果空間不夠,先按照newsize指定的大小分配空間,將原有數據從頭到尾拷貝到新分配的內存區域,而後釋放原來mem_address所指內存區域(注意:原來指針是自動釋放,不需要使用free),同時返回新分配的內存區域的首地址。即重新分配存儲器塊的地址。

malloc 使用流程 申請 判空 使用 釋放 置空

問題1:指針接收 創建的內存空間(malloc),釋放時怎麼知道釋放(free)多少內存空間的

	heap的每個塊兒頭有保存本塊的大小以及本塊分配的大小。
	free並不知道那個指針所指空間的大小,它要先查找當前heap的頭部,然後整個塊兒釋放掉。

問題2:如何用malloc/new 定義一維,二維,三維數組?用free/delete銷燬呢?malloc/free和new/delete區別?

問題3:malloc/calloc/realloc 區別與不同?

問題4:常見的內存泄露和檢測內存泄露的常見方式?內存泄漏和野指針?

12-1 結構體 共用體 枚舉

typedef深入分析

定義

	用自定義名字爲已有數據類型命名。其實叫 typerename 更合適。
	形如:typedef 現在類型名 新類型名;

typedef 和#define 的區別

typedef 是以;號結尾的 C 語言語句。而#define 則是預處理階段的文本替換。有時
他們是可以互換的,但有時不可以。

	typedef a b;
	#define a b

Tips

  1.typedef char *pChar; pChar a; a等同於?
  試着預測輸出並調試以下代碼:
  {
  char *p,q;
  printf("sizeof(p) = %d  sizeof(q) = %d \n",sizeof(p),sizeof(q));// 4,1
  typedef char *pChar;
  pChar a,b;
  printf("sizeof(a) = %d  sizeof(b) = %d \n",sizeof(a),sizeof(b));// 4, 4
  #define DpChar char*;
  DpChar m,n;
  printf("sizeof(m) = %d  sizeof(n) = %d \n",sizeof(m),sizeof(n));// 4 ,1
  }
  
  2.總結
  新類型名一般用大寫表示,以便於區別。
  用 typedef 只能聲明新的類型名,不能創造新的類型,只是爲已經存在的類型起
 一個別名,也不能用來定義變量,即只能用其聲明的類型來定義變量;
  有時也可用宏定義來代替 typedef 的功能,但是宏定義是由預處理完成的,而
 typedef 則是在編譯時完成的,更爲靈活方便。 
  typedef 可以讓類型更見名知意,更便於移值。

結構體的初始化

初始化及成員訪問

點成員運算符(.) 優先級等同-> 但比*高

Tips

	問題一
	  初始化和賦值在c++中實際含義是?
	問題二
	  形參和實參之間傳遞是什麼關係?
	問題三
	  typedef和#define 本質區別在哪裏?
	問題四
	   結構體作形參爲什麼要傳指針?

結構體數組及應用

結構體嵌套和結構體大小

結構體嵌套

	結構體中,嵌套結構體,稱爲結構體嵌套。結構體中,既可以嵌套結構體類型變量,
也可以嵌套結構體類型,後一種方式不推薦。

結構體類型大小

結構體成員內存分佈

首成員在低地址,尾成員在高地址。

內存對齊

對齊規則

目的是解決:一個成員變量需要多個機器週期去讀的現象,稱爲內存不對齊。爲什麼要對齊
呢?本質是犧牲空間,換取時間的方法。

不同的編譯器和處理器,其結構體內部的成員有不同的對齊方式
x86(linux 默認#pragma pack(4), window 默認#pragma pack(8))。linux 最大支持 4 字節對齊。

方法:

	①取 pack(n)的值(n= 1 2 4 8--),取結構體中類型最大值 m。兩者取小即爲外對齊大 小 Y= (m<n?m:n)。 
	②將每一個結構 體的成員大小與 Y 比較取小者爲 X,作爲內對齊大小. 
	③所謂按 X 對齊,即爲地址(設起始地址爲 0)能被 X 整除的地方開始存放數據。
	④外部對齊原則是依據 Y 的值(Y 的最小整數倍),進行補空操作。

外對齊和內對齊:

	 外對齊Y:保證讀取結構體的起始地址到結束地址,表示結構體之間的對齊
	 內對齊X:保證從結構體內變量起始的地址到結束的地址 正好是該變量的長度,結構體內成員變量之間的對齊		

結構體中指針使用注意事項

1.向結構體內未初始化的指針拷貝

結構體中,包含指針,注意指針的賦值,切不可向未知區域拷貝。

struct student
{
 char*name;
 int score;
}stu;
int main()
{
 strcpy(stu.name,"Jimy");
 stu.score=99;
 return 0;
}

name 指針並沒有指向一個合法的地址,這時候其內部存的只是一些亂碼。所以在
調用 strcpy 函數時,會將字符串 “Jimy” 往亂碼所指的內存上拷貝,內存 name 指針根
本就無權訪問,導致出錯。 同樣stu.name = “Jimy”;可以的,name指向常量區,但是將來name不可改

int main()
{
	 struct student *pstu;
	 pstu = (struct student*)malloc(sizeof(struct student));
	 strcpy(pstu->name,"Jimy");
	 pstu->score=99;
	 free(pstu);
	 return 0;
}

爲指針變量 pstu 分配了內存,但是同樣沒有給 name 指針分配內存。錯誤與上面
第一種情況一樣,解決的辦法也一樣。這裏用了一個 malloc 給人一種錯覺,以爲也給
name 指針分配了內存。

2.未釋放結構體內指針所指向的空間

從內向外依次釋放空間。

Tip

1.結構體中嵌套構造類型成員的對齊(數組、結構體成員)

2.深拷貝和淺拷貝

	深拷貝:拷貝內存的內容,結構體之間互不影響。
	淺拷貝:直接地址賦值,指針共享一片內存。一個結構體發生變化,另一個結構體也會發生變化。

13-2 單鏈表

14-2 文本文件和二進制文件

文件流

C 語言把文件看作是一個字符的序列,即文件是由一個一個字符組成的字符流,因 此 c 語言將文件也稱之爲文件流。即,當讀寫一個文件時,可以不必關心文件的格式或
結構。

文件類型

文件,物理上是二進制,所以文本文件與二進制文件的區別並不是物理上的,而是邏輯上的。
文本文件是基於字符編碼的文件,常見的編碼有 ASCII 編碼,二進制文件是基於值編碼
的文件。

文本文件

以 ASCII 碼格式存放,一個字節存放一個字符。 文本文件的每一個
字節存放一個 ASCII 碼,代表一個字符。這便於對字符的逐個處理,但佔用存儲空間
較多,而且要花費時間轉換。

二進制文件

以值(補碼)編碼格式存放。二進制文件是把數據以二進制數的格
式存放在文件中的,其佔用存儲空間較少。數據按其內存中的存儲形式原樣存放。

用例:

int main() {
short a = 10000;

FILE * fp = fopen("ascii.txt", "w");
fprintf(fp, "%d", a);//文本寫
fclose(fp);


FILE *fp2 = fopen("bin.txt", "w");
char buf[] = "abcd";
fwrite(&a, 2, 1, fp2);//字節寫
//fwrite(buf, 4, 1, fp2);//字節寫
fclose(fp2);

return 0;

}

文件緩存

爲什麼要有緩衝區(buffer) 原因爲多種,有兩個重點:
1 從內存中讀取數據比從文件中讀取數據要快得多。
2 對文件的讀寫需要用到 open、read、write 等系統底層函數,而用戶進程每調用
一次系統函數都要從用戶態切換到內核態,等執行完畢後再返回用戶態,這種切
換要花費一定時間成本
(對於高併發程序而言,這種狀態的切換會影響到程序性
能)。

文件的打開和關閉

FILE 結構體

FILE 結構體是對緩衝區和文件讀寫狀態的記錄者,所有對文件的操作,都是通過
FILE 結構體完成的。
typedef struct {
 short level; /* 緩衝區滿/空程度 */
 unsigned flags; /* 文件狀態標誌 */
 char fd; /* 文件描述符 */
 unsigned char hold; /* 若無緩衝區不讀取字符 */
 short bsize; /* 緩衝區大小 */
 unsigned char *buffer; /* 數據傳送緩衝區位置 */
 unsigned char *curp; /* 當前讀寫位置 */
 unsigned istemp; /* 臨時文件指示 */
 short token; /* 用作無效檢測 */
} FILE ; /* 結構體類型名 FILE */

在開始執行程序的時候,將自動打開 3 個文件和相關的流:標準輸入流(stdin)、標
準輸出流(stdout)和標準錯誤(stderr),它們都是 FIEL*型的指針。流提供了文件和程序的
通信通道。

fopen

fopen作用
在這裏插入圖片描述
如果讀寫的是二進制文件,則還要加 b,比如 rb, r+b 等。 unix/linux 不區分文本和
二進制文件。

fclose

作用:強制輸出緩存內容 然後關閉FILE*

文件的讀和寫

一次讀一個字符

fputc

fgetc

feof

特點:feof 這個函數,是去讀標誌位判斷文件是否結束的。即在讀到文件結尾的時候再
去讀一次,標誌位纔會置位,此時再來作判斷文件處理結束狀態,文件到結尾。如果用
於打印,則會出現多打一次的的現象

一次讀一行字符

windows 換行符 ‘\n’ = 0x0d 0a;
linux 換行符 ‘\n’ = 0x 0a;

tips

1.fprintf(fp,fmt,buff) 文本寫出
fwrite(buff,size,count,fp) 字節寫出
2.亂碼原由
二進制文件讀取由acsii碼的方式讀取
3.文件緩存win和linux區別
win會立即輸出,linux會等待緩存滿了再輸出,加上\n會立刻輸出緩存
4.rewind(fp) 將文件指針重置到文件頭

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