这里讨论C语言标准库中各类常用函数,以及它们的高危情况。
1、atoi 函数
这个函数是转换输入字符串转换为整型数。
对于该函数的实现需要考虑以下几个方面:
- 输入字符串为NULL;
- 输入的字符包含前导的空格;
- 输入开始是否包含符号‘+’、‘-’;
- 输入的字符是否合法(对于十进制‘0’~‘9’为合法的输入);
- 计算出的数值为 long int,足够判断溢出;
- 数据溢出的处理(上溢出时,返回最大正数;下溢出时,返回最大负数);
这里讨论C语言标准库中各类常用函数,以及它们的高危情况。
1、atoi 函数
这个函数是转换输入字符串转换为整型数。
对于该函数的实现需要考虑以下几个方面:
先写出两者的3点区别,后面再具体分析:
从概念上讲,指针(pointers)从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变,这点后面再详细叙述。
而引用(references)是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的,自始至终只能依附于同一个变量。
一、运算符的定义
运算符重载就是运算符的“一符多用”。重载运算符是具有特殊名称的函数:保留字 operator 后接需定义的操作符符号。像任意其他函数一样,重载操作符具有返回类型和形参表,每个操作符用于内置类型都有关联的定义,当内置操作符和类型上的操作存在逻辑对应关系时,操作符重载最有用,最直观,使用重载操作符并不是创造命名操作。
二、在哪种情况下使用哪种重载运算符的方式合适?
C++ 提供了两种重载运算符的方式,在大多数情况下:
任何一个对C稍稍有了解的人都知道malloc、calloc、free。前面两个是用户态在堆上分配一段连续(虚拟地址)的内存空间,然后可以通过free释放,但是,同时也会有很多人对其背后的实现机制不了解。
这篇文章则是通过介绍这三个函数,并简单的予以实现,对比现有C的标准库实现(glibc等)相比,并不是特别高效,我们重在阐述背后的基本原理。
一、C程序的存储空间布局
图1
二、Heap 内存模型
这里我们用类String 来介绍这两个函数:
拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用拷贝构造函数。为啥形参必须是对该类型的引用呢?试想一下,假如形参是该类的一个实例,由于是传值参数,我们把形参复制到实参会调用拷贝构造函数,如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构造函数,从而形成无休止的递归调用导致栈溢出。
1 | string(const string &s); |
赋值函数,也是赋值操作符重载,因为赋值必须作为类成员,那么它的第一个操作数隐式绑定到 this 指针,也就是 this 绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为const 引用传递。
1 | string& operator=(const string &s); |
拷贝构造函数和赋值函数并非每个对象都会使用,另外如果不主动编写的话,编译器将以“位拷贝”的方式自动生成缺省的函数。在类的设计当中,“位拷贝”是应当防止的。倘若类中含有指针变量,那么这两个缺省的函数就会发生错误。这就涉及到深复制和浅复制的问题了。
拷贝有两种:深拷贝,浅拷贝