C++学习笔记(超详细笔记记录ing)

C++学习笔记(5)

学习是一件任重而道远的事情,与其焦虑不如动手起来,借助平台记录自己学习笔记,希望和大家多多交流,今天又是努力成为程序媛的一天!

12.指针

12.1取地址运算符&

作用:获取变量的地址,它的操作数必须是变量

#include<stdio.h>

int main(void) {
	int i = 0;
	int p;
	
	//p = (int)&(++i); p = (int)&(p+i);这些都会报错,取地址符的右边必须有一个明确的变量

	printf("%p\n", &i);

	printf("%p\n",&p);//%p会将其做为一个地址来输出,以16进制的方式
	printf("%p\n",&p);//%p会将其做为一个地址来输出,以16进制的方式
	printf("%lu\n", sizeof(int));//int所占字节//4
	printf("%lu\n", sizeof(&i));//i地址所占字节//8
	//这里是64位操作系统,所以地址的大小与int不同,32位是相同的
	printf("%lu\n", sizeof(&p));

	p = (int)&i;
	printf("0x%x\n", p);//与第一行结果一样


	return 0;
}

运行结果:
在这里插入图片描述

  • 地址的大小是否与int相同取决于编译器
  • 取地址符的右边必须有一个明确的变量

12.2指针

几点需要注意:

  • p = &i ,表示地址
  • *p = i表示值,这种形式这个时候表示值
  • int *p = &i就是p=&i 可以理解为 int * (p = &i)
  • int *p = &i,表示:int *p; p=&i;
  • 指针是一个变量,声明一个变量也是这样

12.2.1指针的使用

意义:就是保存地址的变量,即取地址符&得到的地址的那么一个变量

  • 举例
int i;
int* p = &i;//这里p是一个指针,*表示p指向i的地址
int* p,q;//*可以靠近int/p都可以,而q不是指针
int *p,q;//表达pq都是指针,这样写:int *p,*q
  • p指向了i,即p的值就是变量i的地址
  • 指针也是一种数据类型,p为指针变量
  • 32位操作系统,指针都占4个字节,64位,占8个字节,不论什么数据类型
    在这里插入图片描述

12.2.2访问那个地址上的变量*

知道变量的地址是可以访问变量对变量
进行读写操作,而直接将i的值赋值给k,它是会开一个新的内存空间用来存放k,放入i的值,但本身和i并没有任何关联;对于指针p会指向i的地址,里面存放的值是i存放的地址,指向i,而*p就是访问p地址对应的那个变量的值

  • *是一个单目运算符,用来访问指针的值所表示的地址上的变量
  • 可以做右值也可以做左值
    • int k = *p;
    • *p = k+1;
  • 左值为什么叫左值,出现在赋值号左边不是变量,而是值,是表达式的计算结果
    • a[0] = 2;
    • *p = 3;

这里a[0]和*p都不是变量,是表达式运算的结果,但是运算的结果可以放在表达式的左边来接收一个值。
*p中的 *表示一个运算符,是指p地址所代表的变量
数组中的[]方括号也是一个运算符,是指取下标的运算符,取单元的运算符

  • 左值是特殊的值,所以叫左值
#include<stdio.h>
void f(int* p);
void g(int k);


int main(void) {

	int i = 6;
	printf("&i=%p\n", &i);
	f(&i);//i的地址
	g(i);

	return 0;
}

void f(int* p) {
	printf("p=%p\n", p);//输出&i即i的地址
	printf("*p=%d\n", *p);//输出i的值,因为*+i的地址就指向了i的值
	*p = 26;//虽然在函数里面,但是可以通过指针,由外面变量的地址找到i的值并改变它的值
}


void g(int k) {
	printf("k=%d\n", k);//指针访问修改之后,已经改变了i 的值
}

运行结果:
在这里插入图片描述

12.2.3指针的应用场景

  • 场景一:交换两个变量的值
#include<stdio.h>
void swap(int* pa, int* pb);
//交换两个变量的值
//地址传递

int main(void) {

	int a = 5;
	int b = 6;
	
	swap(&a, &b);
	printf("a=%d,b=%d\n", a, b);

	return 0;
}

void swap(int* pa, int* pb) {

	int t = *pa;
	*pa = *pb;
	*pb = t;
}

运行结果:
在这里插入图片描述

  • 场景二:
    • 函数返回多个值,某些值就只能通过指针返回
    • 传入的参数实际上是需要保存带回的结果的变量
  • 函数返回多个值代码如下,找出一组数组中的最大值和最小值:
#include<stdio.h>
void minmax(int a[], int len, int* min, int* max);

int main(void) {

	int a[] = { 1,8,22,75,54,88,22,46,75,0,5,33 };
	int min, max;
	minmax(a, sizeof(a) / sizeof(a[0]), &min, &max);
	printf("min=%d,max=%d\n", min, max);

	return 0;
}

void minmax(int a[], int len, int* min, int* max) {

	int i;
	*min = *max = a[0];//将最大值和最小值都设为数组第一个元素
	//遍历数组所有元素和最大值最小值比较
	//凡是比最大值大的,就替换最大值的值,比最小值小的就替换最小值
	for (int i = 0; i < len; i++) {
		if (*min > a[i]) {
			*min = a[i];
		}
		if (*max < a[i]) {
			*max = a[i];
		}//两个单if语句分别找出最大值和最小值
	}
}

运行结果:
在这里插入图片描述

  • 场景三:我的程序可能会出错,函数返回运算的状态,结果通过指针返回
#include<stdio.h>
int divide(int a, int b, int *result);


//@return:如果除法成功,返回1,否则返回值为0

int main(void) {
	int a = 80;
	int b = 60;
	int c;
	if (divide(a, b, &c)) {
		printf("%d/%d=%d\n", a, b, c);
	}
	return 0;
}


int divide(int a, int b, int *result) {
	int ret = 1;//如果除法正确,返回1
	if (b == 0) {
		ret = 0;
	}//不正确返回0
	else {
		*result = a/b;//除法正确就把输入的结果带入外界的c中
	}
	return ret;
}

运行结果:
在这里插入图片描述

注意:常见错误,定义了指针变量,还没有指向任何变量时,就给它赋值了,no way!

12.2.4指针与数组的关系

#include<stdio.h>
//void minmax(int a[], int len, int* min, int* max);
void minmax(int* a, int len, int* min, int* max);

int main(void) {

	int a[] = { 1,8,22,75,54,88,22,46,75,0,5,33 };
	int min, max;
	printf("main sizeof(a)=%lu\n", sizeof(a));//这里返回a数组大小
	printf("main a=%p\n", a);//数组a的地址 
	minmax(a, sizeof(a) / sizeof(a[0]), &min, &max);
	printf("a[0]=%d\n", a[0]);
	printf("min=%d,max=%d\n", min, max);

	return 0;
}

void minmax(int *a, int len, int* min, int* max) {//这两句话一样的效果
//void minmax(int a[], int len, int* min, int* max) {//这里的a[]相当于指针,a本身就相当于地址

	int i;
	printf("minmax sizeof(a)=%lu\n", sizeof(a));//返回函数参数里面的数组a大小
	printf("main a=%p\n", a);//参数数组里面的a的地址
	a[0] = 1000;  //此时就已经修改了数组第一个的值
	*min = *max = a[0];//将最大值和最小值都设为数组第一个元素
	//遍历数组所有元素和最大值最小值比较
	//凡是比最大值大的,就替换最大值的值,比最小值小的就替换最小值
	for (int i = 0; i < len; i++) {
		if (*min > a[i]) {
			*min = a[i];
		}
		if (*max < a[i]) {
			*max = a[i];
		}//两个单if语句分别找出最大值和最小值
	}
}

运行结果:
在这里插入图片描述

  • mian函数中返回的sizeof(a)是数组所占空间大小;而函数参数里面sizeof(a)返回的是一个int类型地址所占内存空间大小8(64位架构),所以这里a就是a的地址,而a[]就是指针,指向数组a的值
  • 输出main()和函数里面的a的地址发现一样,说明就是同一个a,即指针指向了那个a
  • 在函数参数程序里修改a[0]的值,原数组得到了相应改变

以下四种函数原型是等价的:

  • int sum(int *ar,int n);
  • int sum(int *,int);
  • int sum(int ar[],int n);
  • int sum(int [],int);

数组变量是特殊的指针,数组变量本身表达地址,所以:

  • int a[10];int *p = a;//无需&取地址

  • 但是数组的单元表达的是变量,需要用&取地址

    • a == &a[0];
  • 运算符[]可以对数组做,也可以对指针做:

    • p[0] < == > a[0]
  • *运算符可以对指针做,也可以对数组做

    • *a=25;
  • 数组变量是const的指针,所以不能被赋值

    • int a[] 等价于 int * const a = …

12.2.5指针与const

  • 指针是一种变量,有两个,一个指针本身,一个是指针所指的那个变量,指针本身可以是const,指针指向的那个变量也可以是const
    1.指针是const

int * const q = &i;//q是const
*q = 26;//ok
q++;//error

即q的值变成了常量,即i的地址,也就是q指向了i这个事实不可变,不可以再指向别人了。
这时q就是i的值,是可以改变的,只是与q绑定了地址,改变q值依旧可以改变i值
2.所指向的是const

const int *p = &i;
*p = 26;//error,(*p)是const
i=26;//ok
p=&j;//ok

这里p是可以改变的,可以访问其他地址,i的值也是可以改变的,只不过p无法直接改变i的值了,只能通过改变自身的存放的地址被动接受变量的值,无法主动改变 ,const只能读无法写

判断哪个被const的标志为const在*的前面还是后面

12.3指针的运算

12.3.1指针的算术运算

指针可以做如下算术运算:
- 加、减一个整数(+,+=,-,-=)
- 递增递减(++,–),就是挪到下一个位置
- 两个指针相减,结果不是两个地址的差,而是地址的差/sizeof这个类型,即这两个地址中间,能放几个这样的值

#include<stdio.h>

int main(void) {
	//sizeof(char)=1;sizeof(int)=4
	//指针加1实际上是移动一个sizeof(type)

	char ac[] = { 5,8,3,7,0,3,35,27 };
	char *p = ac;
	char* p1 = &ac[5];
	printf("p1-p=%d\n",p1-p);
	printf("p = %p\n", p);
	printf("p+1 = %p\n", p+1);
	printf("*(p+1)=%d\n", *(p + 1));//*p -> ac[0];*(p+1) -> ac[1]
	int ai[] = { 5,8,3,7,0,3,35,27 };
	int* q = ai;
	int* q1 = &ai[6];
	printf("q1-q=%d\n", q1 - q);
	printf("q = %p\n", q);
	printf("q+1 = %p\n", q + 1);
	printf("*(q+1)=%d\n", *(q + 1));//*(q+n) -> ai[n]

	return 0;

}

运行结果:
在这里插入图片描述

12.3.2*p++

  • 后置++,先进行运算,即取出p所指的那个数据,然后p值加1,即p移到下一个位置去
  • *的优先级比++低
  • 可以用在遍历上
    在这里插入图片描述

12.3.3指针的比较

在这里插入图片描述

12.3.4 指针0地址

  • 每个程序都有一个0地址,但是通常是不能随便碰的地址,即地址值为0
  • 指针不应该是0地址
  • 0地址返回的指针是无效的
  • 指针没有被真正初始化(先初始化为0),如果有指针变量,先给它赋值为0,等于没有给它有意义的东西,并对它做操作系统崩溃或者报错
  • 一般用NULL表示0地址,但是有的程序0地址和NULL不太一样

12.3.5 指针的类型

  • 无论指向什么类型,所有的指针大小都是一样的,因为都是地址
  • 但是指向不同类型的指针是不能直接互相赋值的
  • 这是为了避免用错指针
  • 类型转换:
    在这里插入图片描述

12.4动态内存分配

12.4.1malloc

在这里插入图片描述
小案例:
封装一个函数,利用冒泡排序,实现对整型数组的升序排序

#include<iostream>
using namespace std;


void bubbleSort(int* arr,int len) {
	for (int i = 0; i < len - 1; i++) {
		for (int j = 0; j < len - i - 1; j++) {
			if (arr[j] > arr[j + 1]) {
				//互换位置
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

void printArray(int* arr, int len) {
	for (int i = 0; i < len; i++) {
		cout << arr[i] << "  ";
	}
}

int main(void) {

	//1.创建一个数组
	int arr[10] = { 4,6,5,56,7,3,2,745,2,6 };

	//2.定义一个冒泡函数
	//定义数组长度
	int len = sizeof(arr) / sizeof(arr[0]);
	bubbleSort(arr, len);

	//3.打印排序后的数组
	printArray(arr, len);
	return 0;
}

运行结果:
在这里插入图片描述

第五篇笔记到此结束,C++基础学习会持续更新在C++学习笔记合集中,当作学习笔记复习,如果能帮助其他小伙伴就更好了。
指针笔记是看翁恺老师MOOC慕课以及黑马程序C++时做的记录,笔记中如果有错误和改进的地方,欢迎大家评论交流,up up up!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值