C/C++指针的使用

为什么要使用指针

  1. 函数的值传递,无法通过调用函数,来修改函数的实参;
  2. 被调用的函数需要返回更多的“返回值”给调用函数;
  3. 减少值传递带来的额外开销,提高代码执行效率。

指针的定义

定义形式: 指针类型 *指针变量名;

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

int main(){
	int age = 0;
	char ch = 'a';

	int *p;		// 定义了一个整形指针p,它可以指向整形变量
	char *c;	// 定义了一个字符型指针c,他可以指向字符型变量

	p = &age;	// 指针p指向变量age,p的值就是age的地址
	c = &ch;

	printf_s("输入:");
	scanf_s("%d", p);
	printf_s("值为:%d\n", age);

	system("pause");
	return 0;
}

指针的初始化

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

int main(){
	int age = 10;

	int *p = &age;	// 定义了一个整形指针p,并用age初始化该指针

	system("pause");
	return 0;
}

指针的访问

访问指针

访问(读、写)指针变量本身的值,和其他普通变量的访问方式相同。

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

int main(){
	int age = 10;

	int *p = &age;	// 定义了一个整形指针p,并用age初始化该指针

	// 采用10进制方式打印指针的值,不推荐使用该方式
	printf_s("10进制方式打印指针的值:%d\n", p);

	// 采用16进制方式打印指针的值
	printf_s("访问指针:0x%p\n", p);
	printf_s("访问指针:0x%X\n", p);
	printf_s("访问指针:0x%x\n", p);

	system("pause");
	return 0;
}

访问指针所指向的内容

使用解引用运算符(*)

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

int main(){
	int age = 10;

	int *p = &age;	// 定义了一个整形指针p,并用age初始化该指针

	int i = *p;		// *p表示读取p所指向的变量的值,等同于int i = age;
	printf_s("i=%d\n", i);

	*p = 20;		// 等同于 age = 20;
	printf_s("修改后,age的值为:%d,*p的值为:%d\n", age, *p);

	system("pause");
	return 0;
}

空指针和坏指针

空指针

什么是空指针

      就是值为0的指针。任何程序数据都不会存储在地址为0的内存块中,它是被操作系统预留的内存块。

int *p = 0;
int *p = NULL;	// 推荐

空指针的使用

  1. 指针初始化为空指针,目的:避免访问非法数据;
int *p = NULL;
  1. 指针不再使用是,可以设置为空指针;
int *p = &age;
*p = NULL;

  1. 表示这个指针还没有具体的指向,使用前进行合法性判断。
int *p = NULL;
// ......
if (p) { //p 等同于 p!=NULL
	//指针不为空,对指针进行操作
}

坏指针

int *select; //没有初始化
// 情形一 没有赋值,直接访问
printf("选择的房间是: %d\n", *select);
// 情形二
select = 100;
printf("选择的房间是: %d\n", *select);

const关键字

(1) const int *p; //锁值

(2) int const *p; //同上,锁值

(3) int *const p; //锁址

即:const 在前面 锁值,在后面 锁址

指针的算术运算

注: p++ 的概念是在 p 当前地址的基础上 ,自增 p 对应类型的大小,
也就是说 p = p+ 1*(sizeof(类型))

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

int main(){
	int ages[] = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
	int *p = ages;	// 指针指向数组时不用加取地址符

	for(int i = 0; i < sizeof(ages)/sizeof(ages[0]); i++) {
		printf_s("%d  ", (*p)++);
	}

	system("pause");
	return 0;
}

二级指针

二级指针也是个普通的指针变量,只是它里面保存的是一级指针的地址。

定义

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

int main(){
	int age = 10;
	int *p1 = &age;		// 一级指针,保存age的地址
	int **p2 = &p1;		// 二级指针,保存p1的地址

	system("pause");
	return 0;
}

二级指针的用途

     一级指针可以将外部变量通过参数“带入”函数,但是没办法将内部变量“带出”函数
     二级指针不仅可以将外部变量通过参数“带入”函数,还可以将内部变量“带出”函数

// 二级指针使用场景
#include <stdio.h>
#include <Windows.h>

void pointorUser(int **p) {
	static int i = 10;

	*p = &i;
}

int main(){
	int *p = nullptr;

	pointorUser(&p);

	if(p){
		printf_s("存在!指向的值为:%d\n", *p);
	}else{
		printf_s("不存在!\n");
	}

	system("pause");
	return 0;
}

多级指针的定义及使用

可以定义多级指针指向次一级指针

// 多级指针的定义及使用
#include <stdio.h>
#include <Windows.h>

void pointorUser(int **p) {
	static int i = 10;

	*p = &i;
}

int main(){
	int age = 10;
	int *p1 = &age;
	int **p2 = &p1;
	int ***p3 = &p2;
	int ****p4 = &p3;
	int *****p5 = &p4;

	printf_s("p1取年龄:%d\n", *p1);
	printf_s("p2取年龄:%d\n", **p2);
	printf_s("p3取年龄:%d\n", ***p3);
	printf_s("p4取年龄:%d\n", ****p4);
	printf_s("p5取年龄:%d\n", *****p5);

	system("pause");
	return 0;
}

指针和数组

指针表示法和数组表示法

数组完全可以使用指针来访问,days[3]和*(days+3)等同

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

int main(){
	int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	int arrSize = sizeof(days)/sizeof(days[0]);
	
	// 数组表示法
	printf_s("数组表示法:\n");
	for(int i = 0; i < arrSize; i++) {
		printf_s("%d月有%d天!\n", i + 1, days[i]);
	}

	// 指针表示法
	printf_s("指针表示法:\n");
	for(int i = 0; i < arrSize; i++) {
		printf_s("%d月有%d天!\n", i + 1, *(days + i));
	}

	system("pause");
	return 0;
}

存储指针的数组

定义形式:数据类型 *指针数组名[数组长度];

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

/*******************************************************
 * 项目需求:查找数组中排名前3的身高
********************************************************/

void findHeight(int arr[3][3], int *p[3]) {
	int min = 0;
	for(int i=0;i<3;i++) p[i] = &min;	// 把所有身高置为0

	// 查找最高身高
	for(int i=0;i<3; i++){
		for(int j=0;j<3;j++){
			if(arr[i][j] > *(p[0])) p[0] = &arr[i][j];
		}
	}

	// 查找第二身高
	int max = *(p[0]);
	for(int i=0;i<3;i++) {
		for(int j=0;j<3;j++) {
			if(arr[i][j] > *(p[1]) && arr[i][j] < max){
				p[1] = &arr[i][j];
			}
		}
	}

	// 查找第三身高
	for(int i=0;i<3;i++) {
		for(int j=0;j<3;j++) {
			if(arr[i][j] > *(p[2]) && arr[i][j] < *(p[1])){
				p[2] = &arr[i][j];
			}
		}
	}

}

int main(){
	int heights[3][3] = {
		{ 173, 166, 158 },
		{ 163, 155, 182 },
		{ 154, 162, 172 }
	};

	int *p[3];		// 定义一个有3个元素的指针数组,数组中每个都是一个指针变量

	findHeight(heights, p);

	if(p[0]) {
		for(int i=0;i<3;i++){
			printf_s("第%d身高是:%d\n", i+1, *(p[i]));
		}
	}

	system("pause");
	return 0;
}

指针和二维数组

指向数组的指针

定义:数据类型 (*数组名)[数组长度);

访问方式:参考指针和数组之指针表示法和数组表示法

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

void findMin(int arr[3][3], int **min){
	// 定义了一个指针,该指针指向3个int成员
	int (*p)[3] = nullptr;
	*min = &arr[0][0];
	printf_s("min值:%d\n",*min);
	for(int i = 0;i  < 3; i++) {
		p = &arr[i];
		for(int j = 0;j < 3; j++) {
			printf_s("%d  ", *((*p)+j));
			if(*((*p)+j) < **min) *min = (*p)+j;
		}
		printf_s("\n");
	}

}

// 查找最矮身高
int main(){
	int heights[3][3] = {
		{ 173, 166, 158 },
		{ 163, 155, 182 },
		{ 154, 162, 172 }
	};

	int *min = nullptr;
	
	findMin(heights, &min);

	if(min) {
		printf_s("找到最矮身高,是:%d\n", *min);
	}

	system("pause");
	return 0;
}

使用普通指针访问二维数组

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

// 使用普通指针访问二维数组,指针法访问
void findMin(int *arr, int size, int **min){
	*min = arr;
	for(int i=0;i<size;i++) {
		if((*(arr+i)) < **min) {
			*min = arr+i;
		}
	}
}

// 查找最矮身高
int main(){
	int heights[3][3] = {
		{ 173, 166, 158 },
		{ 163, 155, 182 },
		{ 154, 162, 172 }
	};

	int *min = nullptr;
	
	findMin(&heights[0][0], sizeof(heights)/sizeof(heights[0]), &min);

	if(min) {
		printf_s("找到最矮身高,是:%d\n", *min);
	}

	system("pause");
	return 0;
}

数组与指针的区别

数组:用于储存多个相同类型数据的集合。

指针:指针是一个变量,但是它和普通变量不一样,它存放的是其它变量在内存中的地址。

  1. 赋值
    数组:只能单个赋值或拷贝
    指针:可以相互赋值

  2. 范围
    数组:有效范围就是其空间的范围,数组名使用下表引用元素,不能指向别的数组
    指针:可以指向任何地址,但是不能随意访问,必须依附在变量有效范围之内

  3. sizeof
    数组:
        数组所占内存:sizeof(数组名)
        数组个数:sizeof(数组名)/sizeof(数组第一个元素或者数据类型)
    指针:
        32位系统下占4个字节
        64位系统下占8个字节

  4. 指针数组和数组指针
        (1)指针数组

	int age1 = 15;
	int age2 = 20;
	int *p[2];//定义一个有两个元素的指针数组,每个元素都是一个指针变量
	p[0] = &age1;
	p[1] = &age2;

    (2)数组指针

int (*p)[3]; //定义一个指向三个成员的数组的指针
访问元素的两种方式:
int A[4][3]={{173, 158, 166},
{168, 155, 171},
{163, 164, 165},
{163, 164, 172}};
p = &A[0];
数组法: (*p)[j]
指针法: *((*p)+j)
  1. 传参
    数组传参时,会退化为指针。
    退化的意义:C 语言只会以值拷贝的方式传递参数,参数传递时,如果只拷贝整个数组,效率会大大降低,并且在参数位于栈上,太大的数组拷贝将会导致栈溢出。因此,C 语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。
#include <stdio.h>
#include <Windows.h>

// 一维数组传参,不指定数组大小
void printA(int arr[], int size) {
	for(int i = 0; i < size; i++) {
		printf_s("%d  ", arr[i]);
	}
	printf_s("\n");
}

// 一维数组传参,指定数组大小,可移植性低
void printB(int arr[8]){
	for(int i = 0;i < 8; i++) {
		printf_s("%d  ", arr[i]);
	}
	printf_s("\n");
}

// 一维数组传参退化,用指针进行接收,传的是数组的首地址
void printC(int *arr, int size) {
	for(int i = 0;i < size; i++) {
		printf_s("%d  ", arr[i]);
	}
	printf_s("\n");
}


// 查找最矮身高
int main(){
	int heights[] = { 156, 168, 177, 158, 162, 175, 180, 179 };

	// 一维数组传参,不指定数组大小
	printf_s("一维数组传参,不指定数组大小\n");
	printA(heights, sizeof(heights)/sizeof(heights[0]));

	// 维数组传参,指定数组大小,可移植性低
	printf_s("维数组传参,指定数组大小,可移植性低\n");
	printB(heights);

	// 一维数组传参退化,用指针进行接收,传的是数组的首地址
	printf_s("一维数组传参退化,用指针进行接收,传的是数组的首地址\n");
	printC(heights, sizeof(heights)/sizeof(heights[0]));

	system("pause");
	return 0;
}

void型指针

void     =>   空类型
void *  =>   空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指针做出正确的类型转换,然后再间接引用指针。

常见使用场景:函数传参

注意:

  1. 所有其它类型的指针都可以隐式自动转换成 void 类型指针,反之需要强制转换。
  2. 不可以进行算术运算。
  3. 任何类型可以隐式转换成void类型指针,但是void类型指针必须强制类型转换成指定类型。
#include <stdio.h>
#include <Windows.h>

// 一维数组传参,不指定数组大小
void printA(int arr[], int size) {
	for(int i = 0; i < size; i++) {
		printf_s("%d  ", arr[i]);
	}
	printf_s("\n");
}

// 一维数组传参,指定数组大小,可移植性低
void printB(int arr[8]){
	for(int i = 0;i < 8; i++) {
		printf_s("%d  ", arr[i]);
	}
	printf_s("\n");
}

// 一维数组传参退化,用指针进行接收,传的是数组的首地址
void printC(int *arr, int size) {
	for(int i = 0;i < size; i++) {
		printf_s("%d  ", arr[i]);
	}
	printf_s("\n");
}


// 查找最矮身高
int main(){
	char c = 'a';

	void *p = &c;	// 可以隐式转换成void型指针

	char *p1 = (char *)p;	// 必须强制类型转换

	printf_s("%c\n", *(char *)p);

	system("pause");
	return 0;
}

函数指针

定义方法:返回值类型 (*p)(函数参数列表);

调用方式:贝尔实验室的C和UNIX的开发者采用(*p)(函数参数列表),而伯克利的UNIX推广者却采用p(函数参数列表),ANSI C 兼容了两种方式。

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

int compareInt(const void *a, const void *b) {
	return ((*(int *)a) - (*(int *)b));
}

int compareChar(const void *a, const void *b) {
	return ((*(char *)a) - (*(char *)b));
}

// 查找最矮身高
int main(){
	int (*p)(const void *a, const void *b);	//定义了一个函数指针

	int x = 10, y = 15;
	p = &compareInt;
	int ret = p(&x, &y);
	printf_s("%d与%d相比,%d大!\n", x, y, ret>0?x:y);

	char c1 = 'a', c2 = 'z';
	p = &compareChar;
	ret = p(&c1, &c2);
	printf_s("%c与%c相比,%c大!\n", c1, c2, ret>0?c1:c2);

	system("pause");
	return 0;
}

湖南奇牛信息科技

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值