C++笔记day6-指针基础

141 阅读7分钟

指针和内存单元

指针: 地址。

内存单元: 计算机中内存最小的存储单位。——内存单元。大小一个字节。 每一个内存单元都有一个唯一的编号(数)。

	   称这个内存单元的编号为 “地址”。

指针变量:存地址的变量。

内存单元.png

指针定义和使用:

int a = 10;

int *p = &a;			int* p;--- windows;	int *p ---Linux       int * p ;

int a, *p, *q, b;

*p = 250;			指针的 解引用。 间接引用。

*p : 将p变量的内容取出,当成地址看待,找到该地址对应的内存空间。

	如果做左值: 存数据到空间中。

	如果做右值: 取出空间中的内容。

间接引用.png

任意“指针”类型大小:

	指针的大小与类型 无关。 只与当前使用的平台架构有关。   32位:4字节。	 64位: 8字节。

示例

int main(void)
{
	int a = 10;
	printf("%p\n", a); //打印a变量的地址;

	int *p = &a;
	*p = 2000;
	printf("a = %d\n", a);
	//把p中内容取出当成地址看待;即原定a的空间中的10用2000代替;a = 2000
	//上式为用解引用(间接引用)的方式修改变量a
	a = 350;

	printf("*p=%d\n", *p);

	//指针类型的大小
	printf("sizeof(int *) = %u\n", sizeof(int*));
	printf("sizeof(short *) = %u\n", sizeof(short*));
	printf("sizeof(char *) = %u\n", sizeof(char*));
	printf("sizeof(long *) = %u\n", sizeof(long*));
	printf("sizeof(double *) = %u\n", sizeof(double*));

	printf("sizeof(void *) = %u\n", sizeof(void*)); //泛型指针


	system("pause");
	return EXIT_SUCCESS;
}

野指针:

1) 没有一个有效的地址空间的指针。

	int *p;

	*p = 1000;

2)p变量有一个值,但该值不是可访问的内存区域。
	
	int *p = 10;

	*p = 2000;

【杜绝野指针】

空指针:

int *p = NULL;     #define NULL ((void *)0)

*p 时 p所对应的存储空间一定是一个 无效的访问区域。
    

示例

int main(void)
{
	int* p; //没有一个有效地址空间的指针是野指针;
	*p = 2000;

	int* p = 10; //p变量有一个值但是不可访问的内存区域的指针也是野指针;
	*p = 2000;

	int* p = NULL;//NULL=0;利用空指针避免投机;空指针是一个无效的访问区域;
	
	if (p != NULL) 
	{
		printf("helloworld");
	}

	system("pause");
	return EXIT_SUCCESS;
}

万能指针/泛型指针(void *):

可以接收任意一种变量地址。但是,在使用【必须】借助“强转”具体化数据类型。

	char ch = 'R';

	void *p;  // 万能指针、泛型指针
	
	p = &ch;

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

示例

int main(void)
{
	int a = 345;
	void* p;//可以接收任意一种变量地址,但在使用时必须借助“强转”具体化数据类型;
	p = &a;

	printf("%d\n", *((int*)p));//自右向左结合,先强制类型转换(整型地址),再间接引用

	system("pause");
	return EXIT_SUCCESS;
}

const关键字:

修饰变量:

const int a = 20;

int *p = &a;

*p = 650;

printf("%d\n", a);


修饰指针:

const int *p;

	可以修改 p

	不可以修改 *p。

int const *p;

	同上。

int * const p;

	可以修改 *p

	不可以修改 p。

const int *const p;

	不可以修改 p。

	不可以修改 *p。

总结:const 向右修饰,被修饰的部分即为只读。


常用:在函数形参内,用来限制指针所对应的内存空间为只读。

示例:const修饰变量

int main(void)
{
	const int a = 20;
	//a = 5; //const修饰后a值不能再被修改;但可以被指针修改
	int* p = &a;
	*p = 650;
	printf("%d", a);

	system("pause");
	return EXIT_SUCCESS;
}

指针和数组:

数组名:  
	【数组名是地址常量】 --- 不可以被赋值。	 ++ / -- / += / -= / %= / /=  (带有副作用的运算符)

	指针是变量。可以用数组名给指针赋值。 ++ -- 

取数组元素:

	int arr[] = {1,3, 5, 7, 8};

	int *p = arr;  

	arr[i] == *(arr+0) == p[0] == *(p+0)


指针和数组区别:

	1. 指针是变量。数组名为常量。

	2. sizeof(指针) ===》 4字节 / 8字节

	   sizeof(数组) ===》 数组的实际字节数。

指针++ 操作数组:

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

	for (size_t i = 0; i < n; i++)
	{
		printf("%d ", *p);
		p++;  // p = p+1;   一次加过一个int大小。 一个元素。
	}

	p的值会随着循环不断变化。打印结束后,p指向一块无效地址空间(野指针)。

指针和数组.png

指针操作数组.png

示例1:指针++操作数组

int main(void)
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
	int* p = arr;

	int n = sizeof(arr) / sizeof(arr[0]);
	printf("first p = %p\n", p);

	for (size_t i = 0; i < n; i++)
	{
		printf("%d ", *p);
		p++; // p = p+1;一次加过一个int大小:一个元素
	}
	putchar('\n');
	printf("last p = %p\n", p); 
	int len = p - arr;
	printf("len = %d\n", len);

	system("pause");
	return EXIT_SUCCESS;
}

示例2:综合练习

int main(void)
{
	int arr[10];
	int* p = arr;

	for (size_t i = 0; i < 10; i++)
	{
		*(arr + i) = 10 + i;
	}
	for (size_t i = 0; i < 10; i++)
	{
		printf("%d ", *p);
		p++;
	}
	printf("\n");

	system("pause");
	return EXIT_SUCCESS;
}

指针加减运算:

数据类型对指针的作用:
	
	1)间接引用:

		决定了从指针存储的地址开始,向后读取的字节数。  (与指针本身存储空间无关。)

	2)加减运算:

		决定了指针进行 +1/-1 操作向后加过的 字节数。

指针 * / % : error!!!

指针 +- 整数:

	1) 普通指针变量+-整数

		char *p; 打印 p 、 p+1  偏过 1 字节。

		short*p; 打印 p 、 p+1  偏过 2 字节。

		int  *p; 打印 p 、 p+1  偏过 4 字节。		

	2)在数组中+- 整数

		short arr[] = {1, 3, 5, 8};

		int *p = arr;

		p+3;			// 向右(后)偏过 3 个元素

		p-2;			// 向前(左)偏过 2 个元素

	3)&数组名 + 1

		加过一个 数组的大小(数组元素个数 x sizeof(数组元素类型))

指针 +- 指针:

	指针 + 指针: error!!!

	指针 - 指针:

		1) 普通变量来说, 语法允许。无实际意义。【了解】

		2) 数组来说:偏移过的元素个数。

示例1:数据类型对指针作用

int main(void)
{
	int a = 0x12345678;

	int* p = &a; //0x12345678

	printf("p =  %p\n", p); //a 的地址值
	printf("p + 1 = %p\n", p + 1);

	system("pause");
	return EXIT_SUCCESS;
}

指针类型的作用.png

指针实现 strlen 函数:

char str[] = "hello";

char *p = str;

while (*p != '\0')
{
	p++;
}

p-str; 即为 数组有效元素的个数。

示例

int mystrlen(char arr[])  //借助指针++实现strlen
{
	char* p = arr;
	while (*p != '\0')
	{
		p++;
	}
	return p-arr;
}

int main(void)
{
	char str[] = "hello";

	int ret = mystrlen(str);
	printf("ret = %d\n", ret);

	system("pause");
	return EXIT_SUCCESS;
}

指针比较运算:

1) 普通变量来说, 语法允许。无实际意义。

2) 数组来说:	地址之间可以进行比较大小。

		可以得到,元素存储的先后顺序。

3int *p;

    p = NULL;		// 这两行等价于: int *p = NULL;

    if (p != NULL)

	printf(" p is not NULL");

    else 
	printf(" p is NULL");

指针数组:

一个存储地址的数组。数组内部所有元素都是地址。

1) 
	int a = 10;
	int b = 20;
	int c = 30;

	int *arr[] = {&a, &b, &c}; // 数组元素为 整型变量 地址
2) 

	int a[] = { 10 };
	int b[] = { 20 };
	int c[] = { 30 };

	int *arr[] = { a, b, c }; // 数组元素为 数组 地址。	

指针数组本质,是一个二级指针。

二维数组, 也是一个二级指针。

指针数组.png

示例

int main(void)
{
	int a = 10;
	int b = 20;
	int c = 30;

	int* p1 = &a;
	int* p2 = &b;
	int* p3 = &c;

	int* arr[] = { p1,p2,p3 };
	printf("*(arr[0]) = %d\n", *(arr[0]));

	system("pause");
	return EXIT_SUCCESS;
}

多级指针:

int a = 0;

int *p = &a;  				一级指针是 变量的地址。

int **pp = &p;				二级指针是 一级指针的地址。

int ***ppp = &pp;			三级指针是 二级指针的地址。	

int ****pppp = &ppp;			四级指针是 三级指针的地址。	【了解】
......


多级指针,不能  跳跃定义!


对应关系:

ppp == &pp;			三级指针

*ppp == pp == &p; 			二级指针

**ppp == *pp == p == &a				一级指针

***ppp == **pp == *p == a				普通整型变量



*p : 将p变量的内容取出,当成地址看待,找到该地址对应的内存空间。

	如果做左值: 存数据到空间中。

	如果做右值: 取出空间中的内容。
            

示例

int main(void)
{
	int a = 10;
	int* p = &a; 
	int** pp = &p; //  int **pp =&(&a) 这种定义方法不行;
	int*** ppp = &pp;

	printf("***ppp = %d\n", ***ppp);
	printf("**pp = %d\n", **pp);
	printf("*p = %d\n", *p);
	printf("a = %d\n", a);

	system("pause");
	return EXIT_SUCCESS;
}