总览
主要讲了 c++ 中指针的概念、定义和使用、const以及与函数、数组的使用关系
指针的基本概念
作用:可以通过指针间接访问内存
- 内存的编号是从 0 开始记录的,一般用 16 进制数字表示
- 可以利用指针变量保存地址
指针变量的定义和使用
指针变量定义语法:数字类型 * 变量名
c++
#include <iostream>
using namespace std;
int main() {
int a = 10;
// 1. 定义指针
int * p;
// 将指针指向 a 的地址
p = &a;
// 2. 使用指针:解引用(指针前加*号)
// 通过解引用方式来找到指针指向的内存,找到指针指向内存中的数据
*p = 1000;
// 结果为两个 1000,即已经将0x16f433018地址的数修改了
cout << a << endl;
cout << *p << endl;
return 0;
}
指针所占内存空间
指针也是一种数据类型,那么指针占用的内存空间是多大呢?
c++
#include <iostream>
using namespace std;
#include "swap.h"
int main() {
int a = 10;
// 1. 定义指针
int * p;
// 将指针指向 a 的地址
p = &a;
// 64 位系统 8 字节
// 若是 32 位则为 4 字节
cout << sizeof(p) << endl;
return 0;
}
空指针和野指针
空指针
定义:指针变量指向内存中编号为 0 的空间
用途:初始化指针变量
重点:空指针指向的内存是不可访问的
c++
#include <iostream>
using namespace std;
#include "swap.h"
int main() {
// 1. 定义指针
int * p = NULL;
*p = 100;
// 内存编号 0-255 为系统占用内存,不允许用户访问
cout << *p << endl;
return 0;
}
野指针
指针变量指向非法的内存空间
c++
#include <iostream>
using namespace std;
int main() {
// 指针变量 p 指向内存地址编号为 0x1100 的空间
int * p = (int *)0x1100;
// 访问野指针报错
cout << *p << endl;
return 0;
}
重点:空指针和野指针本质上都不是我们自己申请的空间,不要随意的去访问它
const 修饰指针
const 修饰指针:常量指针
特点:指针的指向可以修改,但是指针指向的值不可以修改
- 常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改
- 常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值
- 指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址
c++
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
// 常量指针
const int * p = &a;
a = 20; // ok,仍然可以通过原来的声明修改值
// *p = 20; Error,*p 是const int的,不可修改,即常量指针不可修改其指向地址
p = &b; //OK,指针还可以指向别处,因为指针只是个变量,可以随意指向;
return 0;
}
const 修饰常量:指针常量
本质是一个常量,而用指针修饰它。指针常量的值是指针,这个值因为是常量,所以不能被赋值。
- 它是个常量!
- 指针所保存的地址可以改变,然而指针所指向的值却不可以改变
- 指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
c++
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
// 常量指针
int * const p = &a;
a = 20; // OK,仍然可以通过原来的声明修改值
*p = 20; // OK,指针是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化
// p = &b; Error,因为p2是const 指针,因此不能改变p2指向的内容
return 0;
}
const 修饰指针和常量
指针就是一个常量,且它指向的对象也是一个常量。
c++
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
// 常量指针
const int * const p = &a;
a = 20; // OK
//*p = 20; ERROR
//p = &b; Error
return 0;
}
技巧:看 const 后面修饰的是指针(*) 还是常量(p),如果修饰的是指针,那就是常量指针;修饰的是常量,那就是指针常量
指针和数组
作用:利用指针访问数组元素
c++
#include <iostream>
using namespace std;
int main() {
//利用指针访问数组
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 第一个元素
int * p = arr; // arr 就是数组首地址
cout << *p << endl;
// 第二个元素
// 因为数组里存放的是 int,所以只需要指针向后移动 4 个字节即可
// 同时指针也是一个 int 类型,所以 p++ 就是让指针向后移动 4 个字节
p++;
cout << *p << endl;
// 遍历
int * p2 = arr;
for (int i = 0; i < 10; i++) {
cout << *p2++ << endl;
}
return 0;
}
指针和函数
作用:利用指针做函数参数,可以修改实参的值
值传递
前面我们讲到了 c++是值传递,形参的改变不会影响形参的值,这就是值传递
c++
#include <iostream>
using namespace std;
// 返回值类型 函数名(参数列表)
void swap(int a, int b) {
int tmp = a;
a = b;
b = tmp;
}
int main() {
int a = 10;
int b = 20;
swap(a, b);
// 因为是值传递,所以a=10,b=20没有变化
cout << a << b << endl;
return 0;
}
地址传递
c++
#include <iostream>
using namespace std;
// 返回值类型 函数名(参数列表)
void swap(int *p1, int *p2) {
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
int main() {
int a = 10;
int b = 20;
swap(&a, &b);
// 因为是地址传递,所以a=20,b=10已经交换
cout << a << b << endl;
return 0;
}
本质上地址传递也是值传递,因为传递的是地址的值
课后作业
描述:封装一个函数,利用冒泡排序,实现对整数数组的排序
c++
#include <iostream>
using namespace std;
// 这么传 int arr[] 也可以
void bubble_sort(int * arr, int size) {
for (int i = 0; i < size-1; i++) {
for (int j = 0; j < size - 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 size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
}
int main() {
int arr[9] = { 9, 8, 5, 4, 3, 6, 1, 7, 2 };
// 预计算数组长度
int size = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, size);
printArray(arr, size);
return 0;
}
如何将数组作为参数传入?
- 传入数组的引用
- 传入数组及其大小