天天看點

C/C++指針和智能指針的用法為什麼使用指針?指針的定義指向數組的指針指針的通路通路指針所指向的内容空指針指針的算數運算(指針的自增、指針的自減、指針與整數的加減)const 修飾二級指針函數指針指針數組void類型的指針函數指針函數傳回值

C語言指針

  • 為什麼使用指針?
  • 指針的定義
  • 指向數組的指針
  • 指針的通路
  • 通路指針所指向的内容
  • 空指針
      • 1、空指針
      • 2、空指針的使用
      • 壞指針
  • 指針的算數運算(指針的自增、指針的自減、指針與整數的加減)
      • 指針自加
      • 指針自減
      • 指針與整數加減
      • 指針與指針之間的加減運算
  • const 修飾
  • 二級指針
  • 函數指針
      • 函數二級指針
      • 數組和指針的糾纏
  • 指針數組
      • 指針和多元數組
  • void類型的指針
  • 函數指針
  • 函數傳回值

任何類型指針所占空間是4個位元組,在計算機架構中最大的尋址範圍為4個位元組

為什麼使用指針?

前言: 不使用指針也可以建構起軟體,帶着疑問往下看

  • 函數的值傳遞,無法通過調用函數,來修改函數的實參
  • 被調用函數需要提供更多的“傳回值”給調用函數
  • 減少值傳遞時帶來的額外開銷,提高代碼執行效率

指針的定義

(跑了聊和尚跑不了廟:和尚是跑了但是,我們知道寺廟的位址,還是可以去寺廟找到那個和尚)

(買東西寄快遞,隻要給個位址,就可以收到快遞)

#include <stdio.h>
#include <Windows.h>

int main( void )
{
	int age;

	/*
	** 定義了一個指針
	** 指針本身也是一個變量
	** 名稱是p,他是一個指針,可以指向一個整數
	** p的值是一個整數的位址
	*/
	int *p;

	/*
	** p指向了age
	** p的值就是age的位址
	*/
	p = &age;

	/*
	scanf_s("%d", &age);
	*/
	scanf_s("%d", p);

	printf("age:%d \n", age);
	system("pause");
	return 0;
}
           
C/C++指針和智能指針的用法為什麼使用指針?指針的定義指向數組的指針指針的通路通路指針所指向的内容空指針指針的算數運算(指針的自增、指針的自減、指針與整數的加減)const 修飾二級指針函數指針指針數組void類型的指針函數指針函數傳回值
#include <stdio.h>
#include <time.h>
#include <Windows.h>


/*************************************************
** 1、函數的值傳遞,無法通過調用函數,來修改函數的實參
** 2、被調用函數需要提供更多的“傳回值”給調用函數
** 3、減少值傳遞時帶來的額外開銷,提高代碼執行效率
**************************************************/

/*給英雄加血的函數*/
void add_blood1( int blood )
{
	blood += 1000;
}

bool add_blood3( int blood )
{
	if ( blood >= 1000 )
	{
		return false;
	} 
	else
	{
		blood+=1000;
	}
	return true;
}

int add_blood2( int blood )
{
	blood += 1000;
	return blood;
}

/*減少值傳遞時帶來的額外開銷,提高代碼執行效率*/
struct _hexo_stat
{
	int blood; //英雄血量
	int power; //英雄攻擊力
	int level; //英雄級别
	char name[64]; //英雄名字
	char details[1024]; //描述英雄狀态
};

struct _hexo_stat upgrade1(struct _hexo_stat hexo, int type)
{
	switch(type)
	{
		case 1:
			hexo.blood += 1000;
			hexo.power += 30;
			hexo.level++;
		break;

		case 2:
			hexo.blood += 2000;
			hexo.power += 50;
			hexo.level++;
			break;

		default:
			break;
	}
	return (hexo);
}

/*指針版本*/
void upgrade2(struct _hexo_stat *hexo, int type)
{
	switch(type)
	{
	case 1:
		hexo->blood += 1000;
		hexo->power += 30;
		hexo->level++;
		break;

	case 2:
		hexo->blood += 2000;
		hexo->power += 50;
		hexo->level++;
		break;

	default:
		break;
	}
}

int main( void )
{
	time_t start, end;
	struct _hexo_stat hexo_xiaohei;

	strcpy(hexo_xiaohei.name, "小黑");
	hexo_xiaohei.blood = 100;
	hexo_xiaohei.power = 10;
	hexo_xiaohei.level = 1000;

	time(&start); /*1970年1月1日0時0分0秒 至今的秒數*/
	for(int i=0; i < 99999999; i++)
	{
		/*
		upgrade1(hexo_xiaohei, 1);
		*/
		upgrade2(&hexo_xiaohei, 2);
	}
	time(&end);
	printf("小黑的血量:%d \n", hexo_xiaohei.power);
	printf("%d \n", end-start);

	/*英雄小黑本身的血量*/
	//int hexo_xiaohei = 1000;
	/*加血*/
	//add_blood1(hexo_xiaohei);
	//hexo_xiaohei = add_blood3(hexo_xiaohei);
	/*加血後的結果*/
	/*
	printf("hexo_xiaohei is blood :%d \n", hexo_xiaohei);
	*/

	system("pause");
	return 0;
}
           

指向數組的指針

指針的初始化

#include <stdio.h>
#include <Windows.h>

int main( void )
{
	int room = 900;

	int *p1 = &room1;
	int *p2 = &room1;

	printf("room1的位址是:0x%p \n",&room);
	printf("p1的位址是:0x%p \n",&p1);
	printf("p2的位址是:0x%p \n",&p2);

	/*
	** 整型指針,所占位元組數,在32位的系統裡 尋址是 4個位元組數
	** 在64位的系統裡 尋址是 8個位元組數
	*/
	printf("room1所占位元組是:%d \n",sizeof(room));
	printf("p1所占位元組是:%d \n",sizeof(p1));
	printf("p2所占位元組是:%d \n",sizeof(p2));
	
	system("pause");
	return 0;
}
           
C/C++指針和智能指針的用法為什麼使用指針?指針的定義指向數組的指針指針的通路通路指針所指向的内容空指針指針的算數運算(指針的自增、指針的自減、指針與整數的加減)const 修飾二級指針函數指針指針數組void類型的指針函數指針函數傳回值

指針的通路

通路指針

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	/*
	** 指針也是一個變量
	** 1. 通路指針本身
	** 2. 
	** 通路(讀、寫)指針本身的值,和其他普通變量的通路方式相同
	** 
	**
	*/
	int room = 2;

	int *p1 = &room;
	int *p2 = p1;
	printf("room的位址:%d\n",&room);
	printf("p1的值:%d p2的值:%d\n", p1, p2);

	int *p3 = p1;
	printf("p3的值:%d\n",p3);

	p3 = &room;
	printf("p3的值:%d room的位址:%d \n", p3, &room);

	/*
	** 使用16進制列印,把位址當成一個無符号數來處理
	** 
	*/
	printf("p1=0x%p\n", p1);
	printf("p1=0x%x\n", p1);
	printf("p1=0x%X\n", p1);


	system("pause");
	return 0;
}
           

通路指針所指向的内容

1、間接通路符(解引符)“ * ” 是一個特殊的運算符,girl表示讀取指針girl所指向的變量

** 2、帶個 “*” 就等同于間接地通路這個位址指向變量的值(的内容)*

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	/*
	** 指針也是一個變量
	** 1. 通路指針本身
	** 2. 
	** 通路(讀、寫)指針本身的值,和其他普通變量的通路方式相同
	** 
	**
	*/
	int room = 2;
	
	int * girl = &room;

	room = 3;
	int x;
	x = *girl; /*  間接通路符(解引符)“ * ” 是一個特殊的運算符,*girl表示讀取指針girl所指向的變量*/

	printf("x:%d\n",x);

	*girl = 4;

	printf("room:%d  *girl:%d\n", room, *girl);
	/* 帶個 "*" 就等同于間接地通路這個位址指向變量的值(的内容)*/

	system("pause");
	return 0;
}
           

空指針

1、空指針

空指針就是值為,0 的指針。(任何資料都不會儲存在位址為0的記憶體中,它是作業系統預留的記憶體塊)

int p = 0;

或者

int p = NULL; //建議使用這種

2、空指針的使用

1、指針初始化為空指針

int *select = NULL;

目的是避免資料非法通路

2、指針不再使用時,可以設定為空指針

int *select = &pointer;

//不再使用了

select = NULL;

3、表示這個指針還沒有具體的指向,使用前進行合法性的判斷

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	//int *p = NULL;
	//if(p){ //p等同于p!=NULL
		//指針不為空,對指針進行操作
	//}
	int *p = NULL;
	int pointer_p;
	p = &pointer_p;

	if(p!=NULL)
	{
		printf(" *p的值不為空 \n");
	}

	system("pause");
	return 0;
}

           

壞指針

     int *select; //指針沒有初始化

形式一

    printf(“選擇的房間是:%d\n”, *select);

形式二

    select = 100;

    printf(“選擇的房間是:%d\n”,select);

指針的算數運算(指針的自增、指針的自減、指針與整數的加減)

指針自加

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	int age[] = {21, 15, 18, 14, 23, 28, 10};
	int len = sizeof(age)/sizeof(age[0]); /*計算age數組的長度*/

	for( int i = 0; i < len; i++ )
	{
		printf("第%d學員的年齡是:%d\n", i+1, age[i]);
	}

	/*
	** 數組本身的位址和數組第一個元素的位址是相同的
	** 1、列印數組的位址
	** 2、列印數組第一個元素的位址
	*/

	printf("age的位址:%d age[0]第一個元素的位址:%p\n",age, &age[0]);

	int *p = age;
	/*通路第一個元素*/
	printf("數組的第一個元素:%d\n", *p);

	/*通路第二個元素*/
	//p = p+1; /* p = p + 1*(sizeof(int)) */
	//printf("數組的第二個元素:%d p的位址是:0x%p\n", *p, p);

	for( int i = 0; i < len; i++ )
	{
		printf("第%d學院的年齡:%d 位址:0x%p \n",i+1, *p, p);
		p++;
	}

	printf("---------------------------\n");

	char ch[4] = {'a', 'b', 'c', 'd'};
	int len_ch = sizeof(ch)/sizeof(char);
	char *cp = ch;

	for(int i = 0; i < len_ch; i++)
	{
		printf("第%d個元素的值:%c 位址是:0x%p\n",
			i+1, *cp, cp);
		cp++;
	}

	system("pause");
	return 0;
}
           

指針自減

輸入字元串,逆轉輸出

#include <stdio.h>
#include <string>
#include <Windows.h>

/*
** 輸入字元串進行,逆轉輸出
*/


int main(void)
{
	char input[128];
	int len;
	char tmp;

	scanf_s("%s", input, 128);
	len = strlen(input);

	for( int i = 0; i < len/2; i++)
	{
		tmp = input[0];
		input[i] = input[len-i-1];
		input[len-i-1] = tmp;
	}
	/*第一種方法*/
	for( int i = 0; i < len; i++)
	{
		printf("%c", input[i]);
	}
	printf("\n");

	printf("逆轉後:%s", input);


	/*第二種方法*/
	for( int i = 0; i < len; i++)
	{
		printf("%c", input[len-i-1]);
	}

	printf("\n");

	/*第三種方法*/
	char *p = &input[len-1];

	for( int i = 0; i < len; i++)
	{
		printf("%c", *p--);
		//p--;
	}

	printf("\n");

	system("pause");
	return 0;
}
           

指針與整數加減

指針與整數的加減在數組中,在數組元素中表示元素向前移動一位或向後移動一位(資料類型 (int) p基位址 + n * sizeof(資料類型) )

#include <stdio.h>
#include <string>
#include <Windows.h>

int main(void)
{
	int ages[] = {10,2,3,4,5,61,90,8,9,10};
	int len = sizeof(ages) / sizeof(ages[0]);

	int *p1 = ages;

	printf("第一個人的年齡:%d\n", *p1 + 6); /* 優先級(*p)+6  */
	printf("第二個人的年齡:%d\n", *(p1 + 6));

	int *p2 = &ages[4];

	printf("第二個人前一個人的年齡:%d\n",*(p2 - 1));
	printf("第二個人前三個人的年齡:%d\n",*(p2 - 3));

	system("pause");
	return 0;
}
           

指針與指針之間的加減運算

指針與指針之間不能進行相加操作?

#include <stdio.h>
#include <string>
#include <Windows.h>

int main(void)
{
	int ages[] = {10,2,3,4,5,6,4,8,9,10};
	int len = sizeof(ages) / sizeof(ages[0]);

	int *xiao_cheng = ages+6;
	int *xiao_zhang = ages+7;

	printf("xiao_zhang - xiao_cheng = %d\n",
		*xiao_zhang - *xiao_cheng);
	printf("xiao_zhang - xiao_cheng = %d\n",
		xiao_zhang - xiao_cheng);

	/*//指針之間不能相加
	printf("xiao_zhang + xiao_cheng = %d\n",
		xiao_zhang + xiao_cheng);
	*/

	system("pause");
	return 0;
}
           

const 修飾

const 關鍵字 隻讀。const 離類型(int) 近,還是離指針變量名近,離誰近,就修飾誰,誰就不能變

#include <stdio.h>
#include <string>
#include <Windows.h>

/*
** const 關鍵字 隻讀
** const 離類型(int) 近,還是離指針變量名近,離誰近,就修飾誰,誰就不能變
*/
int main(void)
{
	int wife = 24;
	int girl = 18;

	/*
	**  渣男 随心所欲 沒有什麼限制
	*/

	int * zha_man = &wife;
	* zha_man = 29;
	zha_man = &girl;
	* zha_man = 19;

	printf("wife:%d girl:%d\n", wife, girl);

	/*
	** 直男 以自我為中心
	*/
	
	/* const修飾後 使用指針通路時 隻能讀 不能修改 */
	
	/* const int * zhi_man = &wife; */  //第一種 寫法
	int const * zhi_man = &wife;	  //第二種 寫法
	
	/* 
	//不能給常量指派
	* zhi_man = 20;
	*/

	zhi_man = &girl;

	printf("wife:%d  girl:%d\n", wife, girl);

	/*
	** 暖男 終此一生
	*/

	int * const nuan_man = &wife;
	/* nuan_man = &girl; */

	printf("wife: %d\n",wife);

	/*
	** 超級暖男 一切宜順老婆 聽老婆的
	*/
	//既不能修改 所指向的 位址,也不能修改,裡面的值
	const int * const super_nuan_man = &wife;
	printf("wife:%d\n",wife);

	system("pause");
	return 0;
}
           

二級指針

一級指針:裡儲存的是普通變量的位址

二級指針:裡儲存的是另一個指針變量的位址

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	int guizi2 = 888;		  //存手槍的第二個櫃子
	int * guizi1 = &guizi2;	  //存第二個櫃子位址的 第一個櫃子
	int ** liu_jian = &guizi1;//手握第一個櫃子位址的 劉健

	printf("劉健手握第一個櫃子的位址:0x%p\n",*liu_jian);
	printf("劉健從第一個櫃子拿到第二個櫃子的位址:0x%p\n",&guizi2);

	int *tmp;
	tmp = &guizi2;

	//printf("劉健從第一個櫃子拿到第二個櫃子的位址,打開櫃子取出槍:%d\n",*(*liu_jian));
	printf("劉健從第一個櫃子拿到第二個櫃子的位址,打開櫃子取出槍:%d\n",*tmp);

	system("pause");
	return 0;
}
           
C/C++指針和智能指針的用法為什麼使用指針?指針的定義指向數組的指針指針的通路通路指針所指向的内容空指針指針的算數運算(指針的自增、指針的自減、指針與整數的加減)const 修飾二級指針函數指針指針數組void類型的指針函數指針函數傳回值

函數指針

#include <stdio.h>
#include <Windows.h>

/*一級指針*/
void swap(int *a, int *b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int main(void)
{
	int x = 100, y = 1000;
	swap(&x , &y);
	printf("x=%d ,y=%d\n",x,y);

	system("pause");
	return 0;
}
           

函數二級指針

二級指針可以将 參數 帶入函數 ,也可以将函數 裡的參數帶出

#include <stdio.h>
#include <Windows.h>

void boy_home(int **meipo) //局部變量,函數結束,一起銷毀
{
	static int boy = 23;
	*meipo = &boy;
}

int main(void)
{
	int *meipo = NULL;

	boy_home(&meipo);  //main裡的 *meipo 0x666
 	printf("boy: %d\n",*meipo);

	system("pause");
	return 0;
}
           
C/C++指針和智能指針的用法為什麼使用指針?指針的定義指向數組的指針指針的通路通路指針所指向的内容空指針指針的算數運算(指針的自增、指針的自減、指針與整數的加減)const 修飾二級指針函數指針指針數組void類型的指針函數指針函數傳回值

數組和指針的糾纏

#include <stdio.h>
#include <Windows.h>

//數組标表示
void array_pointer1(int days[], int len)
{
	printf("====================================== \n");

	for (int i = 0; i < len; i++)
	{
		printf("第 %2d 月的最後一天是 %d \n", i + 1, days[i]);
	}
}

//指針表示
void array_pointer2(int days[], int len)
{
	printf("====================================== \n");
	for (int i = 0; i < len; i++)
	{
		printf("第 %2d 月的最後一天是 %d \n", i + 1, *(days + i));
	}
}

int main(void)
{
	int days[12] = {31, 28, 32, 30, 31, 28, 31, 31, 28, 31, 31, 28};
	int len = sizeof(days) / sizeof(days[0]);

	/*
	for (int index = 0; index < len; index++)
	{
		printf("第 %2d 個月的最後一天是 %d \n", index+1, *(days+index));
	}
	*/

	array_pointer1(days, LEN);
	array_pointer2(days, LEN);

	system("pause");
	return 0;
}
           

指針數組

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	int girls[4][3] = { {178, 179, 167},
						{135, 167, 157},
						{176, 166, 190},
						{155, 165, 180}};

	int *qishou[2]; //定義一個有兩個元素的指針數組,每個元素都是指針變量

	if (girls[0][0] > girls[0][1]) // 如果 178 > 179
	{
		qishou[0] = &girls[0][0]; //把 第零個元素(178)位址 給 第一個旗手
		qishou[1] = &girls[0][1]; //把 第一個元素(179)位址 給 第二個旗手
	}
	else						  //否則
	{
		qishou[0] = &girls[0][1]; //把 第一個元素(179)位址 給 第一個旗手
		qishou[1] = &girls[0][0]; //把 第零個元素(178)位址 給 第二個旗手
	}

	for (int index = 2; index < 12; index++) //
	{
		if (*qishou[1] >= girls[index / 3][index % 3]) //如果 179 大于 girls[0][2]
		{
			continue;
		}
		if (girls[index / 3][index % 3] <= *qishou[0]) // 如果176 小于 178 就把 176 給 棋手 二
		{
			qishou[1] = &girls[index / 3][index % 3];
		}
		else  //否則 就把 178 給棋手二 把 179 給棋手一
		{
			qishou[1] = qishou[0];
			qishou[0] = &girls[index / 3][index % 3];
		}
	}
	printf("最高的是:%d , 次高的是:%d \n", *qishou[0], *qishou[1]);

	system("pause");
	return 0;
}
           

指針和多元數組

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	int A_302[4][3] = { {1, 2, 3},
						{4, 5, 6},
						{7, 18, 9},
						{10, 11, 12}};

	int (*p)[3];
	int* boy = NULL;

	p = &A_302[0];

	/*for (int i = 0;i < 4;i++)
	{
		for (int j = 0;j < 3;j++)
		{
			printf("%d ", (*p)[j]);
		}
		printf("\n");
		p++;
	}*/

	boy = &(*p)[0];

	for (int i = 0;i < 4;i++)
	{
		for (int j = 0;j < 3;j++)
		{
			printf("%d ", *((*p)+j));
			if (*boy < *((*p) + j))
			{
				boy = (*p) + j;
			}
		}
		printf("\n");
		p++;
	}

	printf("偷窺的人是:%d \n", *boy);

	return 0;
}
           

void類型的指針

所有其它類型的指針都可以隐式自動轉換成void類型

#include <stdio.h>
#include <Windows.h>

int main(void)
{
	int arr[] = {1, 2, 3, 4, 5};
	char ch = 'a';
	void * p = arr; //定義了一個void類型的指針

	//p++;  //void * 指針不允許進行算術運算

	p = &ch; //其它類型都可以自動轉換成void * 指針

	//所有其它類型的指針都可以隐式自動轉換成void類型
	printf("p:0x%p \nch:0x%p \n",p ,&ch);

	//強制類型轉換
	char * p1 = (char *)p;

	printf("%c \n", *p1);

	system("pause");
	return 0;
}
           

函數指針

#include <stdio.h>
#include <Windows.h>

int compare_int(const void * a, const void * b)
{
	printf("已經調用了 compare_int 哦\n");

	int * p1 = (int *)a;
	int * p2 = (int *)b;

	//printf("a的位址:0x%p \nb的位址:0x%p\n",&a,&b);
	return *p1 - *p2;
}

int compare_char(const void * a, const void * b)
{
	printf("已經調用了 compare_int 哦\n");

	char ch1 = *((char *) a);
	char ch2 = *((char *) b);

	if(ch1 >= 'A' && ch1 <= 'Z')
	{
		ch1 += 32;
	}
	if(ch2 >= 'A' && ch2 <= 'Z')
	{
		ch2 += 32;
	}
	//printf("a的位址:0x%p \nb的位址:0x%p\n",&a,&b);
	return ch1 - ch2;
}

int main1(void)
{
	int x = 10;
	int y = 5;

	//函數指針的定義 把函數聲明移過來,把函數名改成(* 函數指針名)
	int (*fp)(const void *, const void *);

	fp = &compare_int;
	(*fp)(&x,&y);
	fp(&x,&y);

	printf("compare_int 的位址:0x%p\n",&compare_int);

	compare_int(&x, &y);

	system("pause");
	return 0;
}

int main(void)
{
	//對整形數組排序
	int arr1[] = {2, 5, 1, 22, 2124, 2321, 3, 9, 10};
	qsort(arr1, sizeof(arr1)/sizeof(int), sizeof(int), &compare_int);

	for(int i = 0; i < sizeof(arr1)/sizeof(int);i++)
	{
		printf("%d \n", arr1[i]);
	}

	//qsort 可以對任何類型的數組進行排序
	char arr2[] = {"abcdefgABCDEFGHI"};
	qsort(arr2, sizeof(arr2)/sizeof(char)-1, sizeof(char), &compare_char);
	for(int i = 0; i < sizeof(arr2)/sizeof(char)-1;i++)
	{
		printf(" %c ",arr2[i]);
	}
	printf("\n");

	system("pause");
	return 0;
}
           

函數傳回值

#include <stdio.h>
#include <Windows.h>
#include <iostream>

using namespace std;

int add(int x, int y)
{
	int sum = x + y;
	return sum;
}

int * add1(int x, int y)
{
	int sum = x + y;
	return &sum;
}

int * add2(int x, int y)
{
	int * sum = NULL;
	sum = new int;
	*sum = x + y;
	return sum;
}

int * add3(int x, int y)
{
	static int sum = 0;

	cout << "sum:%d"<< sum <<endl;

	sum = x + y;
	return &sum;
}


int main(void)
{
	int a = 3;
	int b = 5;
	int * sum = NULL;

	//sum = add1(a,b);
	//add2(a,b);
	//cout << add1(a,b) << endl;
	//cout << *sum << endl;
	//cout << *(add2(a,b)) << endl;
	//delete sum;

	//不能使用外部函數局部變量的位址

	sum = add3(a,b);
	cout << *sum << endl;
	* sum = 888;
	add3(a,b);

	system("pause");
	return 0;
}
           

繼續閱讀