目录
7 指针
7.1 指针的基本概念
指针的作用: 可以通过指针间接访问内存。
内存编号是从0开始记录的,一般用十六进制数字表示。
可以利用指针变量保存地址。
内存都是有地址编号的,从0开始记录,一般用十六进制数字来表示编号,可以利用指针来记录地址编号。
int a = 10; // 4字节空间大小
每创建一个变量,都要去记录地址编号,不太方便去使用这个数据,所以才会有这个变量。
知道地址编号,也可以拿到数据。指针就是一个地址,记录地址编号!
7.2 指针变量的定义和使用
指针变量定义语法:
数据类型 * 变量名;
示例
指针变量和普通变量的区别:
普通变量存放的是数据,指针变量存放的是地址。
指针变量可以通过 “ * ” 操作符,操作指针变量指向的内存空间,这个过程称为解引用。
通过p可以找到内存,通过*p可以修改内存。
指针的作用(用途):可以间接地访问内存,并且对其进行读和写的操作(修改、访问)!
#include <iostream>
using namespace std;
int main()
{
//1、指针的定义
int a = 10; //定义整型变量a
//指针定义的语法:数据类型 * 指针变量名 ;
int *p; // point代表指针
//指针变量赋值 让指针记录变量a的地址
p = &a; //指针p指向(等于)变量a的地址【取地址符号& 取变量a的地址】建立变量与指针之间的关系
cout << "a的地址为:" << &a << endl; //打印数据a的地址:0x61fe14
// cout << "a的地址为:" << (int)&a << endl; //十六进制转整型
cout << "指针p为:" << p << endl; //打印指针变量p:0x61fe14【指针就是一个地址,记录地址编号!】
//2、指针的使用
//可以通过“解引用”的方式来找到指针指向的内存
//指针前加一个* 代表 解引用,找到指针指向的内存中的数据
//通过*操作指针变量指向的内存
cout << "*p = " << *p << endl; // 10
*p = 1000; //解引用 通过指针间接地找到了a的内存 通过解引用拿到p指向的内存中的数据(进行 修改、读取)
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
system("pause");
return 0;
}
总结1: 我们可以通过 & 符号 获取变量的地址。
总结2:利用指针可以记录地址。
总结3:对指针变量解引用,可以操作指针指向的内存。
7.3 指针所占内存空间
提问:指针也是种数据类型,那么这种数据类型占用多少内存空间?
总结:所有指针类型在32位操作系统下是4个字节,在64位操作系统下是8字节。
示例
#include <iostream>
using namespace std;
int main() //指针所占内存空间
{
int a = 10;
// int *p;
// p = &a; //指针指向数据a的数据地址
int *p = &a; //建立关系
//在32位操作系统下,指针是占4个字节空间大小(不管是什么数据类型)
//在64位操作系统下,指针是占4个字节空间大小(不管是什么数据类型)
cout << *p << endl; //10 * 解引用
cout << "sizeof(p) = " << sizeof(p) << endl; //8 查看变量(数据类型)占用的内存空间
cout << "sizeof(int *) = " << sizeof(int *) << endl; //8
cout << "sizeof(float *) = " << sizeof(float *) << endl; //8
cout << "sizeof(double *) = " << sizeof(double *) << endl; //8
cout << "sizeof(char *) = " << sizeof(char *) << endl; //8
system("pause");
return 0;
}
7.4 空指针和野指针
空指针:指针变量指向内存中编号为0的空间。内存条,都有自己的编号,从0开始递增。指向编号为0的指针,称为“空指针”!
用途:初始化指针变量。指针一开始不知道指向哪里好,就指向内存中编号为0的空间。
注意:空指针指向的内存是不可以访问的。无权利访问空指针指向的内存。0~255之间的这块内存,是系统占用的,一旦访问,就会出错!
示例1:空指针
#include <iostream>
using namespace std;
int main() //空指针
{
//1、空指针用于给指针变量进行初始化
// int * p;//指针指向哪?未知!所以,一般会让指针指向NULL(空)
int *p = NULL; //指针变量p指向内存地址编号为0的空间
//2、空指针是不可以进行访问的
//0~255之间的内存编号是系统占用的,因此不可以访问
*p = 100; //直接引用 操作内存
//访问空指针报错
//内存编号0 ~255为系统占用内存,不允许用户访问
cout << *p << endl;
//system("pause");
return 0;
}
示例2:野指针
野指针:指针变量指向非法的内存空间。非法的内存空间:不是使用者申请的内存空间。
#include <iostream>
using namespace std;
int main() //野指针 在程序中,尽量避免出现野指针
{
// int *p = NULL;
//指针变量p指向内存地址编号为0x1100的空间【0x1100:十六进制数字】
int *p = (int *)0x1100; //变为地址:(int *)强转为指针类型
//0x1100随便在内存中指向了这样一个编号,这个编号中的数 无权利操作!没有申请,无权利操作!
//举例:花钱买房间A(int a = 10; int *p = &a;),没有权利去房间B(房间B->野指针)
cout << *p << endl; //访问野指针报错
system("pause");
return 0;
}
总结
总结:空指针和野指针都不是我们申请的空间,因此不要访问。
7.5 const修饰指针
const修饰指针有3种情况:
const 修饰指针 —— 常量指针 const int *p1 = &a;
const 修饰常量 —— 指针常量 int *const p2 = &a;
const 既修饰指针,又修饰常量
1、const修饰指针——常量指针
红框:被限定,不可以修改;黑线:可以修改。
2、const修饰常量——指针常量
红线:被限定,不可以修改;黑框:可以修改。
3、const既修饰指针,又修饰常量
示例
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int b = 10;
//int *p0 = &a; //普通写法
//1、常量指针(记法:const在前 先常量 后指针)
//const修饰的是指针,指针的指向可以更改,指针指向的值不可以更改(可以理解为const 修饰的是解引用 int*,所以指针指向的值不可以更改)
const int *p1 = &a;
p1 = &b; //正确
// *p1 = 20; //错误
//2、指针常量(记法:int* 在前 先指针 后常量)
//const修饰的是常量,指针的指向不可以更改,指针指向的值可以更改(可以理解为const修饰的是指针本身,所以指针指向的值不可以修改)
int *const p2 = &a;
*p2 = 100; //正确
//p2 = &b; //错误,指针常量 指针的指向不可以更改
//3、const既修饰指针又修饰常量
const int *const p3 = &a;
//指针的指向和指针指向的值都不可以改
//*p3 = 100; //错误
//p3 = &b; //错误
system("pause");
return 0;
}
技巧:看const右侧紧跟着的是指针还是常量,是指针就是常量指针,是常量就是指针常量。
7.6 指针和数组
作用:利用指针访问数组中元素。数组:一段连续的空间中,存放了相同类型的数据元素。
示例
#include <iostream>
using namespace std;
int main()
{
//指针和数组
//让(利用)指针也能访问数组中的每一个元素
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << "第一个元素为:" << arr[0] << endl;
int *p = arr; //指向数组的指针 数组是整型的,所以创建整型指针,指向数组的地址 arr数组名就是数组的首地址
//指针指向数组首地址,对指针进行解引用的操作,就可以解出数组中的第一个元素
cout << "利用指针访问第一个元素:" << *p << endl; //*p解引用
p++; //指向数组中的第二个元素,让指针向后偏移(移动)4个字节(整型指针)
cout << "利用指针访问第二个元素:" << *p << endl; //*p解引用
cout << "利用指针遍历数组:" << endl;
int *p2 = arr;
for (int i = 0; i < 10; i++) //利用指针遍历数组
{
// cout << arr[i] << endl;
cout << *p2 << endl;
p2++;
}
system("pause");
return 0;
}
7.7 指针和函数
作用:利用指针作函数参数,可以修改实参的值。
示例
指针保存的是地址。传递地址,可以间接地改变实参数据。
#include <iostream>
using namespace std;
//1、值传递:实现两个数字进行交换
void swap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "swap01中 a = " << a << endl;
cout << "swap01中 b = " << b << endl;
}
//2、地址传递
void swap02(int *p1, int *p2)
{
int temp = *p1; //解出内存 解引用
*p1 = *p2;
*p2 = temp;
cout << "swap02中 *p1 = " << *p1 << endl;
cout << "swap02中 *p2 = " << *p2 << endl;
}
int main() //指针和函数
{
int a = 10;
int b = 20;
//1、值传递不会改变实参
swap01(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
//2、地址传递会改变实参
//将a、b变量地址传入函数体中,用指针接受地址【如果是地址传递,可以修饰实参】
swap02(&a, &b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
总结:如果不想修改实参,就用值传递,如果想修改实参,就用地址传递。
7.8 指针、数组、函数
案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序。
例如数组:int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
示例
#include <iostream>
using namespace std;
//冒泡排序函数【参数1:数组首地址;参数2:数组长度】
void bubbleSort(int *arr, int len) //int * arr 也可以写为int arr[]
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
if (arr[j] > arr[j + 1]) //如果j > j + 1的值,交换数字
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//打印数组函数
void printArray(int arr[], int len) //void printArray(int *arr, int len)
{
for (int i = 0; i < len; i++)
{
// cout << arr[i] << endl;
cout << arr[i] << "、";
}
}
int main()
{
//1、创建一个数组
int arr[10] = {4, 3, 6, 9, 1, 2, 10, 8, 7, 5};
//数组长度
int len = sizeof(arr) / sizeof(int);
//2、创建一个函数实现冒泡排序
bubbleSort(arr, len); //传递数组地址arr:数组名就是数组的首地址
//3、打印排序后的数组
printArray(arr, len); //传递数组地址arr
system("pause");
return 0;
}
总结:当数组名传入到函数作为参数时,被退化为指向首元素的指针。