c-指標進階篇

栗子飛啊飛發表於2020-10-22

指標函式

指標函式:
1.引數是指標
    1.1 在子函式中,修改普通的實參的值,傳的是普通實參的地址(一級指標),在函式中修改是*一級指標
    1.2 在子函式總,修改指標的實參的值,傳的是指標實參的地址(二級指標),在子函式中修改是*二級指標

指標最基本的使用

void modify(int* a)	//int *a=#  賦值操作,形參,實參型別一致
{
	*a = 1001;	//*一級指標
}    //在子函式中,修改普通的實參的值,傳的是普通實參的地址(一級指標),在子函式中修改是*一級指標
int main() 
{
	int num = 1;
	modify(&num);		
	printf("%d\n", num);
	int aa = 1;
	int bb = 1001;
	return 0;
}
在子函式中,修改的是*a

在這裡插入圖片描述
交換a,b的值,使其可以在主函式當中輸出

void Swap(int* a, int* b) //交換a,b的值,使其可以在主函式當中輸出
{
	int temp = *a;
	*a = *b;
	*b = temp;
}   //子函式當中修改了函式的值
int main()
{
    int aa = 1;
	int bb = 1001;
	Swap(&aa, &bb);  //傳參就係要傳實參的地址
	printf("a=%d,b=%d\n", aa, bb);
    return 0;    
}

在子函式總,修改指標的實參的值,傳的是指標實參的地址(二級指標),在子函式中修改是*二級指標

int g_num = 1001;
void modifyPoint(int** p)  //在當前函式中修改指標的指向,由p指向num改為p指向g_num
{    //在子函式總,修改指標的實參的值,傳的是指標實參的地址(二級指標),在子函式中修改是*二級指標
	*p = &g_num;  //修改p的指向需要p的地址,就要傳二級指標**p
}

int main()
{
    num = 1111;
	int* p = #
	modifyPoint(&p);
	printf("%d\n", *p);
	returnPremer2(p);
    return 0;    
}
2.反回值是指標
	2.1 不能返回區域性變數地址
    2.2 規定用int型別的返回變數,int*型別的返回變數的地址,int返回變數
int* returnNum() //合法
{
	return &g_num;
}
//int a 是區域性變數 不能返回區域性變數的地址
int* returnPremer(int a)   //1
{
	return &a;  //return 1對 return &1錯
}
int* returnPremer2(int* a)  //傳入的是指標變數的值,不是區域性變數 
{		
	return  a;				//所以返回指標變數的值  
}

陣列中

//在陣列中
void  initArray(int array[], int arrayNum)   //傳陣列的專用寫法
{
	for (int i = 0; i < arrayNum; i++)
	{
		array[i] = i;
	}
}                           
void initArray2(int* array, int arrayNum)   //傳指標或地址的寫法
                                            //int array[],int* array兩種寫法等效 
{                                           //相當於傳一級指標
	for (int i = 0; i< arrayNum; i++)//array 等效於&array[0](第一的元素的地址)  int *array
	{
		array[i] = i;
	}
}

字串函式(string.h)

int  strcmp(char const* _Str1, char const* _Str2);//字串比較函式
                                                  //str1>str2 返回值大於0  1
	                                              //str1==str2  0
	                                              //str1<str2  -1
strlen(char const* _Str);                    //計算字串可見長度
strcat(str,str);                                    //字串連線
strcpy(str,str);                                    //字串拷貝 

#include <string.h>  //字串處理標頭檔案
#include <stdio.h>
int main() 
{
	//int  strcmp(char const* _Str1, char const* _Str2);
	//str1>str2 返回值大於0  1
	//str1==str2  0
	//str1<str2  -1
	char str1[] = "ABC";
	char str2[] = "ACB";
	char str3[] = "ACB";
	//mystrcmp
	if (strcmp(str1, str2) > 0) //
	{
		printf("str1>str2\n");
	}
	if (strcmp(str2, str3) == 0) 
	{
		printf("str2==str3\n");
	}
	//size_t 等效 unsigned int
	//size_t  strlen(char const* _Str);
	printf("%d\n", strlen(str1));
	int size = strlen(str1) + 1;  // +\0
	//字串連線 strcat
	char strInfo[1024] = "ILoveyou";
	strcat(strInfo, str1);  //字串必須要空間足夠,前面的字串必須大於兩個字串的總長度
	puts(strInfo);            //列印字串
	//字串拷貝 strcpy
	//C語言中操作字串不能直接賦值 
	char strA[] = "ILoveLOUU";
	char strB[] = "IMissYou";
	//strA = strB;  不能直接賦值
	strcpy(strA, strB);	/*一定要能裝的下,第一個要大於等於第二個,一般用於字串賦值,和
	作用原理,把第一個清空掉在賦值,陣列申請的空間長度空間長度不會改變,裝不下的話會溢位*/
	printf("strA:%s\n", strA);
	printf("strB:%s\n", strB);
	//字串函式都是處理到\0
	char str11[10] = "abc";  
	char str22[12] = "ABCD";
	strcpy(str11, str22);
	puts(str11);
	//不常見的: 
	//1.大寫轉小寫
	char ABC[] = "ABCDd";
	_strlwr(ABC);//'_'是vs對函式做了修改
	puts(ABC);
	//2.小寫轉大寫
	_strupr(ABC);
	puts(ABC);
	return 0;
}

字串賦值溢位時報錯,順便教幾個單詞
溢位情況

動態申請記憶體

1.動態申請記憶體
動態申請的記憶體是堆記憶體,特點 人為申請,手動釋放
size_t 就是 unsigned int  無符號整型的另一個名字(正整數)
void *malloc(size_t size);  使用時需要強制轉換
 (int*)malloc(sizeof(int))
 前面(int*)是: 指標型別				//去掉變數名
 後面是(int): 指標所指向的型別		//去掉變數名和* 你要儲存的資料型別
2.free(void *point);
3. 野指標: 沒有指向的指標
	NULL :通常會給指標一個指向

動態記憶體申請的格式

void *malloc(size_t size);  //void *說明使用malloc函式時需要強制轉換

釋放格式

free(void *point);

只申請一個變數記憶體

//只申請一個變數記憶體
int* pInt = (int*)malloc(sizeof(int));
//指標變成變數,有兩種方式
//1.指向變數的地址
*pInt1001;//*+地址就是一個變數
//int* pVoid = NULL;
//*pVoid=1001;   *pVoid不能被改變,NULL是空,相當於0,是常量,常量不能改變

//2.申請堆記憶體,只要申請了堆記憶體,它就是一個變數

動態記憶體申請的好處

int* malloNum() //所做的操作會被保留
{
	int* p = (int*)malloc(sizeof(int));
	*p = 1002;
	return p;
}

申請一段記憶體: 陣列

    int arrayNum = 0;
	scanf("%d", &arrayNum);
	int* pArray = (int*)malloc(sizeof(int) * arrayNum);	//pArray[5],申請陣列
	for (int i = 0; i < arrayNum; i++) 
	{
		pArray[i] = i;
		printf("%d\t", pArray[i]);
	}
	printf("\n");
	//釋放一定要置空
	free(pInt);//釋放指標
	pInt = NULL;
	free(pArray);
	pArray = NULL;

通過二級指標去理解二維陣列

int** makeArray2D(int row, int cols) 
{	
	//int *array --->array[i] 存放多個整數
	//int **p--->(*p)[i] 存放多個一級指標
	//p[i]---> 存放多個整數
	//二級指標如何變成二維陣列
	int  **p = (int **)malloc(sizeof(int*) * row); //指標型別(int **),
	for (int i = 0; i < row; i++) 
	{
		p[i] = (int*)malloc(sizeof(int) * cols);
	}
	return p;
}
int main()
{
    //通過二級指標去理解二維陣列
	//二級指標如何變成二維陣列
	int** array2D = makeArray2D(2, 2);
	array2D[1][1] = 1001;
	printf("%d\n", array2D[1][1]);
	free(array2D);
	array2D = NULL;
    return 0;
}

釋放問題:在哪裡申請的, 在哪個位置釋放

//釋放問題: 在哪裡申請的, 在哪個位置釋放
	int* pMove = (int*)malloc(sizeof(int) * 3);
	//正常的使用
	pMove[0] = 1;
	pMove[1] = 2;
	pMove[2] = 3;
	//做了指標指向的改變
	pMove++;                //此時pMove指向pMove[1]
	//pMove[i]等效於 *(pMove+i) 指標的運算
	printf("pMove[-1]=%d\n", pMove[-1]);
	//釋放前要還原指標原來的指向
	--pMove;
	free(pMove);//從哪裡申請就要從哪裡釋放
	pMove = NULL;

其他釋放函式

//1
	//malloc申請的記憶體是沒有初始化,存放的是垃圾值
	//void * calloc(size_t count,size_t size); 申請記憶體過程中初始化
	int* pMalloc = (int*)malloc(4 * sizeof(int));
	memset(pMalloc, 0, sizeof(int) * 4);		//設定記憶體每個位元組的值
	int* pCalloc = (int*)calloc(4, sizeof(int));//(4, sizeof(int))相當於(4 * sizeof(int))
	                      //以上三句話中,前兩句話相當於第三句話
	free(pMalloc); //使用完後要釋放
	pMalloc = NULL;//一定要為空,否則成為野指標
	free(pCalloc);
	pCalloc = NULL;
//2
	//為指標重新申請記憶體
	//void *realloc(void *p,size_t size);

	int* pRealloc = (int*)malloc(sizeof(int) * 2);
	pRealloc[0] = 1;
	pRealloc[1] = 2;
	printf("%p\n", pRealloc + 1);
	printf("%p\n", pRealloc + 2);
	pRealloc=(int *)realloc(pRealloc, sizeof(int) * 4);//重新申請的空間一定要比上次的大
	                               //該返回值指向重新申請的空間的首地址
	pRealloc[2] = 3;
	pRealloc[3] = 4;
	printf("%p\n", pRealloc + 1);
	printf("%p\n", pRealloc + 2);
	for (int i = 0; i < 4; i++) 
	{
		printf("%d\t", pRealloc[i]);
	}
	printf("\n");
	free(pRealloc);
	pRealloc = NULL;

指標陣列與陣列指標

1.如何區分指標陣列,陣列指標
   看主謂賓
   整形陣列: 存放的整數的陣列,他是多個整數
   指標陣列: 指標的陣列  --->他是陣列
            存放多個指標的陣列,他是多個指標
   陣列指標: 陣列的指標  --->他是一個指標
2. 功能
   2.1 指標陣列一般是用來操作字串
   2.2 陣列指標-->表示二維陣列
       2.2.1 變成二維陣列
       2.2.2 當做二維陣列的指標使用

指標陣列
一般是用來操作字串

    //指標陣列
	int a = 1;
	int b = 2;
	int c = 3;
	int* pInt[3];//每一個pInt[i]都可以存放一個指標,數字類比較少用
	//pInt[i] 多個一級指標
	pInt[0] = &a;
	pInt[1] = &b;
	pInt[2] = &c;
	const char* p = "ILove";//一級指標可以操作一個字串,所以常用指標陣列操作多個字串
	const char* pStr[3] = {"ILove","IMissyou","wantyou"};
	for (int i = 0; i < 3; i++) 
	{
		puts(pStr[i]);//pStr[i]是一級指標,puts自動換行
		//好處,每個字串的申請空間可以不相同
	}

陣列指標
可以表示二維陣列
2.2.1 變成二維陣列
2.2.2 當做二維陣列的指標使用
傳二維陣列時的一個錯誤做法

void print(int array[][3], int row, int cols)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			printf("%d\t", array[i][j]);
		}
		printf("\n");
	}
}
int main()//真正的二維陣列其實不是二維指標
{
	int array[2][3] = { 1, 2, 3, 4, 5, 6 };
	//“函式”:“int (*)[3]”與“int **”的間接級別不同
	int **p = array;
	print(p, 2, 3);  //按規矩來說其實本身不允許這樣寫
	//c語言中可以執行,但是會有問題,如下圖。但是c++中會報錯
	return 0;
}

當看到有以下問題時,將其當做錯誤程式碼處理
在這裡插入圖片描述

這些都是因為指標的型別不一致,一般情況下陣列與二級指標是有區別的,所以一般情況下我們傳的是陣列指標
正確做法將後半段改為

    int array[2][3] = { 1, 2, 3, 4, 5, 6 };
    //陣列指標
	int (*p)[3] = array;   //這裡做了改變
	//*優先跟p結合在一起,就形成了一個指標,這個指標是指向一個陣列的,這個陣列的長度是三
	print(p, 2, 3);

陣列指標如何申請記憶體
變成二維陣列

    //陣列指標如何申請? 讓他變成二維陣列
	//(指標的型別)malloc(sizeof(指標所指向的型別)*n);
	//指標的型別:去掉變數
	//指標所指向的型別:去掉變數名和*
	int(*pArray)[3] = (int(*)[3])malloc(sizeof(int[3]) * 2);	//等效 int pArray[2][3];
	for (int i = 0; i < 2; i++) 
	{
		for (int j = 0; j < 3; j++) 
		{
			pArray[i][j] = i + j;
			printf("%d\t", pArray[i][j]);
		}
		printf("\n");
	}
	free(pArray);
	pArray = NULL;

相關文章