C++知识点整理
空指针与野指针的区别:空指针也就是通常指向为NULL的指针,野指针就是指向一块未知的内存区域(可以是通过malloc或new申请空间后,释放后没有将指针置为空),也有可能定义了一个指针没有初始化,由于内存空间中的值在未赋值之前是随机数,所以也有可能诞生野指针。
malloc函数为C语言中的标准函数,标准中规定:在分配内存失败时会返回“NULL Pointer”空指针,而非为初始化的指针。
C++在分配内存失败时会抛出BAD_ALLOC异常。 野指针:指向垃圾内存的指针,而非空指针。 野指针产生原因:
1.声明的指针未被初始化,指针默认值随机产生。创建指针应该将其初始化为NULL或者指向某一内存。
2.free和delete掉的指针未重置为NULL,free后的指针仍指向该内存,但该内存已变为垃圾内存。
另:空指针不指向任何实际的对象或函数,反过来说对象或函数的指针也不可能为空指针。 ,auto是默认类型,每次调用sum函数时auto类型的变量重新赋值为0,static是静态变量,如果在函数内部进行定义,则只在第一次调用时进行赋初值,其作用范围是sum函数内部,在函数内部可以改静态变量的值;
先说宏和函数的区别:
1. 宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型.
2. 宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的.
3. 宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.
4. 宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.
5. 函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.
内联函数与宏的区别:
1.内联函数在运行时可调试,而宏定义不可以;
2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
3.内联函数可以访问类的成员变量,宏定义则不能;
4.在类中声明同时定义的成员函数,自动转化为内联函数。
external 作用之一:当它与 “C” 一起连用时,如:extern “C” void fun(int a , int b);会告诉C++编译器在编译fun这个函数名是按着C的规则去翻译相应的函数名而不是C++的,C++的规
则在翻译这个函数名时会把fun这个名字变得面目全非。。。
回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
回调函数不能是特定的类成员函数。因为类成员函数含有this指针。
核心就是类成员函数需要this指针访问函数,而全局或者静态函数不需要this指针。 简言之,类的成员函数需要隐含的this指针 而回调函数没有办法提供。
int **a[3][4], 则变量占有的内存空间为每个元素存放的都是二级指针,每个指针在32位系统下占4个字节的内存,共3*4=12个元素,则共占内存12*4=48字节
C++中使用模板类的原因:
(1) 可用来创建动态增长和减小的数据结构; (2) 它是类型无关的,因此具有很高的可利用性; (3) 它在编译时检查数据类型,保证了类型安全; (4) 它是平台无关的,具有可移植性; (5) 可用于基本数据类型。
A. 由于编译后的名字不同,C++程序不能直接调用C 函数 B. extern \既可以修饰函数也可以修饰变量
C. c++提供关键字extern“C”,被extern \修饰的是按照C语言方式编译和连接的
它是if not define 的简写,是宏定义的一种,实际上确切的说,这应该是预处理功能三种(宏定义、文件包含、条件编译)中的一种----条件编译。
在c语言中,对同一个变量或者函数进行多次声明是不会报错的。所以如果h文件里只是进行了声明工作,即使不使用# ifndef宏定义,多个c文件包含同一个h文件也不会报错。 但是在c++语言中,#ifdef的作用域只是在单个文件中。所以如果h文件里定义了全局变量,即使采用#ifdef宏定义,多个c文件包含同一个h文件还是会出现全局变量重定义的错误。 使用#ifndef可以避免下面这种错误:如果在h文件中定义了全局变量,一个c文件包含同一个h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef,则不会出现这种错误。
根据语言定义, 在指针上下文中的常数0 会在编译时转换为空指针。也就是说, 在初始化、赋值或比较的时候,
如果一边是指针类型的值或表达式, 编译器可以确定另一边的常数0 为空指针并生成正确的空指针值。因此下边的代码段完全合法: char *p = 0; if(p != 0)
然而, 传入函数的参数不一定被当作指针环境, 因而编译器可能不能识别未加修饰的0 “表
示” 指针。
在函数调用的上下文中生成空指针需要明确的类型转换,强制把0 看作指针。 例如, Unix 系统调用execl接受变长的以空指针结束的字符指针参数。它应该如下正确调用: execl(\
如果省略最后一个参数的(char *) 转换, 则编译器无从知道这是一个空指针,从而当作一个0 传入。(注意很多Unix 手册在这个例子上都弄错了。)
堆和栈
A. 堆的大小仅受操作系统的限制,栈的大小一般一般较小
B. 在堆上频繁的调用new/delete容易产生内存碎片,栈没有这个问题 A. 堆和栈都可以动态分配
所谓动态内存分配就是指在程序运行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
从以上动、静态内存分配比较可以知道动态内存分配相对于静态内存分配的特点: 1、不需要预先分配存储空间;
2、分配的空间可以根据程序的需要扩大或缩小。
一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。
二是空间不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由函数alloca()进行分配。不过栈的动态分配和堆不同,他的动态分配是由编译器进行释放,无需我们手工实现。
对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。
一般,用static修饰的变量,全局变量位于静态数据区。函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。
内存管理常用区间和区间中的数据
静态区(static): 存放(初始化的)全局变量、静态变量和(未初始化的)全局变量和静态变量;
栈区(stack): 存放局部变量和函数的形参。栈中的内存空间由编译器自动申请和释放。 堆区(heap): 存放动态分配内存函数申请的变量。堆中的内存空间需要程序员手动释放,否则会引发内存泄露。