目录
四、指针
int *ptr_year;
ptr_year=&year; //&year表示取地址
注意:
1. int* p的写法偏向于地址,即p就是一个地址变量,表示一个十六进制地址
2. int *p的写法偏向于值,*p是一个整形变量,能够表示一个整型值
3. 声明中的*号和使用中的*号含义完全不同
example:
#include <iostream>
using namespace std;
int main()
{
char ch = 'a';
char * ptr_ch = &ch;
cout << (void *)ptr_ch << '\t' << *ptr_ch << endl;
return 0;
}
> 结果输出:0x6dfeeb a
(void *):任意类型指针
1、空指针
空指针不指向任何对象,在试图使用一个指针之前可以首先检查是否为空
用法
int *ptr1 = nullptr;//等价于int *ptr1 = 0;
int *ptr2 = 0; //直接将ptr2初始化为字面常量0
//需要包含#include <stdlib.h>头文件
int *ptr3 = NULL; //等价于int *ptr3=0;
2、void*指针
1. (void*)指针存放一个内存地址,地址指向的内容是什么类型不能确定
2. (void*)类型指针一般用来:拿来和别的指针比较、作为函数的函数的输入和输出;赋值给另一个(void*)指针
3、引用
int int_value = 1024;
//refValue指向int_value,是int_value的另一个名字
int& refValue = int_value;
//错误:引用必须被初始化
int& refValue2;
注:
- 指向常量的引用是非法的
double & ref = 100; //错误
const double & ref = 100; //正确
引用并非对象,只是为一个已经存在的对象起的别名
- 引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起
int &ref_value = 10; //错误
引用必须初始化,所以使用之前不需要测试其有效性,因此使用引用可能会比使用指针效率高
int * num = &abc; //引用
-
引用更接近const指针,一旦与某个变量关联起来,就将一直效忠于它
-
将引用变量用作参数时,函数将使用原始数据,而非副本
-
当数据所占内存比较大时,建议使用引用参数
4、指针和引用
1. 引用对指针进行了简单封装,底层仍然是指针
2. 获取引用地址时,编译器会进行内部转换
3.引用比指针效率高
int num = 108;
int& rel_num = num;
rel_num = 118;
cout << &num << '\t' <<&rel_num <<endl;
转换为:
int num = 108;
int* rel_num = #
*rel_num = 118;
cout << &num << '\t' <<rel_num <<endl;
5、指针和数组
数组:
1. 存储在一块连续的内存空间中
2. 数组名就是这块连续内存空间的首地址
#include <iostream>
using namespace std;
int main()
{
double score[] {11, 22, 33, 44, 55};//数组类型-->double[5]
double * ptr_score = score;
//数组名就是这块连续内存空间的首地址
//cout << score << endl; //输出为地址值
cout << sizeof(score) << ''\t' <<sizeof(ptr_score) <<endl
}
结果:40 4
解答:ptr_score为double型的地址,所以只占4个字节;而score为数组,占40字节。
6、指针的算数运算
6.1、指针的递增和递减(++、--)
int i;
double score[5] {98, 87, 65, 43, 76};
double * ptr_score;
ptr_score = score;
for(i=0;i<5; i++)
{
cout << (void*)ptr_score++ << endl;
}
输出:
0x6dfec0
0x6dfec8
0x6dfed0
0x6dfed8
0x6dfee0
注:一个类型为T的指针的移动,以sizeof(T)为移动单元。因为score是double类型,所以每次移动8个字节空间
6.2、指针加上或减去某个整数值
int i;
double score[5] {98, 87, 65, 43, 76};
double * ptr_score;
ptr_score = &score[1]; //取第一个数据的地址
ptr_score +=2; //取第三个数据的地址
cout << (void*)ptr_score++ << endl;
ptr_score -=3; //取第一个数据的地址
cout << (void*)ptr_score++ << endl;
结果:
0x6dfed8
0x6dfec8
7、数组与指针小结
7.1、一维数组与指针
-
int num[50]; //num是数组名,也可以理解成数组的首地址
-
num的值与&num[0]的值是相同的
-
数组第i+1个元素可表示为:
第i+1个元素的地址:
&num[i+1]
num+i+1
第i+1个元素的值:
num[i+1]
*(num+i+1)
*++ptr_num
-
为指向数组的指针赋值:
int * ptr_num = num;
int * ptr_num = &num[0];
-
指针变量可以指向数组元素
int * ptr_num = &num[4];
int * ptr_num = num + 4;
7.2、二维数值与指针
-
如5行3列的二维数组指针首地址为&a[0][0]
-
使用指针访问二维数组元素中的元素
*(a[1]+2); //表示a[1][2]
*(*(a+1)+2); //表示a[1][2]
example1:
#include <iostream>
using namespace std;
int main()
{
int * p = new int[10];
//用指针创建二维数组
int (*p2)[3] = new int[5][3];//降维操作//第一维已经降维,第二维3不变
p2[3][2] = 555; //给其中一个赋值
for(int i = 0; i<5; i++)
{
for(int j=0; j<3; j++)
{
//两种输出模式
//cout << p2[i][j] << ',';
cout << *(*(p2+i)+j) << ',';
}
cout << endl;
}
return 0;
}
输出结果:
0,0,0,
0,0,0,
0,0,555,
0,0,0,
example2:
#include <iostream>
using namespace std;
int main()
{
int * p = new int[10];
//用指针创建二维数组
int arrays[5][3] ={
{1,2,3},
{4,5,6},
{7,8,9},
{10,11,12},
{13,14,15},
};
int (*p2)[3] = arrays;
for(int i = 0; i<5; i++)
{
for(int j=0; j<3; j++)
{
//两种输出模式
//cout << p2[i][j] << ',';
cout << *(*(p2+i)+j) << ',';
}
cout << endl;
}
//输出每个元素的地址
cout << &arrays[1][0] << endl;
for(int i = 0; i<5; i++)
{
cout << p2 + i <<endl;
}
return 0;
}
输出结果:
1,2,3,
4,5,6,
7,8,9,
10,11,12,
13,14,15,
0x6dfeac //arrays[1][0]输出元素地址
0x6dfea0
0x6dfeac arrays[1][0]输出元素地址
0x6dfeb8
0x6dfec4
0x6dfed0
8、动态分配内存
8.1、使用new分配内存
int * p = new int; //在运行此句话阶段分配未命名的内存
int num; //此为编译阶段分配内存;
解析:p在栈区,在堆区分配一块int型空间
注:在运行阶段分配未命名的内存以存储值
8.2、使用delete释放内存
delete ptr_int; //释放由new释放的内存
注:
-
与new配对使用;
-
不要释放已经释放的内存;
-
不能释放生命变量分配的内存;
-
不要创建两个指向同一内存块的指针,有可能误删除两次。
example:
#include <iostream>
using namespace std;
int main()
{
int num[5];//分配了20字节的空间,即使空间里面没有数值;
int* nums = new int[5]; //运行到这一步才在堆内存分配5个字节的整形空间,nums表明空间的地址大小
cout << sizeof(num) << ''\t' <<sizeof(nums) <<endl
}
运行结果:20 4
9、动态分配数组
9.1、使用new创建动态分配的数组
int * intArray = new int[10];
注:new运算符返回第一个元素的地址
9.2、使用delete[]释放内存
delete [] intArray;//释放整个数组
example:
int * ptr_int = new_int;
short * ptr_short = new short[500];
delete ptr_int; //释放由new释放的内存
delete [] ptr_short;//释放整个数组
注:关于new和delete使用的规则:
-
不要使用delete释放不是new分配的内存
-
不要使用delete释放统一内存两次
-
如果使用new[]释放为数组分配内存,则对应delete[]释放内存
9.3、程序的内存分配
-
栈区(stack):由编译器自动分配释放,一般存放函数的参数值,局部变量的值等。其操作方式类似数据结构中的栈-先进后出。
-
堆区(heap):一般由程序员分配释放,若程序不释放,程序结束时可能由操作系统回收。其数据结构中的堆是两回事,分配方式类似链表。
-
全局区(静态区-static):全局变量和静态变量是存储在一起的,程序结束后由系统回收。
-
文字常量区:常量符号串就放在这里,程序结束由系统回收。
string a="hello,world!";
-
程序代码区:存放函数体的二进制代码。
注:
#include <iostream>
using namespace std;
int num1 = 0;
int * ptr1;
int main
{
int num2; //栈区
char str[] = "hello,world!";//栈区
char * ptr2; //栈区
char * ptr3 = "hello,world!"; //hello,world!以及\0在常量区,ptr3在栈区
static int num3 = 1024; //全局(静态)初始化区
ptr1 = new int[10]; //分配的内存在堆区
ptr2 = new char[20];
//注意:ptr1和ptr2本身在栈区
return 0;
}
10、总结
1、指针是一个变量,存储另一个变量(对象)的内存地址
2、指针的声明由基本类型、星号(*)和变量名组成
3、为指针赋值,赋值运算符号右侧必须是一个地址
-
如果是普通变量需要在前面加一个取地址运算符&
-
如果是另一个指针变量或者是一个数组,不需要加&运算符
4、运算符*用于返回指针指向的内存地址中存储的值
5、使用指针访问一维数组和二维数组的元素
int num = 4;
int* p_num = #//取num所在的地址值
*p_num = 112; //指向num的值变为112