C語言知識點匯總-創(chuàng)新互聯(lián)

C語言——只看這一篇就夠了!

C語言知識點保姆級總結,這不得進你的收藏夾吃灰?!
拖了很久的C語言所學知識的簡單小結,內容有點多,第一次總結也可能有錯誤或者不全面,歡迎隨時補充說明!

成都創(chuàng)新互聯(lián)公司堅持“要么做到,要么別承諾”的工作理念,服務領域包括:成都網站建設、網站建設、企業(yè)官網、英文網站、手機端網站、網站推廣等服務,滿足客戶于互聯(lián)網時代的武穴網站設計、移動媒體設計的需求,幫助企業(yè)找到有效的互聯(lián)網解決方案。努力成為您成熟可靠的網絡建設合作伙伴!數據類型

? 用不同數據類型所定義的變量所占空間大小不一樣,定義的變量不是保存于數據類型中,而是因為只有定義了該數據類型的變量才能保存數據。

一、整型

? 1、整型(int) 四字節(jié),默認有符號(-231-231-1),無符號加unsigned(0-232-1)(十位數);

? 2、短整型(short int) ,兩字節(jié)(-215-215-1)(五位數);

? 3、長整型(long int) ,四字節(jié)(同int,在目前的操作系統(tǒng)中幾乎沒有區(qū)別);

? 4、長長整型(long long int), 八字節(jié)(-263-263-1),無符號(0-264-1);

二、浮點型

? 1、單精度浮點數(float),四字節(jié),保留小數點后6位

? 2、雙精度浮點數(double),八字節(jié),保留小數點后15位

int為一個32為的存儲單元,long long為64為的存儲單元

1 B/byte(字節(jié)) = 8 bit(比特)(位)

1 KB(千字節(jié)) = 1024 B/byte(字節(jié))

1 MB = 1024 KB

1 GB = 1024 MB

1TB =1024 GB

1 PB = 1024 TB

1 EB = 1024 PB

三、字符型

? char,用于儲存字符,和int很像,可用ASCII碼來儲存字符,
eg:

char grade=’A’;
 char grade=65;
'  '單引號為字符,eg:char a='a';

? " "雙引號為字符串,eg:char* a=“asd”;編譯器會自動給字符串結尾添加’\0‘來作為字符結束標識,strlen函數中不統(tǒng)計\0,但是\0在內存中占據空間。

? 除此之外,還有轉義字符,通過反斜杠來完成相關操作,如果要特殊字符轉字面字符需要另外添加反斜杠,轉義字符在字符串中占空間,但是只計算一個長度,\0不計長度。

?在這里插入圖片描述

四、變量和常量

? 作用域(scope),程序設計概念,通常來說,一段程序代碼中所用到的名字并不總是有效/可用的,而限定這個名字的可用性的代碼范圍就是這個名字的作用域。

? 生命周期:變量的生命周期指的是變量的創(chuàng)建到變量的銷毀之間的一個時間段

在這里插入圖片描述

#includeint global = 2019;//全局變量
int main()
{
	int local = 2018;//局部變量
	return 0;
} 
分支及循環(huán)語句 一、分支語句

? 1、if語句

? 語法結構:

if(表達式)
語句;

if(表達式){
	語句列表1
}
else{
	語句列表2;
}

//多分支
if(表達式1){
	語句列表1;
}
else if(表達式2){
	語句列表2;
}
else{
	語句列表3;
}

? 表達式部分為真則執(zhí)行語句(0為假,非0為真),盡量在每個分支語句后都加{},否則只會執(zhí)行分支后第一條語句。

? else在沒有括號的情況下遵循就近原則所以在多重if語句嵌套使用情況下一定要加括號!

? 2、switch語句

? switch作為分支結構常用于多分支的情況,可以簡化多重if語句嵌套的情況。

? 語法結構

switch(表達式A){
           case  常量表達式1:
                    語句1;
                    break;
           case  常量表達式2:
                    語句2;
                    break;
                    ……
           case  常量表達式n:
                    語句n;
                    break;
           default:
                    語句n+1; 
                     break;
}

? 其中

? 1、case后第一句不可定義變量,必須跟常量或者常量表達式,并且不可相同;

? 2、break在語句中可以起到劃分作用,不可省略,否則無法實現(xiàn)分支功能;

? 3、default語句不應該省略,一般推薦位語句列表末尾;

? 4、switch語句結束條件:①遇到break;②執(zhí)行到語句列表末尾。

二、循環(huán)語句

? 1、while語句

? 語法結構

while(表達式){
	循環(huán)語句;
}

? 注:在循環(huán)語句中break的作用是停止后期所有循環(huán),continue的作用是終止本次循環(huán),開始下一次循環(huán)的判斷。

? 2、for語句

for(表達式1;表達式2;表達式3){
	循環(huán)語句;
}

? 表達式1為初始化部分,用于初始化循環(huán)變量,當表達式1為空時直接進入循環(huán);

? 表達式2 為條件判斷部分,用于判斷循環(huán)是否終止,當表達式2為空時為死循環(huán);

? 表達式3為調整部分,用于循環(huán)條件的調整 。

? 注:建議使用“前閉后開”來限定區(qū)間范圍。

for(i=0; i<10; i++){
	a[i]=i;
}

? 3、do while語句

do{
	循環(huán)語句;
}while(表達式);

? 循環(huán)體至少執(zhí)行一次,while之后記得加分號。

? 二分查找函數循環(huán)實現(xiàn)范例:

int bin_search(int arr[], int left, int right, int key)
{
	int mid = 0;
	while(left<=right){
		mid = (left+right)>>1;
		if(arr[mid]>key)
		{
			right = mid-1;
		}
		else if(arr[mid]< key)
		{
			left = mid+1;
		}
		else
		{
			return mid;//找到了,返回下標
		}
	}
	return -1;//找不到
}
函數 一、庫函數

? C語言基礎庫中的函數,在添加頭文件后可直接調用。

二、自定義函數 1、函數組成

? 由函數名、返回值類型、函數參數以及函數體組成。

? 實參:真實的傳入函數的變量,**在被調用時會發(fā)生臨時拷貝,并非把原來的變量直接放入函數中,**只是把實參的數據拷貝給形參。

? 形參:函數名括號后的變量,因為形參只有在被調用的時候才被實例化并分配空間(形參實例化),**在被調用過后即被銷毀,只在該函數中有效(局部變量),**所以叫形參。

? 函數聲明,要滿足先聲明后使用的原則,由返回值類型、函數名與函數參數組成(需要加分號), 當我們用到很多函數聲明的時候,為了方便我們的調用,我們可以創(chuàng)建一個頭文件.h(比如test.h),將函數聲明放在頭文件當中 ,在寫頭文件時,要注意加上#pragma once 。

//函數定義
double	Add(double x, double y){
	return x+y;
}
//函數聲明
double 	Add(double x, double y);
2、函數調用

? 分為傳值調用與傳址調用,其中傳址調用是把函數外部創(chuàng)建的內存地址傳遞給函數,可以真正與原值建立起聯(lián)系,直接操縱函數外部的變量。

? 函數也可以進行嵌套調用以及鏈式訪問。

? 嵌套調用樣例:

#includevoid new_line()
{
	printf("hehe\n");
}
void three_line()
{
	int i = 0;
	for(i=0; i<3; i++)
	{
		new_line();
	}
}
int main()
{
	three_line();
	return 0;
}

? 鏈式訪問樣例:

#include#includeint main()
{
	char arr[20] = "hello";
	int ret = strlen(strcat(arr,"bit"));
	printf("%d\n", ret);
	return 0;
}
3、函數遞歸

? 程序自身調用被稱為遞歸,把復雜問題層層轉化為與原問題類似的小問題,函數在調用自身的情況下存在不合法遞歸(即無限次的遞歸導致棧溢出)。

? 所以在使用遞歸的時候一定要有遞歸出口,否則會陷入死循環(huán)導致棧溢出!

? **注:**棧結構為電腦存儲的一部分,從高地址處向下開辟存儲空間,與用于開辟動態(tài)存儲空間的堆相向開辟(堆為從低地址出向上開辟存儲空間),而函數調用將會形成棧幀,函數返回后自動釋放棧幀結構,在此過程中,該函數定義的所有局部變量都在該函數的棧幀內進行空間開辟。

樣例:求n的階乘

int factorial(int n)
{
	if(n<= 1)
		return 1;
	else
		return n* factorial(n-1);
}

? 遞歸與迭代

? 遞歸在使用過程中由于頻繁進行函數調用,且每次調用都需要時間成本以及空間成本,所以遞歸程序簡單,但是可能導致遞歸效率變低的問題,而迭代方案通過對變量值進行替換所以不會造成棧溢出,解決了效率低下的問題。

? 樣例(求斐波那契數列第n個的值):

//遞歸實現(xiàn)
int fibrec(int n){
	if(n<=2) retuen 1;
	else return fibrec(n-1)+fibrec(n-2);
}
//迭代實現(xiàn)
int fibite(int n){
	int fir=1,sec=1,thd=2;
	if(n<=2) return 1;
	else{
		while(n>2){
			fir=sec;
			sec=thd;
			thd=fir+sec;
			n--;
		}
		return thd;
	}
}
數組 一、一維數組的創(chuàng)建與初始化

? 創(chuàng)建數組時數組空間為整體開辟整體釋放,在內存中是連續(xù)存放,在定義時就已經確定數組大?。ㄏ聵瞬豢蔀?),且不可被整體賦值。在數組的創(chuàng)建過程中,如果進行了初始化則可不指定數組的大小,多維數組按照一維數組進行理解。

? 數組傳參發(fā)生降維,降維成指向其(數組)內部元素類型的指針。

? 數組名一般情況下都指的是首元素的地址,但如果sizeof()單獨出現(xiàn)以及&后跟數組名時表示的是整個數組

int s[5];
//表示數組首元素地址
printf("%d\n", sizeof(s+1));//結果為4/8,指針的具體大小根據編譯器的不同大小不同
//表示整個數組
printf("%d\n", sizeof(s));//結果為20
二、數組傳參(函數)

? 由于在傳參過程中如果拷貝整個指針會導致效率大大降低甚至導致棧溢出,所以數組傳參要發(fā)降維問題,函數內數組作為參數時,實參為首元素地址,形參為指針。

? 在訪問結構體成員時也同樣要發(fā)生類似的操作,用指向結構體的指針來指代結構體。

typedef struct node{
 int a;
 int b;
}point;

void pop(int* p){
	
}

int main(){
	point a;
	int* p=a;
	pop(p);
	return 0;
}

? 傳參樣例:

//用數組的形式傳遞參數,不需要指定參數的大小,傳的只是數組首元素的地址。
void test(int arr[])
{}

//也可以傳入數組大小
void test(int arr[10])
{}

//數組降維,用指針進行接收,傳的是數組首元素的地址
void test(int *arr)
{}

//二維數組傳參,第一個方括號可以空,但是第二個不可以空
void test(int arr[][5])
{}

void test(int arr[4][5])
{}

//傳過去的是二維數組的數組名,即數組首元素的地址,也就是第一行的地址,第一行也是個數組,用一個數組指針接收(比較少用)
void test(int (*arr)[5])
{}
三、字符數組
char a[]={'a','x','d'};
//此處由于結尾沒有'\0',strlen的機制是遇到'\0'即停止,所以在結尾沒有'\0'時為隨機數
//strlen(a)為隨機數
//sizeof(a)為3

char a[]={'a','x','d','\0'};
//strlen(a)為3
//sizeof(a)為4

char* a="axd";//或char a[]="axd"
//直接通過""定義字符串時,會自動在結尾補'\0',不需要自行補充,但'\0'依舊會占據一個字節(jié)
//strlen(a)為3
//sizeof(a)為4


char c[5]={'a', 'b', '\0', 'c', '\0'};
printf("%s", c);//結果為ab,因為字符串結束標志位'\0'
操作符 一、運算優(yōu)先級

? 注:①++/–高于解引用;

? ②解引用高于±*/

? ③±*/高于位運算;

? ④位運算高于+=、-=、/=、*=;

%操作兩邊必須是整數。

二、二進制中的操作符 1、位運算基本介紹

與運算:&

? 同1則1,否則為0;

或運算:|

? 同0為0,否則為1

非運算:~

? 1取0 0 取1

異或運算:^

? 兩者相等為0,不等為1

移位運算操作符:<< 左移 ; >>右移

? ①**<<左移:**左邊拋棄末尾補0;負數對反碼的補碼進行移位操作;相當于乘2;

? ②**>>右移:有符號的補符號位**,無符號的補0;相當于除以2。

2、反碼與補碼

? **反碼:**正數的反碼為原碼本身,負數反碼符號位不變,剩余的數字位取反;

? **補碼:**正數的補碼為原碼本身,負數的補碼為反碼+1 。

三、隱式類型轉換

? 隱式類型轉換的原因:參與計算的數據如果類型不同無法直接進行計算。

? 整型提升:有符號的補符號位,無符號的補0(符號位為最外面的那位)

? 樣例:

在這里插入圖片描述

? 例題,求循環(huán)次數

在這里插入圖片描述

? 解答:

? unsigned char 8位數據位,范圍在0-255,所以-2(11111110)時,變成254;同理-1(11111111)時,變成255;最后減到0時,不滿足循環(huán)條件,for停止。剛好173次。 (7 4 1 ==>共(7-1)/3+1=3次,1-3=-2,即254,繼續(xù)循環(huán))
254 251 … 5 2 ==>共(254-2)/3+1=85次(2-3=-1,即255,繼續(xù)循環(huán))
255 252 … 6 3 ==>共(255-5)/3+1=85次(3-3=0,退出循環(huán)) 所以總共173次

指針

? 指針變量是個變量,指針本身是個地址,用于存放內存單元的地址。

? 指針時用來存放地址的,指針類型的變量無論指向目標是什么類型,指針本身在32位平臺所占大小都為4個字節(jié),在64位平臺是8個字節(jié) 。

#includeint main()
{
	int a = 10;//在內存中開辟一塊空間,左值為空間,右值為內容
	int *p = &a;//type* p
				//這里我們對變量a,取出它的地址,可以使用&操作符。
				//將a的地址存放在p變量中,p就是一個之指針變量。
	return 0;
}
一、指針的解引用

? 1、對指針的解引用只能看到sizeof(type)個字節(jié)的數據;

? 2、按字節(jié)為單位,數據有高權值和低權值之分,地址有低地址和高地址之分;

? 3、數據低權值位在低地址處即為小端存儲,反之則為大端存儲。
(“小小小”)

二、野指針 概念

? 指向的位置是不可知的指針。

規(guī)避

? 1、指針在定義時就進行初始化;

? 2、避免指針越界(eg:注意循環(huán)時循環(huán)次數的限制);

? 3、指針使用完即進行指針指向空間釋放;

? 4、避免返回局部變量的地址;

? 5、指針使用前檢查其有效性。

三、指針運算

? 1、指針±整數,等價于±sizeof(type);

? 2、指針-指針,兩指針必須同一類型,一般用于數組與字符串求其兩地址間相隔的單元格數,否則無效(指針+指針為非法操作);

? 3、指針的關系運算。

? 4、指針和數組都可以用中括號或者解引用(二者互通)。

四、字符指針 1、字符指針

在指針的類型中我們知道有一種指針類型為字符指針 char* ;

一般使用方法

int main()
{
	char ch = 'w';
	char *pc = &ch;
	*pc = 'w';
	return 0;
}

用char*指針指向字符串

int main()
{
	const char* pstr = "hello bit.";
	printf("%s\n", pstr);
	return 0;
}
//上述代碼中把字符串hello bit.的首地址放入了指針中

需注意字符串可以以字符數組的形式給出,但是此時的字符數組附有存儲功能,而字符指針具有常量屬性,指向的是常量區(qū)的內容,因此不可被修改,可以寫作:

const char* str="hello world";//從上圖表示不可被修改

? 也正因為這個原因 C/C++會把常量字符串存儲到單獨的一個內存區(qū)域,當幾個指針指向同一個字符串的時候,他們實際會指向同一塊內存。但是用相同的常量字符串去初始化不同的數組的時候就會開辟出不同的內存塊。

#includeint main()
{
	char str1[] = "hello world.";
	char str2[] = "hello world.";
	const char *str3 = "hello world.";
	const char *str4 = "hello world.";
	if(str1 ==str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");
	if(str3 ==str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");
	return 0;
}

在這里插入圖片描述

2、const知識點

? 在此說一下const的一些知識點:

? ① const修飾的變量不能被直接修改,但是可以通過指針在進行類型強轉來修改(只是可以但是完全沒必要);

? ② const修飾指針,表示不可以通過指針來修改所指目標;

? ③ const能用則用,會很好的保護數據,

? const的作用:

? ① 寫給編譯器看,提前發(fā)現(xiàn)可能錯誤的修改;

? ② 寫給程序員看,提示該變量不建議修改。

const int* p=&a;
*p = 20; //錯誤,*p所指的值不可以修改
p = &n;//正確,*p的指向可以修改

int* const q = &m;
*q = 20;//正確,此時const修飾的是q,此時q所指向的值可以進行修改
q = &t;//錯誤,由于const修飾的是q,此時的q的指向不可以進行修改

const int a=10; //若const a=10,編譯器也會默認為a是int類型的
int *P=(int*)&a; //注意需要強制&a前需要加int*類型強制類型轉換(&a的原本類型為const int*)
*P=12;
五、指針數組

指針數組本質上是數組,該類數組內存放的的元素是指針。

int* arr1[10]; //整形指針的數組
char *arr2[4]; //一級字符指針的數組
char **arr3[5];//二級字符指針的數組
六、數組指針 1、數組指針定義

指針數組本質上是指針,該類指針指向的是一個數組。

example:

int (*p)[10];
//解釋:p先和*結合,說明p是一個指針變量,然后指著指向的是一個大小為10個整型的數組。所以p是一個
指針,指向一個數組,叫數組指針
2、數組指針&數組

首先需要看的是數組名與&數組名(可以等價于數組指針)之間的關系

#includeint main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("&arr= %p\n", &arr);
	printf("arr+1 = %p\n", arr+1);
	printf("&arr+1= %p\n", &arr+1);
	return 0;
}

在這里插入圖片描述

根據上面的代碼我們發(fā)現(xiàn),其實&arr和arr,雖然值是一樣的,但是意義應該不一樣的。實際上: &arr 表示的是數組的地址,而不是數組首元素的地址。(細細體會一下)本例中 &arr 的類型是: int(*)[10] ,是一種數組指針類型數組的地址+1,跳過整個數組的大小,所以 &arr+1 相對于 &arr 的差值是40

七、數組傳參,指針傳參 1、一維數組傳參

數組傳參會發(fā)生降維,最終傳入的是首元素的地址(指針),并利用此來訪問數組內其他元素。

#includevoid test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int *arr)//ok
{}
void test2(int *arr[20])//不ok
{}
void test2(int **arr)//不ok
{}
int main()
{
	int arr[10] = {0};
	int *arr2[20] = {0};
	test(arr);
	test2(arr2);
}
2、二維數組傳參

二維數組傳參,函數形參的設計只能省略第一個[]的數字。
因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。

void test(int arr[3][5])//ok
{}
void test(int arr[][])//不ok
{}
void test(int arr[][5])//ok
{}
void test(int *arr)//不ok
{}
void test(int (*arr)[5])//ok
{}
void test(int* arr[5])//不ok
{}
void test(int **arr)//ok
{}
int main()
{
	int arr[3][5] = {0};
	test(arr);
}
3、一級指針傳參

需要傳入一個地址

#includevoid print(int *p, int size)
{
	int i = 0;
	for(i=0; i
4、二級指針傳參
#includevoid test(int** ptr)
{
	printf("num = %d\n", **ptr);
}
int main()
{
	int n = 10;
	int*p = &n;
	int **pp = &p;
	test(pp);
	test(&p);
	return 0;
}
八、函數指針

1、函數指針定義

? 函數指針是指指向函數而非指向對象的指針。像其他指針一樣,函數指針也指向某個特定的類型(特定的函數類型)。函數類型由其返回類型以及形參表確定,而與函數名無關 。

? 代碼在電腦中同樣占據內存空間,所以具有存儲地址,而代碼部分在電腦中也是不可被修改的類似字符串常量。

? 在函數中,函數名單獨時即為函數的地址(eg:main=&main),所以在用指針調用函數時,可以直接用指針調用不需要加*

Type (*pFunc)(datatype args);

    //pFunc為函數指針,Type為數據類型,參數(datatype args)可以有多個,也可以沒有。

使用示例

bool max(int a, int b)
{
	if (a>b)
	{
		return a;
	}else{
		return b;
	}
}
 
void Test()
{
	bool (*pFunc)(int, double);
	pFunc = max;
	cout<< max(5, 10)<< endl;
}
九、函數指針數組

指針指向一個數組 ,數組的元素都是函數指針

使用方法: 把幾個相同類型的函數地址放到一個數組中,這個數組就是函數指針的數組。

十、回調函數

解釋:調用庫中函數,但是庫中函數需要編寫程序時編寫一個調用函數,該庫中的函數為回調函數。也就是說一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。

? 回調函數必須在中間函數以及回調函數同時具備時才可以實現(xiàn)。

作者:no.body
鏈接:https://www.zhihu.com/question/19801131/answer/27459821
來源:知乎
//大佬在這個帖子里對于概念解釋的很好,可以做參考!

回調函數就是回調函數不是由該函數的實現(xiàn)方直接調用,而是在特定的事件或條件發(fā)生時由另外的一方調用的,用于對該事件或條件進行響應。

結構體 1、基本定義

? **注:**①結構體不可以自引用!但可以在結構體內定義該結構體類型的指針!

? ②定義結構體本質是新增一種類型;

? ③結構體傳參要傳結構體地址(指針),以此提高效率。

struct node1{
	int  a;
	int  b;
};

typedef struct node2 {
	int c;
	int d;
}node2;//通過typedef為結構體變量定義一個別名node2,在以后使用中可以使用別名以此提高編寫效率

int main(){
	struct node1 s;//用結構體定義變量
	node2  q;//用別名定義變量
}

? **注:**結構體不可以自引用!但可以在結構體內定義該結構體類型的指針!

struct Node
{
	int data;
	struct Node next;
};//錯誤

struct Node
{
	int data;
	struct Node* next;
};//正確

typedef struct Node
{
	int data;
	struct Node* next;
}Node;//正確
2、結構體變量的定義和初始化
struct Point1
{
	int x;
	int y;
}p1; //聲明類型的同時定義變量p1
struct Point p2; //定義結構體變量p2

typedef struct Point2
{
	int x;
	int y;
}p;
p p1;
p p2;

在定義結構體變量時,可以在初始化的部分定義其內容,也可以在之后定義。

typedef struct Point2
{
	int x;
	int y;
}p;
p p1;
p1.x=1;
p1.y=2;//可以直接用結構體類型的變量進行定義

typedef struct Point2
{
	int x;
	int y;
}p;
p* p2=(p*)malloc(sizeof(p));
p2->x=1;
p2->y=2;//定義一個指向結構體的指針并為其分配空間即可進行定義
3、結構體的內存對齊(結構體的占用大小的計算)

結構體內存空間占用的大小并不是單純的元素相加,而是通過浪費一定量的空間來換取目標數據讀取速度的加快

計算方式:

① 第一個成員在與結構體變量偏移量為0的地址處。

② 其他成員變量要對齊到某個數字(對齊數)的整數倍的地址處。

? (起始偏移量要能整除該成員的對齊數)

對齊數 = 編譯器默認的一個對齊數 與 該成員大小的較小值。

? VS中默認的值為8

③ 結構體總大小為大對齊數(每個成員變量都有一個對齊數)的整數倍。

④ 如果嵌套了結構體的情況,嵌套的結構體對齊到自己的大對齊數的整數倍處(即結構體大小),結構體的整

體大小就是所有大對齊數(含嵌套結構體的對齊數)的整數倍。

樣例:

struct S1
{
    char a;
    int b;
    char C;
};
printf("%d\n", sizeof(struct S1));

char 為1個字節(jié), int 為4個字節(jié);
char a 從0偏移開始,占用一個字節(jié);偏移量為1,接下來存放 int b,偏移量1不是對齊數4 的整數倍,所以向后繼續(xù)偏移一直到4,4是對齊數4的整數倍,所以將int b存放在偏移地址為4處,占用4個字節(jié);偏移量變?yōu)?,存放 char c ,占一個字節(jié),偏移量9不是大對齊數4的整數倍,所以向后繼續(xù)偏移直到偏移處為12的地方。

圖示如下:

在這里插入圖片描述

自主設置默認對齊數

#pragma pack(a)//通過該指令可設置默認對齊數為a
位斷

位段的聲明和結構是類似的,有兩個不同:
1.位段的成員必須是 int、unsigned int 、signed int或者char(同屬于整型家族);
2.位段的成員名后邊有一個冒號和一個數字。

struct A
{
int _a:2;
int _b:5;
int _c:10;
int _d:30;
};

注: ① 位段的成員可以是 int unsigned int signed int 或者是 char (屬于整形家族)類型;

② 位段的空間上是按照需要以4個字節(jié)( int )或者1個字節(jié)( char )的方式來開辟的;

③ 位段涉及很多不確定因素,位段是不跨平臺的,注重可移植的程序應該避免使用位段;

④位斷不需要考慮內存對齊問題所以較為節(jié)省空間。

總而言之, 跟結構相比,位段可以達到同樣的效果,但是可以很好的節(jié)省空間,但是有跨平臺的問題存在。

枚舉

即一一列舉

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun   //最后一個不加逗號
};
enum Sex//性別
{
	MALE,
	FEMALE,
	SECRET
};

與宏定義相比枚舉的優(yōu)點:
① 增加代碼的可讀性和可維護性;
② 和#define定義的標識符比較枚舉有類型檢查,更加嚴謹;
③ 防止了命名污染(封裝);
④ 便于調試;
⑤ 使用方便,一次可以定義多個常量 。

聯(lián)合體

聯(lián)合也是一種特殊的自定義類型
這種類型定義的變量也包含一系列的成員,特征是這些成員公用同一塊空間(所以聯(lián)合也叫共用體), 聯(lián)合的成員是共用同一塊內存空間的,這樣一個聯(lián)合變量的大小,至少是大成員的大?。ㄒ驗槁?lián)合至少得有能力保存大的那個成員)。

//聯(lián)合類型的聲明
union Un
{
	char c;
	int i;
};
//聯(lián)合變量的定義
union Un un;

**注:**聯(lián)合體需要考慮內存對齊,要求為大內存數的整數倍。

動態(tài)內存管理

? 程序開始運行后在堆上開辟大量空間(數組之類的空間開辟在棧上進行),而在堆上開辟的空間使用完畢后需要在使用完成后由free函數進行釋放,然后令指向該空間的指針指空,如果只申請不釋放會造成內存泄漏問題。

動態(tài)申請空間主要涉及三個函數:malloc函數,calloc函數,relloc函數。

void* malloc (size_t size);

? 只申請空間,不對空間進行初始化,傳入的參數size為要開辟的空間大小;

void* calloc (size_t num, size_t size);

? 申請空間,與malloc唯一的不同之處在于calloc會初始化為0,傳入的參數size為單個空間的大小,參數a為所需要的單個空間的數量;

void* realloc (void* ptr, size_t size);

將分配size個大小的空間,然后在調整原內存空間大小的基礎上,將原來內存中的數據移動到新的空間,返回值為調整之后的內存起始位置。

由于realloc可能會申請失敗返回NULL所以不建議直接用原指針接收返回地址,正確使用方法為:

int* ptr = (int*)malloc(100);
int* p = NULL;
p = realloc(ptr, 1000);
if (p = !NULL) {
	ptr = p;
}

內存釋放操作

int* p=(int*)malloc(100);
......
free(p);
p = NULL;
柔性數組 1、定義

? 在結構體內大小為0(a[0])或空(a[])的數組(必須為結構體內最后一個元素且不能是唯一元素)

這樣可以在結構體內具有一個變長數組包含柔性數組成員的結構用malloc ()函數進行內存的動態(tài)分配,并且分配

的內存應該大于結構的大小,以適應柔性數組的預期大小 ,sizeof 返回的這種結構大小不包括柔性數組的內存 。

2、使用方法
typedef struct st_type
{
	int i;
	int a[0];//柔性數組成員
}type_a;
printf("%d\n", sizeof(type_a));//輸出的是4
//初始化
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
p->i = 100;
for(i=0; i<100; i++)
{
	p->a[i] = i;
}
free(p);

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

新聞標題:C語言知識點匯總-創(chuàng)新互聯(lián)
網頁鏈接:http://muchs.cn/article36/csjppg.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供全網營銷推廣、網站制作、動態(tài)網站、網站排名網站建設、建站公司

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)