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!!!