Week07-指针

Week07-指针,第1张

文章目录
  • 前言
    • 1.指针的定义、初始化和访问
      • 1.1 指针的定义和初始化
      • 1.2 指针的访问
    • 2.空指针和坏指针
      • 2.1 空指针
      • 2.2 坏指针
    • 3.指针的加减运算
      • 3.1 指针与常数
      • 3.2 指针与指针
    • 4.const 对指针的修饰
      • 4.1 const int *p
      • 4.2 int *const p
      • 4.3 const int * const p
    • 5.二级指针和多级指针
      • 5.1 二级指针的定义和访问
      • 5.2 多级指针
    • 6.指针作为函数的参数
      • 6.1 为什么要使用指针
      • 6.2 指针的应用
    • 7.指针与数组
      • 7.1 指针数组与数组指针
        • 7.1.1 指针数组
        • 7.1.2 数组指针
      • 7.2 一维数组、二维数组、指针数组作为函数的参数传递
    • 8.void类型的指针
    • 9.函数指针


前言

本篇文章主要介绍了C/C++指针的用法


1.指针的定义、初始化和访问 1.1 指针的定义和初始化

指针定义时 * 两边的缩进并无要求(这里有几种缩进风格):
(1)int *p;
(2)int * p;
(3)int* p;
(4)int*p; //这种形式是不推荐的
在定义时,没有指向的指针(暂时不用初始化),我们一般初始化为 NULL 或 0 (避免出现坏指针)


#include 
#include 

/*
	指针本身也是一个变量,但真值的内存空间中存储的是其他变量内存空间的地址
*/

int main(void){
	
	//首先定义基本的变量
	int age = 18;
	char ch = 'T';

	//然后定义指针分别存储age和ch的地址
	//指针需要指向什么样的数据类型,就定义该类型的指针
	int *p;
	char *c;

	//注意:这里p1是指针,但p2只是一个整形变量
	int *p1, p2;
	//指针的初始化
	p = &age; //&是取地址符,在scanf("%d",&age);函数中使用过
	c = &ch;
	system("pause");
	return 0;
}

1.2 指针的访问

对于一个指针变量(p),我们可以访问指针的地址(&p),指针的值(p)和指针所指向的值(*p)

#include 
#include 

using namespace std;
int main(void) {

	int room = 80;
	char ch = 'c';

	int *p1 = &room;
	char *p2 = &ch;
	//1.访问指针变量内存中的值与其他普通变量的方式相同
	//地址建议用printf输出用%p(会用4个字节的格式化输出),或者使用16进制打印(%x,%X)
	printf("&p1 = %p\n", &p1);
	printf("p1 = %p\n", p1);
	printf("p1 = 0x%x\n", unsigned int(p1));
	printf("p1 = 0X%X\n", unsigned int(p1));

	//2.访问指针所指向的元素用 *p ,‘*’叫做间接访问运算符
	printf("*p1 = %d\n", *p1); //输出为:80
	*p1 = 40;//*p1可以改变room的值
	printf("room = %d\n", room);

	/*cout << "&p1:" << &p1 << endl;
	cout << "p1:" << p1 << endl;
	cout << "*p1:" << *p1 << endl;*/

	//3.指针所占字节数:在32位系统(x86)中指针占用4个字节的空间,但在64位系统(x64)中占用8个字节
		  											//       x86   x64
	printf("sizeof(room) = %d\n", sizeof(room));    //output: 4     4
	printf("sizeof(p1) = %d\n", sizeof(p1));        //output: 4		8
	printf("sizeof(ch) = %d\n", sizeof(ch));        //output: 1     1   
	printf("sizeof(p2) = %d\n", sizeof(p2));        //output: 4     8    
	
	system("pause");
	return 0;

}

2.空指针和坏指针 2.1 空指针

空指针:
(1)空指针,就是值为0的指针,
(2)指针不再使用时,推荐置为空
(3)不确定指针具体指向时,使用前可以进行合法性检查

#include 
#include 
using namespace std;

int main() {
	/*
		如果使用了没有初始化的指针,程序是不能通过编译的
	*/
	//int *p;
	//cout << "p:" << p << endl;
	/*
		但如果我们打印空指针的地址,程序可以通过编译
	*/
	int *p = NULL;//NULL是一个宏定义#define NULL 0
	cout << "p:" << p << endl;//输出为00000000(x86),(这表示为空指针)
	cout << "*p:" << p << endl;//这里会造成程序中断执行,因为0地址是不允许被程序访问的,然后在使用前做合法性检查。
	system("pause");
	return 0;
}
2.2 坏指针

坏指针:
(1)没有初始化就使用的指针
(2)指定不属于你应用程序变量的地址,比如我们随便赋值为指针 int *p = 100;

#include 
#include 
using namespace std;

int main() {
	/*
		这里如果我们输入的不是666或者888,那么p就没有被赋值,此时p虽然分配了内存空间,
		但依旧没有初始化,此时p就变成了一个野指针,要防范野指针可以先使用空指针进行初始化
	*/
	int room1 = 666;
	int room2 = 888;
	//int *p;

	int *p = NULL;

	int select = 0;
	cout << "请输入房间号:";
	cin >> select;

	if (select == 666) {
		p = &room1;
	} else if(select == 888){
		p = &room2;
	}

	//cout << *p << endl;

	//合法性检查
	if(p){
		cout << *p << endl;
	}
	system("pause");
	return 0;
	
	
}



3.指针的加减运算 3.1 指针与常数
#include
#include
/*
	p++ 的概念是在 p 当前地址的基础上 ,自增 p 对应类型的大小, 也就是说 p = p+ 1*(sizeof(类型))
	指针的自减运算
	同样指针的加减法运算都是通过类型大小来计算的,如:
		设int型数组的第一个地址为00F3FC90,那么指向该数组的指针p+1 = 00F3FC90 + 4
		如果是char型数组那么 p + 1 = 00F3FC90 + 1
		所以指针 p + n = %p + n*sizeof(type) 
*/

int main(void) {

	int arr[] = { 1,2,3,4,5,6,7 };
	int *p = arr;
	int len = sizeof(arr) / sizeof(arr[0]);

	char chs[] = { 'a','b','c','d','e' };
	char *c = chs;


	//1.使用数组的形式来访问数组元素
	for (int i = 0; i < len; i++) {
		printf("arr[%d] = %d\n", i, arr[i]);
	}
	//2.数组的地址就是数组第一个成员的地址
	printf("arr的地址:%p,arr[0]的地址:%p\n", &arr, &arr[0]);


	//通过指针访问第一个元素,并打印地址
	printf("arr[0] = %d,&arr = %p\n", *p, p);
	//通过指针访问第二个元素,并打印地址
	printf("arr[1] = %d,&arr = %p\n", *(p + 1), p + 1);

	//通过指针访问chs中的元素
	printf("chs[0] = %c,&arr = %p\n", *c, c);
	printf("chs[1] = %c,&arr = %p\n", *(c + 1), c + 1);
	
	for (int i = 0; i < len; i++) {
		printf("arr[%d] = %d,adress = %p\n", i, *p++,&arr[i]);
	}
	p -= len;
	for (int i = 0; i < len; i++) {
		printf("arr[%d] = %d,\n", i, *p);
		p += 1;
	}


	c = &chs[4];
	for (int i = 4; i >= 0; i--) {
		printf("chs[%d] = %c,adress = %p\n", i, *c--, &chs[i]);
	}

	system("pause");
	return 0;
}
3.2 指针与指针
#include 
#include 

/*
	指针与指针的加减运算跟上方道理一样
	使用指针与指针的运算计算元素的偏移值
*/
int main0106(void) {

	int ages[] = { 16,18,20,22,26,30,36 };
	int ages1[] = { 12,24,26,60 };
	int len = sizeof(ages) / sizeof(ages[1]);

	int *tree = ages + 4;
	int *martin = ages + 6;
	int *rock = ages1 + 3;

	printf("martin - tree = %d\n", martin - tree);
	printf("tree - martin = %d\n", tree - martin);

	//不同数组之间(同类型)也可以运算,但大多数情况下没有意义
	printf("rock:%p,tree:%p,rock - tree = %d\n", rock, tree, rock - tree);
	printf("tree - rock = %d\n", tree - rock);

	system("pause");
	return 0;
}


4.const 对指针的修饰 4.1 const int *p

const int *p = &a 与 int const *p = &a等效,都表示 *p 只能读取a的值,但不能修改a的值

4.2 int *const p

int *const p = &a 表示 p 的值不能改变(p只能指向a,不能再被赋为其他值)

4.3 const int * const p

const int * const p = &a,此时,*p和p都不能修改了

#include 
#include 

int main(void) {

	//1.const int * 与 int const * 都表示不能修改指针所指内存的值(*p),但可以改变所指向的位置(p)

	int age1 = 30;
	int age2 = 24;

	int const *p1 = &age1;
	const int *p2 = &age1;
	
	printf("age1的地址:%p,age2的地址:%p\n", &age1, &age2);
	printf("\n修改前:\np1的值:%d\tp2的值:%d\np1的地址:%p\tp2的地址:%p\n"
		   ,*p1,*p2,p1,p2);
	//*p1 = 25;
	//*p2 = 25; //错误提示为表达式必须是可修改的左值,说明*p2此时是不可修改的
	p1 = &age2;
	p2 = &age2;
	printf("修改p1,p2后:\np1的值:%d\tp2的值:%d\np1的地址:%p\tp2的地址:%p\n"
		   , *p1, *p2, p1, p2);


	//2.int * const p:这时,指针只能修改所指内存的值(*p),不能修改地址(p)
	int *const p3 = &age1;
	printf("\n修改前:\np3的值:%d\np3的地址:%p\n"
		   , *p3, p3);
	//p3 = &age2;//错误同上
	*p3 = 26;
	printf("修改*p3后:\np3的值:%d\np3的地址:%p\n"
		   , *p3, p3);

	//3.const int * const p:这时p和*p都不能更改
	const int *const p4 = &age1;

	printf("\n修改前:\np4的值:%d\np4的地址:%p\n"
		   , *p4, p4);
	/*p4 = &p2;
	*p4 = 26;*/
	printf("我比较苦B,啥也改不了");


	system("pause");
	return 0;
}

5.二级指针和多级指针 5.1 二级指针的定义和访问
#include 
#include 

/*
	二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址
*/
int main(void) {
	int guizi = 888; //柜子里存放q
	int *guiziA = &guizi; //柜子A里存放guizi的地址
	int **lilianjie = &guiziA; //lilianjie手里拿着guiziA的地址

	printf("guizi的地址:%p\n", &guizi);
	printf("guiziA存放的地址:%p\n", guiziA);
	printf("lilianjie首先打开guiziA,拿到guizi的地址:%p\n", *lilianjie);
	
	int *tmp = *lilianjie;

	printf("拿到guizi的地址后,打开柜子拿q:%d\n", *tmp);
	printf("真是麻烦,我就一步到位:%d\n", **lilianjie);

	system("pause");
	return 0;
}
5.2 多级指针
#include 
#include 
/*
	多级指针:
		1.n级指针只能被n-1级指针赋值
		2.并无大多意义
*/

int main0110(void) {
	int guizi = 888;
	int *guizi2 = &guizi;
	int **guizi3 = &guizi2;
	int ***guizi4 = &guizi3;
	int ****guizi5 = &guizi4;


	printf("*guizi2:%d\n", *guizi2);
	printf("**guizi3:%d\n", **guizi3);
	printf("***guizi4:%d\n", ***guizi4);
	printf("****guizi5:%d\n", ****guizi5);

	system("pause");
	return 0;
}

6.指针作为函数的参数 6.1 为什么要使用指针

1.在函数调用时,普通参数只能进行值传递,通过指针来进行地址传递,修改实参的值;
2.通过指针可以让被调用函数提供更多返回值
3.减少值传递带来的额外开销,提高程序执行效率

6.2 指针的应用
#include 
#include 

/*
	1. 普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数
	2. 二级指针不但可以将变量通过参数函数内部,也可以将函数内部变量 “带出”到函
	   数外部
*/

//1.先声明一个函数,作用是:交换两个变量的值
void swap_ordinary(int, int);//普通的形参因为通过值传递并不能直接交换实参的值
void swap(int *, int *);  //指针型实参通过传递实参的地址来交换实参的值

//2.meipo要访问boy_home里的money变量
void boy_home(int **meipo);

int main0109(void) {
	//1.
	int a = 101;
	int b = 202;

	int *p1 = &a;
	int *p2 = &b;
	printf("&p1:%p\n", &p1);


	swap_ordinary(a, b);
	printf("调用普通形参函数后:a:%d,b:%d\n", a, b);

	swap(p1, p2);
	//swap(&a, &b);//这样可以不用再定义指针
	printf("调用指针形参后:a:%d,b:%d\n", a, b);


	//2.
	int *meipo = NULL;
	printf("mian中meipo的地址:%p\n", &meipo);
	boy_home(&meipo);
	printf("*meipo:%d\n", *meipo);
	printf("meipo:%p\n", meipo);


	system("pause");
	return 0;
}
void swap(int *p1, int *p2) {
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}
void swap_ordinary(int a, int b) {
	int tmp = a;
	a = b;
	b = tmp;
}
void boy_home(int **meipo) {
	//这里要定义静态变量,否则的话,可能会使meipo成为野指针
 	static int money = 1000000; 
	*meipo = &money;
	printf("meipo的地址:%p\n", &meipo);
	printf("money的地址:%p\n", &money);
}

7.指针与数组 7.1 指针数组与数组指针 7.1.1 指针数组

指针数组:一个数组里存的都是相同类型的指针(本质上是一个数组),跟一般数组用法基本类似。

	/*一维的指针数组*/
	int a = 1, b = 2, c = 3;

	int *p[3] = { &a,&b,&c };

	for (int i = 0; i < 3; i++) {
		cout << *p[i] << endl;//要访问数组里指针所指的变量,只需要在前面加上解引
		//cout << **(p + i) << endl;
	}
	/*二维的指针数组*/
	const char *ptr_array[3][3] = { {"asdx","qwer","fdsfaf"}{"44444","555","6666"},{"a78x","q3er","f2f"} };

	cout << ptr_array[2][2] << endl;

	for (int i = 0; i < 3; i++) {
		for (int j = 0; j < 3; j++) {
			printf("%s\t", ptr_array[i][j]);
			//printf("%s\t", *(*(ptr_array + i) + j));
		}
		printf("\n");
	}
7.1.2 数组指针

数组指针:就是一个指向数组的指针。(本质上是一个指针)

#include 
#include 

using namespace std;

int main() {

	int a[3] = { 1,2,3 };

	cout << "&a[0]:\t" << &a[0] << "\t" << typeid(&a[0]).name() << endl;
	cout << "a:\t" << a << "\t" << typeid(a).name() << endl;
	cout << "&a:\t" << &a << "\t" << typeid(&a).name() << endl;

	int (*p)[3] = &a;
	for (int i = 0; i < 3; i++) {
		cout << (*p)[i] << "\t";
		//cout << (*p) + i << "\t";
	}
	/*
		a[0]的地址、a的值、a的地址是相同的。
		a[0]会分配一段4字节的内存空间;
		数组名a看成一个指向a[0]的指针,所以值是a[0]的地址;
		&a则是取数组a的首地址,所以也是a[0]的地址。

		数组a的类型是 int [3],所以要定义一个int [3]*类型的指针,但c/c++要写成int (*)[3]的形式.
	*/
	//二维数组的数组名并不是一个二级指针,而是一个指向第一行元素的数组指针

	int b[4][3] = {
		{1, 2, 3},
		{4, 5, 6},
		{7, 8, 9},
		{10, 11, 12}
	};

	int (*p2)[3] = b;

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 3; j++) {
			//cout << setw(2) << left << p2[i][j] << " "; //也可以直接通过b来访问,
			//cout << setw(2) << left << (*(p2+i))[j] << " ";
			cout << setw(2) << left << *(*(p2+i)+j) << " ";
		}
		cout << endl;
	}
	system("pause");
	return 0;	
}



7.2 一维数组、二维数组、指针数组作为函数的参数传递

(1)一维数组和一维指针数组

/*
	一维数组传参(数组名就是指向第一个元素的指针):

		(1) 形参不指定数组大小:int arr[],int len
		 (2) 形参指定数组大小:int arr[SIZE]
		 (3) 形参用指针,传数组首元素地址:int *arr,int len
*/
void method1(int arr[], int len);
void method2(int arr[SIZE]);
void method3(int *arr, int len);

/*
	指针数组传参:
		(1) 指针数组传参,声明成指针数组,不指定数组大小:int *arr[],int len
		(2) 指针数组传参,声明成指针数组,指定数组大小:int *arr[SIZE]
		(3) 二级指针传参 : int **arr,int len
			arr 指向 arr[0]的指针,*arr = arr[0] , **arr = *arr[0]
*/
void method1(int *p_arr[], int len);
void method2(int *p_arr[10]);
void method3(int **p2, int len);

(2)二维数组

#include 
#include 
#include 

using namespace std;

#define LINES 4
#define COLUMNS 3

void printArr1(int arr[LINES][COLUMNS]) {
	cout << "1." << endl;
	for (int i = 0; i < LINES; i++) {
		for (int j = 0; j < COLUMNS; j++) {
			//cout << setw(2) << left << arr[i][j] << "\t";
			cout << setw(2) << left << *(*(arr+i)+j) << "\t";
		}
		cout << endl;
	}
}

void printArr2(int arr[][COLUMNS], int lines) {
	cout << "2." << endl;
	for (int i = 0; i < LINES; i++) {
		for (int j = 0; j < COLUMNS; j++) {
			cout << setw(2) << left << arr[i][j] << "\t";
		}
		cout << endl;
	}
}

void printArr3(int (*arr)[COLUMNS], int lines) {
	cout << "3." << endl;
	for (int i = 0; i < LINES; i++) {
		for (int j = 0; j < COLUMNS; j++) {
			cout << setw(2) << left << arr[i][j] << "\t";
		}
		cout << endl;
	}
}

void printArr4(int **arr, int lines, int columns) {

	cout << "4." << endl;
	for (int i = 0; i < lines; i++) {
		for (int j = 0; j < columns; j++) {
			cout << setw(2) << left << *((int*)arr + columns*i + j) << "\t";//不能写成数组的形式
		}
		cout << endl;
	}
}


int main() {

	int arr2[LINES][COLUMNS];
	for (int i = 0; i < LINES*COLUMNS; i++) {
		arr2[i / COLUMNS][i % COLUMNS] = i + 1;
	}

	printArr1(arr2);

	printArr2(arr2,LINES);

	printArr3(arr2, LINES);

	printArr4((int **)arr2, LINES, COLUMNS);//要将arr2转换为二级指针

	system("pause");
	return 0;
}

8.void类型的指针

void 表示空类型
void* 表示空类型指针(即只存储地址,但没有类型)
void类型指针是无法节引访问的,要访问void类型的值,必须要对其进行正确的强制类型转换 (所有其它类型的指针都可以隐式自动转换成 void 类型指针,反之需要强制类型转换)

#include 
#include 
int main(void){
	int arr[]={1, 2, 3, 4, 5};
	char ch = 'a';
	void *p = arr;//定义了一个void 类型的指针
	//p++; //不可以, void * 指针不允许进行算术运算
	p = &ch; //其它类型可以自动转换成void * 指针
	//printf("数组第一个元素: %d\n", *p); //不可以进行访问
	printf("p: 0x%p ch: 0x%p\n", p, &ch);
	//强制类型转化
	char * p1 = (char *)p;
	printf("p1 指向的字符是: %c\n", *p1);
	system("pause");
	return 0;
}
9.函数指针

定义和初始化:返回值类型 (*指针名)(参数列表)= &函数名;
使用:
方式1:(*指针名)(参数列表)
方式2: 指针名(参数列表)

#include 
#include 

int compare_int(const void *a,const void *b) {
	//printf("Success!\n");
	int *num1 = (int *)a;
	int *num2 = (int *)b;
	return *num2 - *num1;
}

int compare_char(const void *a, const void *b) {
	char c1 = *(char *)a;
	char c2 = *(char *)b;
	if (c1 >= 'A' && c1 <= 'Z') {
		c1 += 32;
	}
	if (c2 >= 'A' && c2 <= 'Z') {
		c2 += 32;
	}
	return c1- c2;
}

int main(void) {

	int arr[] = { 1,2,3,4,5,6,8,7,9 };

	int (*p_comp_int)(const void *a, const void *b) = &compare_int;//定义函数指针并初始化
	//compare_int(&arr[1], &arr[2]);//实参进行隐式转换

	(*p_comp_int)(arr, arr+1);//指针进行函数调用方式1
	int num = p_comp_int(arr, arr + 1);//指针进行函数调用方式2
	printf("%d\n", num);

	/*快速排序通过函数指针(在函数中定义排序规则)和void类型指针可以实现对任何类型的数组排序*/
	//qsort对整形数组排序
	qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), &compare_int);

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

	//qsort对char类型数组排序
	char arr2[] = { 'a','b','c','d','e','f','A','B','C','D','E','F' };
	
	int (*p_comp_char)(const void *a, const void *b);
	qsort(arr2, sizeof(arr2) / sizeof(char), sizeof(char), &compare_char);

	for (int i = 0; i < sizeof(arr2) / sizeof(char); i++) {
		printf("%c", arr2[i]);
	}

	system("pause");
	return 0;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/676208.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存