老九学堂 学习 C++ 第五天

指针

基本概念:指针(pointer)是一个值为内存地址的变量(或数据对象)

在这里插入图片描述
声明及初始化指针变量

基本用法:数据类型 * 指针变量名

int * ptr_num;
char * ptr_name;
float * money_ptr;

注:

  1. int* p的写法偏向于地址,即 p 就是一个地址变量,表示一个十六进制地址
  2. int *p的写法偏向于值, *p是一个类型变量,能够表示一个整型值
  3. 声明中的 * 号和使用中的 * 号含义完全不同

空指针(null poiniter)

空指针:空指针不指向任何对象,在试图使用任何一个指针之前可以先检查是否为空,空指针指向的内存是不可以访问的。

用法:

int *ptr1 = nullptr//如果不赋初值,就是野指针
注意初始化所有指针,并在可能的情况下,尽量等定义了对象之后再定义指向它的指针。

void *指针:
一种特殊的指针类型,可以存放任意对象的地址

	double num = 3.14;
	double * ptr_num1 = #
	void * ptr_num2 = #
	cout << (ptr_num1 == ptr_num2) << endl;

注:

  1. void *指针存放一个内存地址,地址指向的内容是什么类型不能确定
  2. void *类型指针一般用来:拿来和别的指针作比较、作为函数的输入和输出;赋值给另一个void *指针

小结:

  • 如果一个变量存储另一个对象的地址,则称该变量指向这个对象
  • 指针变量可以赋值,指针的指向在程序执行中可以改变

注:

  1. 指针可以是任何基本数据类型、数组和其他所有高级数据结构的地址
  2. 若指针已声明为指向某种类型数据的地址,则它不能用于存储其他类型数据的地址
  3. 应为指针指定一个地址后,才能在语句中使用指针
  4. 0~255之间的内存编号是系统占用的,因此不可以访问

野指针:指针变量指向非法的内存空间

int main()
{
	//指针变量p指向内存地址变量为0x1100的空间
	int * p = (int*)0x1100;
	//访问时报错:
	cout << *p << endl;
}

const修饰指针

const修饰指针有三种情况:

  1. const修饰指针——常量指针
  2. const修饰常量——指针常量
  3. const既修饰指针,又修饰常量
int a = 1, b = 2;
const int * p = &a;	//常量指针
特点:指针的指向可以修改,但是指针指向的值不能修改
*p = 2;	//false
p = &b;	//true
***********************************************
int * const p = &a;	//指针常量
特点:指针的指向不可以修改,指针指向的值可以修改
*P = 3;	//true
p = &b;	//false
**********************************************
const int * const p = &a;//const既修饰指针,又修饰常量,都不能修改

引用

引用:为对象起了另外一个名字(引用即别名)

语法:数据类型 &别名 = 原名;

注:

  1. 引用并非对象,只是为一个已经存在的对象起的别名
  2. 引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起(int& ref_value = 10 ;//错误)
  3. 引用必须初始化,所以使用引用之前不需要测试其有效性,因此使用引用可能会比使用指针效率高。引用一旦初始化,就不可以在修改(例:a是b的别名,就不能再改成c的别名了)
  4. 将引用变量用作参数时,函数将使用原始数据,而非副本
  5. 当数据所占内存比较大时,建议使用引用参数
  6. 不可以直接引用常量,但是指向常量的引用是合法的:
double& d = 2;// d = 2 ,不合法,违背了常量的基本概念
//指向常量的引用--合法
const double& d = 2;

引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参;
优点:可以简化指针修改实参

使用引用参数:

void Swap1(int, int);
void Swap2(int*, int*);
void Swap3(int&, int&);
int main()
{
    int num1 = 10, num2 = 5;
    Swap1(num1, num2);
    cout << "执行Swap1后:" << num1 << '\t' << num2 << endl;
    Swap2(&num1, &num2);
    cout << "执行Swap2后:" << num1 << '\t' << num2 << endl;
    Swap3(num1, num2);
    cout << "执行Swap3后:" << num1 << '\t' << num2 << endl;
    return 0;
}
void Swap1(int num1, int num2)
{
    int temp;
    temp = num1;
    num1 = num2;
    num2 = temp;
}
void Swap2(int* p1, int* p2)
{
    int temp;
    temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}
void Swap3(int& ref1, int& ref2)//使用引用
{
	//使用引用的理由:1.可以更加简便的书写代码
	//2.可以直接传递某个对象,而不只是把对象复制一份
    int temp;
    temp = ref1;
    ref1 = ref2;
    ref2 = temp;
}

注:

  1. 引用参数中使用const可以避免参数被无意修改
  2. 引用参数建议尽可能的使用const

返回引用

函数返回引用类型;用法:函数调用作为左值

  1. 不要返回局部变量的引用
#include <iostream>
using namespace std;
int& sum();//返回引用类型的函数
int main()
{
	int& Num = sum();
	cout << Num << endl;//打印结果正确,因为编译器做了保留
	cout << Num << endl;//结果错误,因为num的内存已经释放
    return 0;
}
int& sum()
{
    int num = 10;//函数中的局部变量会被内存回收
    return num;
}
  1. 函数调用作为左值
int& test()
{
	static int a = 10;//静态变量数据存放在全局区,在程序结束后系统释放
	return a;
}
int main()
{
	int& ref = test();
	cout << ref << endl;
	cout << ref << endl;
	test() = 100;//如果函数的返回值是引用,这个函数调用可以作为左值
	//test返回的是a这个变量,相当于a=100,ref是a的别名
	cout << ref << endl;
}
  1. 函数可以不返回值,默认返回传入的引用对象本身
int& sum(int& num1, int& num2)
{
    num1++;
    num2++;
    //默认返回最后一个更新的引用参数
}
int main()
{
    int num1 = 10, num2 = 15;
    int& result = sum(num1, num2);
    cout << result << endl;
    return 0;
}
  1. 返回引用时,要求函数参数中包含被返回的引用对象

小结:使用引用参数的一些指导原则

  1. 能够修改调用函数中的数据对象
  2. 数据对象较大时传递引用可以提高程序的运行效率
    函数中不需要修改传递的参数:
    ①如果数据对象很小,建议按值传递
    ②传递数组只能使用指针,并使用const关键字
    ③较大的对象则使用const指针或引用,以提高程序效率
    函数中需要修改传递的参数:
    ①数据对象是基本类型或结构时,可以使用指针或引用
    ②数据对象是数组时只能使用指针
    ③数据对象是类对象时,要求使用引用

引用的本质

引用过的本质在c++内部实现是一个指针常量

指针和引用:

  • 引用对指针进行了简单的封装,底层仍然是指针
  • 获取引用地址时,编译器会进行内部转换
int num = 100;
int& rel_num = num;
rel_num = 100; 
cout << &num << '\t' << &rel_num  << '\t' << rel_num << endl;

常量引用

使用场景:用来修饰形参,防止误操作
·

·

·

·

·

·

动态分配内存

使用new分配内存,使用delete释放内存,与new配对使用(不能释放声明变量分配的内存)

//1.在运行阶段为一个int值分配未命名的内存
//2.使用指针来访问(指向)这个值(右->左)
int * ptr_int = new int;
delete ptr_int; //释放由new分配的内存

注:不要创建两个指向同一内存块的指针,有可能误删两次

动态分配的数组
使用new创建动态分配的数组

int * intArray = new int [10];

new运算符返回第一个元素的地址

使用delete[]释放内存

delete[] intArray;//释放整个数组

关于new和delete使用的规则:

  1. 不要使用delete释放不是new分配的内存
  2. 不要使用delete释放同一内存两次
  3. 如果使用new[]c为数组分配内存,则对应delete[]c释放内存
  4. 对空指针使用delete是安全的
栈区(stack)

由编译器自动分配释放,一般存放函数的参数值、局部变量的值等;
操作方式类似数据结构中的栈— —先进后出

堆区(heap)

一般由程序员分配释放,若程序不释放,程序结束时可能由操作系统回收;
注:与数据结构中的堆是两回事,分配方式类似链表

全局区(静态区-static)

存放全局变量和静态变量以及常量,程序结束后由系统释放

文字常量区

存放常量字符串的地方,程序结束由系统释放

程序代码区

存放函数体的二进制代码,由操作系统进行管理的

#include <iostream>

using namespace std;

int main()
{
    //栈区
    int num2;
    //栈区
    char str[] = "空你急哇";
    //栈区
    char * ptr2;
    //“空你急哇”以及'\0'在常量区,ptr3在栈区
    char * ptr3 = "空你急哇";
    //全局(静态)初始化区
    static int num3 = 2048;
    //分配的内存在堆区
    ptr1 = new int[10];
    ptr2 = new char[20];
    //注:ptr1和ptr2本身是在栈区中的
    return 0;
}

·

·

·
数组与指针案例:

#include <iostream>
using namespace std;

int main()
{
    //定义数组实现,实现逆序
    int num[]{12, 43, 10, 389, 8, 67};
    int numCount = sizeof(num) / sizeof(num[0]);
    int* p_num = num;
    int* temp = new int;
    for(int j = 0; j < numCount / 2; j++)
    {
        *temp = *(p_num + j);
        *(p_num + j) = *(p_num + numCount - j - 1);
        *(p_num + numCount - j - 1) = *temp;
    }
    cout << "逆序后:" << '\n';
    for(int i = 0; i < numCount; i++)
    {
        cout << *(p_num + i) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值