指针
目录
一 为什么使用指针
- 函数的值传递,无法通过调用函数,来修改函数的实参
- 被调用函数需要提供更多的“返回值”给调用函数
- 减少值传递时带来的额外开销,提高代码执行效率
二 指针的定义与读写
//------------ 指针的定义 -------------
int age = 0;
int *p = NULL; // p是一个指针,p的值就是一个整数的地址
P = &age;
*p = 4; // age = 4
//-------------- 指针的读 --------------
// 读指针变量本身
float w_daige = 80.5;
float* p = &w_daige;
printf("%p", p); // 0019FED8
// 读指针变量指向的内容
float w_daige = 80.5;
float* p = &w_daige;
printf("%.2f", *p); // 80.50
//-------------- 指针的写 --------------
// 写指针变量本身
float w_daige = 80.5;
float *p = &w_daige;
float w_xiaoyu = 50;
p = &w_xiaoyu;
// 写指针变量指向的内容
float w_daige = 80.5;
float *p = &w_daige;
*p = 60; // w_daige = 60;
32 位系统中,int 整数占 4 个字节,指针占 4 个字节
64 位系统中,int 整数占 4 个字节,指针占 8 个字节
三 空指针和野指针
空指针,就是值为0的指针,任何程序数据都不会存储在地址为0的内存块中,它是被操作系统预留的内存块
int *p = 0;
int *p = NULL;
- 空指针的使用
- 指针初始化为空指针,避免访问非法数据
- 指针不再使用时,可以设置为空指针
- 表示这个指针还没有具体的指向,使用前进行合法性判断
// 1 指针初始化为空指针,避免访问非法数据
int *p = NULL;
// 2 指针不再使用时,可以设置为空指针
int a = 3;
int *p = &a;
*p = 5;
p = NULL;
// 3 表示这个指针还没有具体的指向,使用前进行合法性判断
int *p = NULL;
if(p) // 等价于 p != NULL
{
}
- 野指针:没有进行初始化
int *p; // 野指针
四 指针与整数加减运算
指针加减数字表示的意义是指针在数组中位置的移动; 对于整数部分而言,它代表的是一个元素,对于不同的数据类型,其数组的元素占 用的字节是不一样的, 比如指针 + 1,并不是在指针地址的基础之上加 1 个地址,而是在这个指针地址的 基础上加 1 个元素占用的字节数
- p++:在 p 当前地址的基础上 ,自增 p 对应类型的大小,p = p + 1*(sizeof(类型))
- p--:在 p 当前地址的基础上 ,自减 p 对应类型的大小,p = p - 1*(sizeof(类型))
#include <iostream>
using namespace std;
int main()
{
int arry[] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = arry;
cout << p << endl; // array[0] 0019FEB4
/* 1.p++; 在 p 当前地址的基础上, 自增 p 对应类型的大小,p = p + 1 * (sizeof(类型)) */
p++;
cout << p << endl; // array[1] 0019FEB8
/* 2.p--; */
p--;
cout << p << endl; // array[0] 0019FEB4
/* 3.*p; */
cout << *p << endl; // array[0] 0
/* 4.*p++;
*和++的优先级相同,运算顺序从右向左,所以*p++就相当于*(p++);
这里存在一个知识误区:本应该先执行括号内的p++,使p从指向a[0]变成指向a[1];
但其实事实并非如此;事实是,对于直接对*p++的输出来说,第一步是运算*p,输出后,第二步再运算p++
*/
cout << *p++ << endl; // array[0] 0
cout << p << endl; // array[1] 0019FEB8
/* 5.*p--; */
cout << *p-- << endl; // array[1] 1
cout << p << endl; // array[0] 0019FEB4
/* 6.*p+5; */
cout << *p + 5 << endl; // array[0] 0+5
cout << p << endl; // array[0] 0019FEB4
/* 7.*(p+5); */
cout << *(p + 5) << endl; // array[5] 5
cout << p << endl; // array[0] 0019FEB4
return 0;
}
五 指针与指针的比较运算
// 指针与指针的比较
#include <iostream>
#include <string>
using namespace std;
int main()
{
string gong_lue[5] = { "秀逼格", "吃夜宵", "看电影", "订房", "谈理想" };
string* p;
string* p1 = gong_lue;
string* p2 = p1 + sizeof(gong_lue) / sizeof(gong_lue[0]);
while (p1 < p2)
{
cout << *p1 << endl;
p1++;
}
return 0;
}
六 指针与指针的减法运算
指针和指针做减法适用的场合:两个指针都指向同一个数组,相减结果为两个指针之间的元素数目,而不是两个指针之间相差的字节数
// 指针的减法运算
#include<iostream>
#include <string>
using namespace std;
int main()
{
string gong_lue[5] = { "秀逼格", "吃夜宵", "看电影", "订房", "谈理想" };
string* p;
string* p1 = gong_lue;
string* p2 = &gong_lue[4];
printf("p1: %p\n", p1); // p1: 0019FE44
printf("p2: %p\n", p2); // p2: 0019FEB4
printf("%d\n", sizeof(string)); // 28
printf("p2-p1=%d\n", p2 - p1); // p2-p1=4
printf("p2-p1=%d\n", (unsigned int)p2 - (unsigned int)p1); // p2-p1=112 (28*4=112)
return 0;
}
VC++ 关于Sizeof(string) 为何是28(x86)40(x64)个字节
七 指针的强制类型转换
#include <stdio.h>
#include <string>
int main()
{
int x = 0x31323334; // 31 32 33 34 ---> 1 2 3 4
char* p = (char*)&x;
for (int i = 0; i < 4; i++)
{
printf("%c ", *p);
p++;
}
printf("\n");
return 0;
}
八 指针与const
看 const 离类型(int)近,还是离指针变量名近,离谁近,就修饰谁,谁就 不能变
#include <iostream>
int main()
{
int value = 0;
int value1 = 0;
const int* p = NULL;
p = &value;
//*p = 5; // const离类型近,value的值不能被改变
int* const p1 = &value;
*p1 = 5;
//p1 = &value1; // const离指针变量名近,p1的指向不能改变
return 0;
}
九 二级指针
二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址
int guizi1 = 888;
int *guizi2 = &guizi1; //1 级指针,保存 guizi1 的地址
int **liujian = &guizi2; //2 级指针,保存 guizi2 的地址,guizi2 本身是一个一级指针变量
二级指针的用途
- 普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数
#include <iostream>
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int num1 = 2;
int num2 = 3;
swap(&num1, &num2);
std::cout << "num1:" << num1 << " num2:" << num2 << std::endl;
return 0;
}
- 二级指针可以不但可以将变量通过参数函数内部,也可以将函数内部变量 “带出”到函数外部。
#include <iostream>
void boyHome(int** matchmaker)
{
static int boy = 23;
*matchmaker = &boy;
}
int main()
{
int* meipo = NULL;
boyHome(&meipo);
*meipo = 32;
std::cout << "boy:" << *meipo << std::endl;
return 0;
}
十 指针和数组
10.1 指针表示法和数组表示法
数组完全可以使用指针来访问, days[3] 和 *(days+3) 等同
#include <iostream>
#include <iomanip>
using namespace std;
void printMonths1(int days[], int months)
{
cout << "--------- 数组表示法 ----------" << endl;
for (int i = 0; i < months; i++)
{
cout << setw(2) << i + 1 << "月有" << days[i] << "天" << endl;
}
}
void printfMonths2(int* days, int months)
{
cout << "--------- 指针表示法 ----------" << endl;
for (int i = 0; i < months; i++)
{
cout << setw(2) << i + 1 << "月有" << *(days + i) << "天" << endl;
}
}
int main()
{
int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
printMonths1(days, sizeof(days) / sizeof(days[0]));
printfMonths2(days, sizeof(days) / sizeof(days[0]));
return 0;
}
10.2 指针数组和数组指针
#include <iostream>
using namespace std;
int main()
{
//---------------- 指针数组 ----------------
int* p1[2]; // 定义了一个有两个元素的指针数组,每个元素都是一个指针变量
int num1 = 172;
int num2 = 173;
p1[0] = &num1;
p1[1] = &num2;
//---------------- 数组指针 ----------------
int (*p2)[3]; // 定义一个指向三个成员的数组的指针
int arry[4][3] = {
{0,1,2},
{3,4,5},
{6,7,8},
{9,11,12}
};
p2 = &arry[0];
// 数组法访问元素 (*p)[j]
cout << "---------- 数组法访问 ----------" << endl;
for (int i = 0; i < sizeof(arry) / sizeof(arry[0]); i++)
{
for (int j = 0; j < sizeof(arry[0]) / sizeof(arry[0][0]); j++)
{
cout << (*(p2 + i))[j] << " ";
}
cout << endl;
}
// 指针法访问元素 *((*p)+j)
cout << "---------- 指针法访问 ----------" << endl;
for (int i = 0; i < sizeof(arry) / sizeof(arry[0]); i++)
{
for (int j = 0; j < sizeof(arry[0]) / sizeof(arry[0][0]); j++)
{
cout << *(*(p2 + i) + j) << " ";
}
cout << endl;
}
return 0;
}
int main()
{
int a[3][4] = { 0 };
int (*p)[4] = a; //数组指针:定义一个指向四个成员的数组的指针
// 给二维数组赋值
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
static int count = 1; // static只初始化一次
a[i][j] = count++;
}
}
// 用指针法访问二维数组
cout << *(*(p + 1) + 2) << endl;
return 0;
}
十一 函数指针
#include <iostream>
using namespace std;
void swap(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int num1 = 1;
int num2 = 2;
// 1.函数指针的定义 把函数声明移过来,把函数名改成 (* 函数指针名):
void (*fp)(int* a, int* b);
fp = &swap;
// 2.调用
/*贝尔实验室的C和UNIX的开发者采用第1种形式,而伯克利的UNIX推广者却采用第2种形式, ANSI C 兼容了两种方式*/
(*fp)(&num1, &num2); // 第2种,按普通指针解引的方式进行调用 // 交换完:num1:2 num2:1
fp(&num1, &num2); // 第2种,直接调用 // 交换完:num1:1 num2:2
// 3.给函数指针起别名
typedef void(*swap_t)(int* a, int* b);
// 用别名进行调用
((swap_t)&swap)(&num1, &num2); // 交换完:num1:2 num2:1
cout << num1 << num2 << endl; // 2 1
return 0;
}