- 前言
- 1.指针的定义、初始化和访问
- 1.1 指针的定义和初始化
- 1.2 指针的访问
- 2.空指针和坏指针
- 2.1 空指针
- 2.2 坏指针
- 3.指针的加减运算
- 3.1 指针与常数
- 3.2 指针与指针
- 4.const 对指针的修饰
- 4.1 const int *p
- 4.2 int *const p
- 4.3 const int * const p
- 5.二级指针和多级指针
- 5.1 二级指针的定义和访问
- 5.2 多级指针
- 6.指针作为函数的参数
- 6.1 为什么要使用指针
- 6.2 指针的应用
- 7.指针与数组
- 7.1 指针数组与数组指针
- 7.1.1 指针数组
- 7.1.2 数组指针
- 7.2 一维数组、二维数组、指针数组作为函数的参数传递
- 8.void类型的指针
- 9.函数指针
前言
本篇文章主要介绍了C/C++指针的用法
1.指针的定义、初始化和访问 1.1 指针的定义和初始化
指针定义时 * 两边的缩进并无要求(这里有几种缩进风格):
(1)int *p;
(2)int * p;
(3)int* p;
(4)int*p; //这种形式是不推荐的
在定义时,没有指向的指针(暂时不用初始化),我们一般初始化为 NULL 或 0 (避免出现坏指针)
#include
#include
/*
指针本身也是一个变量,但真值的内存空间中存储的是其他变量内存空间的地址
*/
int main(void){
//首先定义基本的变量
int age = 18;
char ch = 'T';
//然后定义指针分别存储age和ch的地址
//指针需要指向什么样的数据类型,就定义该类型的指针
int *p;
char *c;
//注意:这里p1是指针,但p2只是一个整形变量
int *p1, p2;
//指针的初始化
p = &age; //&是取地址符,在scanf("%d",&age);函数中使用过
c = &ch;
system("pause");
return 0;
}
1.2 指针的访问
对于一个指针变量(p),我们可以访问指针的地址(&p),指针的值(p)和指针所指向的值(*p)
#include
#include
using namespace std;
int main(void) {
int room = 80;
char ch = 'c';
int *p1 = &room;
char *p2 = &ch;
//1.访问指针变量内存中的值与其他普通变量的方式相同
//地址建议用printf输出用%p(会用4个字节的格式化输出),或者使用16进制打印(%x,%X)
printf("&p1 = %p\n", &p1);
printf("p1 = %p\n", p1);
printf("p1 = 0x%x\n", unsigned int(p1));
printf("p1 = 0X%X\n", unsigned int(p1));
//2.访问指针所指向的元素用 *p ,‘*’叫做间接访问运算符
printf("*p1 = %d\n", *p1); //输出为:80
*p1 = 40;//*p1可以改变room的值
printf("room = %d\n", room);
/*cout << "&p1:" << &p1 << endl;
cout << "p1:" << p1 << endl;
cout << "*p1:" << *p1 << endl;*/
//3.指针所占字节数:在32位系统(x86)中指针占用4个字节的空间,但在64位系统(x64)中占用8个字节
// x86 x64
printf("sizeof(room) = %d\n", sizeof(room)); //output: 4 4
printf("sizeof(p1) = %d\n", sizeof(p1)); //output: 4 8
printf("sizeof(ch) = %d\n", sizeof(ch)); //output: 1 1
printf("sizeof(p2) = %d\n", sizeof(p2)); //output: 4 8
system("pause");
return 0;
}
2.空指针和坏指针 2.1 空指针
空指针:
(1)空指针,就是值为0的指针,
(2)指针不再使用时,推荐置为空
(3)不确定指针具体指向时,使用前可以进行合法性检查
#include
#include
using namespace std;
int main() {
/*
如果使用了没有初始化的指针,程序是不能通过编译的
*/
//int *p;
//cout << "p:" << p << endl;
/*
但如果我们打印空指针的地址,程序可以通过编译
*/
int *p = NULL;//NULL是一个宏定义#define NULL 0
cout << "p:" << p << endl;//输出为00000000(x86),(这表示为空指针)
cout << "*p:" << p << endl;//这里会造成程序中断执行,因为0地址是不允许被程序访问的,然后在使用前做合法性检查。
system("pause");
return 0;
}
2.2 坏指针
坏指针:
(1)没有初始化就使用的指针
(2)指定不属于你应用程序变量的地址,比如我们随便赋值为指针 int *p = 100;
#include
#include
using namespace std;
int main() {
/*
这里如果我们输入的不是666或者888,那么p就没有被赋值,此时p虽然分配了内存空间,
但依旧没有初始化,此时p就变成了一个野指针,要防范野指针可以先使用空指针进行初始化
*/
int room1 = 666;
int room2 = 888;
//int *p;
int *p = NULL;
int select = 0;
cout << "请输入房间号:";
cin >> select;
if (select == 666) {
p = &room1;
} else if(select == 888){
p = &room2;
}
//cout << *p << endl;
//合法性检查
if(p){
cout << *p << endl;
}
system("pause");
return 0;
}
3.指针的加减运算 3.1 指针与常数
#include
#include
/*
p++ 的概念是在 p 当前地址的基础上 ,自增 p 对应类型的大小, 也就是说 p = p+ 1*(sizeof(类型))
指针的自减运算
同样指针的加减法运算都是通过类型大小来计算的,如:
设int型数组的第一个地址为00F3FC90,那么指向该数组的指针p+1 = 00F3FC90 + 4
如果是char型数组那么 p + 1 = 00F3FC90 + 1
所以指针 p + n = %p + n*sizeof(type)
*/
int main(void) {
int arr[] = { 1,2,3,4,5,6,7 };
int *p = arr;
int len = sizeof(arr) / sizeof(arr[0]);
char chs[] = { 'a','b','c','d','e' };
char *c = chs;
//1.使用数组的形式来访问数组元素
for (int i = 0; i < len; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
//2.数组的地址就是数组第一个成员的地址
printf("arr的地址:%p,arr[0]的地址:%p\n", &arr, &arr[0]);
//通过指针访问第一个元素,并打印地址
printf("arr[0] = %d,&arr = %p\n", *p, p);
//通过指针访问第二个元素,并打印地址
printf("arr[1] = %d,&arr = %p\n", *(p + 1), p + 1);
//通过指针访问chs中的元素
printf("chs[0] = %c,&arr = %p\n", *c, c);
printf("chs[1] = %c,&arr = %p\n", *(c + 1), c + 1);
for (int i = 0; i < len; i++) {
printf("arr[%d] = %d,adress = %p\n", i, *p++,&arr[i]);
}
p -= len;
for (int i = 0; i < len; i++) {
printf("arr[%d] = %d,\n", i, *p);
p += 1;
}
c = &chs[4];
for (int i = 4; i >= 0; i--) {
printf("chs[%d] = %c,adress = %p\n", i, *c--, &chs[i]);
}
system("pause");
return 0;
}
3.2 指针与指针
#include
#include
/*
指针与指针的加减运算跟上方道理一样
使用指针与指针的运算计算元素的偏移值
*/
int main0106(void) {
int ages[] = { 16,18,20,22,26,30,36 };
int ages1[] = { 12,24,26,60 };
int len = sizeof(ages) / sizeof(ages[1]);
int *tree = ages + 4;
int *martin = ages + 6;
int *rock = ages1 + 3;
printf("martin - tree = %d\n", martin - tree);
printf("tree - martin = %d\n", tree - martin);
//不同数组之间(同类型)也可以运算,但大多数情况下没有意义
printf("rock:%p,tree:%p,rock - tree = %d\n", rock, tree, rock - tree);
printf("tree - rock = %d\n", tree - rock);
system("pause");
return 0;
}
4.const 对指针的修饰 4.1 const int *p
const int *p = &a 与 int const *p = &a等效,都表示 *p 只能读取a的值,但不能修改a的值
4.2 int *const pint *const p = &a 表示 p 的值不能改变(p只能指向a,不能再被赋为其他值)
4.3 const int * const pconst int * const p = &a,此时,*p和p都不能修改了
#include
#include
int main(void) {
//1.const int * 与 int const * 都表示不能修改指针所指内存的值(*p),但可以改变所指向的位置(p)
int age1 = 30;
int age2 = 24;
int const *p1 = &age1;
const int *p2 = &age1;
printf("age1的地址:%p,age2的地址:%p\n", &age1, &age2);
printf("\n修改前:\np1的值:%d\tp2的值:%d\np1的地址:%p\tp2的地址:%p\n"
,*p1,*p2,p1,p2);
//*p1 = 25;
//*p2 = 25; //错误提示为表达式必须是可修改的左值,说明*p2此时是不可修改的
p1 = &age2;
p2 = &age2;
printf("修改p1,p2后:\np1的值:%d\tp2的值:%d\np1的地址:%p\tp2的地址:%p\n"
, *p1, *p2, p1, p2);
//2.int * const p:这时,指针只能修改所指内存的值(*p),不能修改地址(p)
int *const p3 = &age1;
printf("\n修改前:\np3的值:%d\np3的地址:%p\n"
, *p3, p3);
//p3 = &age2;//错误同上
*p3 = 26;
printf("修改*p3后:\np3的值:%d\np3的地址:%p\n"
, *p3, p3);
//3.const int * const p:这时p和*p都不能更改
const int *const p4 = &age1;
printf("\n修改前:\np4的值:%d\np4的地址:%p\n"
, *p4, p4);
/*p4 = &p2;
*p4 = 26;*/
printf("我比较苦B,啥也改不了");
system("pause");
return 0;
}
5.二级指针和多级指针 5.1 二级指针的定义和访问
#include
#include
/*
二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址
*/
int main(void) {
int guizi = 888; //柜子里存放q
int *guiziA = &guizi; //柜子A里存放guizi的地址
int **lilianjie = &guiziA; //lilianjie手里拿着guiziA的地址
printf("guizi的地址:%p\n", &guizi);
printf("guiziA存放的地址:%p\n", guiziA);
printf("lilianjie首先打开guiziA,拿到guizi的地址:%p\n", *lilianjie);
int *tmp = *lilianjie;
printf("拿到guizi的地址后,打开柜子拿q:%d\n", *tmp);
printf("真是麻烦,我就一步到位:%d\n", **lilianjie);
system("pause");
return 0;
}
5.2 多级指针
#include
#include
/*
多级指针:
1.n级指针只能被n-1级指针赋值
2.并无大多意义
*/
int main0110(void) {
int guizi = 888;
int *guizi2 = &guizi;
int **guizi3 = &guizi2;
int ***guizi4 = &guizi3;
int ****guizi5 = &guizi4;
printf("*guizi2:%d\n", *guizi2);
printf("**guizi3:%d\n", **guizi3);
printf("***guizi4:%d\n", ***guizi4);
printf("****guizi5:%d\n", ****guizi5);
system("pause");
return 0;
}
6.指针作为函数的参数 6.1 为什么要使用指针
1.在函数调用时,普通参数只能进行值传递,通过指针来进行地址传递,修改实参的值;
2.通过指针可以让被调用函数提供更多返回值
3.减少值传递带来的额外开销,提高程序执行效率
#include
#include
/*
1. 普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数
2. 二级指针不但可以将变量通过参数函数内部,也可以将函数内部变量 “带出”到函
数外部
*/
//1.先声明一个函数,作用是:交换两个变量的值
void swap_ordinary(int, int);//普通的形参因为通过值传递并不能直接交换实参的值
void swap(int *, int *); //指针型实参通过传递实参的地址来交换实参的值
//2.meipo要访问boy_home里的money变量
void boy_home(int **meipo);
int main0109(void) {
//1.
int a = 101;
int b = 202;
int *p1 = &a;
int *p2 = &b;
printf("&p1:%p\n", &p1);
swap_ordinary(a, b);
printf("调用普通形参函数后:a:%d,b:%d\n", a, b);
swap(p1, p2);
//swap(&a, &b);//这样可以不用再定义指针
printf("调用指针形参后:a:%d,b:%d\n", a, b);
//2.
int *meipo = NULL;
printf("mian中meipo的地址:%p\n", &meipo);
boy_home(&meipo);
printf("*meipo:%d\n", *meipo);
printf("meipo:%p\n", meipo);
system("pause");
return 0;
}
void swap(int *p1, int *p2) {
int tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void swap_ordinary(int a, int b) {
int tmp = a;
a = b;
b = tmp;
}
void boy_home(int **meipo) {
//这里要定义静态变量,否则的话,可能会使meipo成为野指针
static int money = 1000000;
*meipo = &money;
printf("meipo的地址:%p\n", &meipo);
printf("money的地址:%p\n", &money);
}
7.指针与数组 7.1 指针数组与数组指针 7.1.1 指针数组
指针数组:一个数组里存的都是相同类型的指针(本质上是一个数组),跟一般数组用法基本类似。
/*一维的指针数组*/
int a = 1, b = 2, c = 3;
int *p[3] = { &a,&b,&c };
for (int i = 0; i < 3; i++) {
cout << *p[i] << endl;//要访问数组里指针所指的变量,只需要在前面加上解引
//cout << **(p + i) << endl;
}
/*二维的指针数组*/
const char *ptr_array[3][3] = { {"asdx","qwer","fdsfaf"},{"44444","555","6666"},{"a78x","q3er","f2f"} };
cout << ptr_array[2][2] << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%s\t", ptr_array[i][j]);
//printf("%s\t", *(*(ptr_array + i) + j));
}
printf("\n");
}
7.1.2 数组指针
数组指针:就是一个指向数组的指针。(本质上是一个指针)
#include
#include
using namespace std;
int main() {
int a[3] = { 1,2,3 };
cout << "&a[0]:\t" << &a[0] << "\t" << typeid(&a[0]).name() << endl;
cout << "a:\t" << a << "\t" << typeid(a).name() << endl;
cout << "&a:\t" << &a << "\t" << typeid(&a).name() << endl;
int (*p)[3] = &a;
for (int i = 0; i < 3; i++) {
cout << (*p)[i] << "\t";
//cout << (*p) + i << "\t";
}
/*
a[0]的地址、a的值、a的地址是相同的。
a[0]会分配一段4字节的内存空间;
数组名a看成一个指向a[0]的指针,所以值是a[0]的地址;
&a则是取数组a的首地址,所以也是a[0]的地址。
数组a的类型是 int [3],所以要定义一个int [3]*类型的指针,但c/c++要写成int (*)[3]的形式.
*/
//二维数组的数组名并不是一个二级指针,而是一个指向第一行元素的数组指针
int b[4][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
{10, 11, 12}
};
int (*p2)[3] = b;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
//cout << setw(2) << left << p2[i][j] << " "; //也可以直接通过b来访问,
//cout << setw(2) << left << (*(p2+i))[j] << " ";
cout << setw(2) << left << *(*(p2+i)+j) << " ";
}
cout << endl;
}
system("pause");
return 0;
}
7.2 一维数组、二维数组、指针数组作为函数的参数传递
(1)一维数组和一维指针数组
/*
一维数组传参(数组名就是指向第一个元素的指针):
(1) 形参不指定数组大小:int arr[],int len
(2) 形参指定数组大小:int arr[SIZE]
(3) 形参用指针,传数组首元素地址:int *arr,int len
*/
void method1(int arr[], int len);
void method2(int arr[SIZE]);
void method3(int *arr, int len);
/*
指针数组传参:
(1) 指针数组传参,声明成指针数组,不指定数组大小:int *arr[],int len
(2) 指针数组传参,声明成指针数组,指定数组大小:int *arr[SIZE]
(3) 二级指针传参 : int **arr,int len
arr 指向 arr[0]的指针,*arr = arr[0] , **arr = *arr[0]
*/
void method1(int *p_arr[], int len);
void method2(int *p_arr[10]);
void method3(int **p2, int len);
(2)二维数组
#include
#include
#include
using namespace std;
#define LINES 4
#define COLUMNS 3
void printArr1(int arr[LINES][COLUMNS]) {
cout << "1." << endl;
for (int i = 0; i < LINES; i++) {
for (int j = 0; j < COLUMNS; j++) {
//cout << setw(2) << left << arr[i][j] << "\t";
cout << setw(2) << left << *(*(arr+i)+j) << "\t";
}
cout << endl;
}
}
void printArr2(int arr[][COLUMNS], int lines) {
cout << "2." << endl;
for (int i = 0; i < LINES; i++) {
for (int j = 0; j < COLUMNS; j++) {
cout << setw(2) << left << arr[i][j] << "\t";
}
cout << endl;
}
}
void printArr3(int (*arr)[COLUMNS], int lines) {
cout << "3." << endl;
for (int i = 0; i < LINES; i++) {
for (int j = 0; j < COLUMNS; j++) {
cout << setw(2) << left << arr[i][j] << "\t";
}
cout << endl;
}
}
void printArr4(int **arr, int lines, int columns) {
cout << "4." << endl;
for (int i = 0; i < lines; i++) {
for (int j = 0; j < columns; j++) {
cout << setw(2) << left << *((int*)arr + columns*i + j) << "\t";//不能写成数组的形式
}
cout << endl;
}
}
int main() {
int arr2[LINES][COLUMNS];
for (int i = 0; i < LINES*COLUMNS; i++) {
arr2[i / COLUMNS][i % COLUMNS] = i + 1;
}
printArr1(arr2);
printArr2(arr2,LINES);
printArr3(arr2, LINES);
printArr4((int **)arr2, LINES, COLUMNS);//要将arr2转换为二级指针
system("pause");
return 0;
}
8.void类型的指针
void 表示空类型
void* 表示空类型指针(即只存储地址,但没有类型)
void类型指针是无法节引访问的,要访问void类型的值,必须要对其进行正确的强制类型转换 (所有其它类型的指针都可以隐式自动转换成 void 类型指针,反之需要强制类型转换)
#include
#include
int main(void){
int arr[]={1, 2, 3, 4, 5};
char ch = 'a';
void *p = arr;//定义了一个void 类型的指针
//p++; //不可以, void * 指针不允许进行算术运算
p = &ch; //其它类型可以自动转换成void * 指针
//printf("数组第一个元素: %d\n", *p); //不可以进行访问
printf("p: 0x%p ch: 0x%p\n", p, &ch);
//强制类型转化
char * p1 = (char *)p;
printf("p1 指向的字符是: %c\n", *p1);
system("pause");
return 0;
}
9.函数指针
定义和初始化:返回值类型 (*指针名)(参数列表)= &函数名;
使用:
方式1:(*指针名)(参数列表)
方式2: 指针名(参数列表)
#include
#include
int compare_int(const void *a,const void *b) {
//printf("Success!\n");
int *num1 = (int *)a;
int *num2 = (int *)b;
return *num2 - *num1;
}
int compare_char(const void *a, const void *b) {
char c1 = *(char *)a;
char c2 = *(char *)b;
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
return c1- c2;
}
int main(void) {
int arr[] = { 1,2,3,4,5,6,8,7,9 };
int (*p_comp_int)(const void *a, const void *b) = &compare_int;//定义函数指针并初始化
//compare_int(&arr[1], &arr[2]);//实参进行隐式转换
(*p_comp_int)(arr, arr+1);//指针进行函数调用方式1
int num = p_comp_int(arr, arr + 1);//指针进行函数调用方式2
printf("%d\n", num);
/*快速排序通过函数指针(在函数中定义排序规则)和void类型指针可以实现对任何类型的数组排序*/
//qsort对整形数组排序
qsort(arr, sizeof(arr) / sizeof(int), sizeof(int), &compare_int);
for (int i = 0; i < sizeof(arr)/sizeof(int); i++) {
printf("%d ", arr[i]);
}
printf("\n");
//qsort对char类型数组排序
char arr2[] = { 'a','b','c','d','e','f','A','B','C','D','E','F' };
int (*p_comp_char)(const void *a, const void *b);
qsort(arr2, sizeof(arr2) / sizeof(char), sizeof(char), &compare_char);
for (int i = 0; i < sizeof(arr2) / sizeof(char); i++) {
printf("%c", arr2[i]);
}
system("pause");
return 0;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)