天天看点

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;
}
           

继续阅读