Essential C++ (2.2)

2.2 调用函数

2.2.1 案例引入

这一节书上借用了一个冒泡排序来讲解调用函数

一个很经典的案例:swap( )函数,为什么在传入参数后,并没有改变原来的参数取值?

2.2.2 引用类型

首先,借助知乎的回答,看一下引用和指针的区别:

在C和C++中,指针一般指的是某块内存的地址,通过这个地址,我们可以寻址到这块内存;而引用是一个变量的别名,例如我们给小明起了个外号:明明,那我们说明明的时候,就是说小明。

对于指针来说,它是一个地址,这个地址是一个数值,那么就意味这个数值可以为0(空指针),也可以为其他,即指针可以不指向任何东西。

而对于引用来说,他是一个外号,外号一定是“某个存在物体”的外号,所以引用不能为空,即不能存在空引用。

根据以上可知指针和引用的一个重要不同:**指针可以为空,引用不能为空。**这就意味着我们拿到一个引用的时候,是不需要判断引用是否为空的,而拿到一个指针的时候,我们则需要判断它是否为空。这点经常在判断函数参数是否有效的时候使用。

作者:知乎用户
链接:https://www.zhihu.com/question/37608201/answer/90293843
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

A reference (&) is like a constant pointer that is automatically
dereferenced. ——《Thinking in C++》Page 475

一个结论:引用是不能改变的指针

2.2.2.1 引用的性质
  • Any changes made through the reference variable are actually performed on the original variable (通过引用所做的读写操作实际上是作用于原变量上).
  • A reference must be initialized in declaration 引用必须在声明的时候初始化。
  • Once initialized, the name of the reference cannot be assigned to other variables (引用一旦初始化,引用名字就不能再指定给其它变量)

(一个思考):引用占不占内存?因为我在网上看到不同的答案,暂时还无法给出唯一的答案

2.2.2.2 使用引用的理由
  1. 希望直接对传入对象进行修改
  2. 降低复制大型对象的额外负担

我的一个思考比喻:例如我们想在D盘就可以看到C盘的一个文件,有两种方法:

  1. 直接将C盘文件全部复制过来
  2. 创建一个快捷方式到D盘

分析一下:第一种方式像我们常见的传值操作,复制过后,我们修改D盘内的文件,对C盘没有影响,可以理解为一种只读操作,在函数使用中,如果我们只想临时用,但不动它原本,这时我们可以直接传值

第二种方式:我们看似是在D盘里,其实我们一点快捷方式,我们就进入了C盘,我们是对它本身进行操作

这里的快捷方式应该是引用(常量指针)而不是所有类型指针:快捷方式只对应了一个,如果我们可以改变改快捷方式所指向的位置,则就是指针。超链接的比喻似乎更恰当?我们可以简单的修改一个超链接,指向不同位置。(而且,超链接可以为空,没错,这个例子更贴切!)

2.2.3 代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void display(vector<int> vec) {
	for (int ix = 0; ix < vec.size(); ++ix) 
		cout << vec[ix] << " ";
	cout << endl;
}
void swap(int &val1, int &val2) {
	int temp = val1;
	val1 = val2;
	val2 = temp; 
}
//ostream ofil("text_out1");
void bubble_sort(vector<int> &vec) {
	for (int ix = 0; ix < vec.size(); ++ix) {
		for (int jx = ix + 1; jx < vec.size(); ++jx) {
			if (vec[ix] > vec[jx]) {
				/*cout << "about to call swap! "
					<< " ix: " << ix << " jx: " << jx << '\t'
					<< " swapping: " << vec[ix]
					<< " with " << vec[jx] << endl;*/
				swap(vec[ix], vec[jx]);
			}
		}
	}
}
int main() {
	int ia[8] = {8, 34, 3, 13, 1, 21, 5, 2 };
	vector<int> vec{ ia,ia + 8 };
	cout << "Vector before sort: ";
	display(vec);
	bubble_sort(vec);
	cout << "Vector after sort: ";
	display(vec);
	return 0;
}
2.2.4 作用域及范围

函数是暂时位于程序堆栈(内存的一块区域)之中,当函数调用结束,这块会被POP掉,因此函数内部定义的局部变量也不复存在。

我们常用的retrun:我们开始说了函数的最开始就是返回类型 int / char …… ,再加上return后面的,就是一个完整的返回。

对象在程序内的存活区域称为储存期( storage duration ) 或者范围( extent )

对象的存活区域也就对应着对象的作用域( scope ) : local scope & file scope

file scope(全局):static extent 从main函数开始执行之前就已经分配好内存

2.2.5 动态内存管理

local scope 或 file scope都是由系统自动管理,也就是说不用我们去开辟内存空间

dynamic extent(动态范围):由程序员从空闲空间分配出来,称为 heap memory (堆内存)

通过 new 来完成, delete 来释放

计算机内存的四大区域:栈区,常量区,全局区,堆区(动态分配)

  1. 栈 区:在栈里面储存一些我们定义的局部变量以及形参;
  2. 常量区:主要是储存一些字符常量;
  3. 全局区:在全局区里储存一些全局变量和静态变量;
  4. 堆 :堆主要是通过动态分配的储存空间

内存分配方式及区别

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

tip(关于内存泄漏):内存泄露是因为堆的空间被我们动态分配用完了,这样当我们再去使用动态分配堆的空间的时候就没有足够的空间让我们使用了,这样就需要占有原来的空间,也就是会把其他的空间来储存我们键入的值,这样会导致原来储存的数据被破坏掉,导致了内存的泄露了。

思考:为什么要动态分配内存?为啥不直接定义一个局部变量或者全局变量?

总结的话:最大限度的利用内存

假设在一个函数体内,未到函数结束,系统分配的内存一直被占用,也就是局部变量和全局变量,如果是一个较大的程序,可能内存就不够用了。因此我们需要人为的去分配和释放,这样用完就空出内存空间。

以链表的创建结点为例:

struct Node* t;
t = (struct Node*)malloc(sizeof(struct Node));
t->data = x;

如果没有第二行的开辟空间,这里就会出错,就像下面这里如果没有第二行也会报错

int *p;
p = (int*)malloc(sizeof(int));
*p = 3;

“指针所占用的内存空间”和“指针指向的内存空间”

**!!!重要:**一组代码对比:

int *p;
p = 3;
int *p;
*p = 3;

第一组代码正确,第二组错误,若在第二组代码的第二行加入动态分配内存就正确了。

两组代码的第一行一样,第一行的代码作用是:定义了一个指针,也确定了类型,指针所占用的内存空间确定了,如果单纯对指针变量p进行操作是没有问题的,就像第一组代码一样;但是指针的作用是指向其他变量,指针指向的内存空间还未知,那么其他变量是什么呢?一定需要说明,也就是动态分配内存,这样我们就知道了指针变量所指向的区域,再解引用操作就没有错。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值