一、define定义的宏和const定义的常量有什么区别?
define定义宏的指令,程序在预处理阶段将用#define所定义的内容只是进行了替换。因此程序运行时,常量表中并没有用#define所定义的宏,系统并不为它分配内存,而且在编译时不会检查数据类型,出错的概率要大一些。
const定义的常量,在程序运行时是存放在常量表中,系统会为它分配内存,而且在编译时会进行类型检查。
define定义表达式时要注意“边缘效应”,例如如下定义:
define N 2 + 3 //我们预想的N值是5,我们这样使用N
int a = N / 2; //我们预想的a的值是2.5,可实际上a的值是3.5
二、关键字volatile有什么含义?并给出三个不同的例子
优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
- 并行设备的硬件寄存器(如:状态寄存器)
- 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
- 多线程应用中被几个任务共享的变量
三、完成字符串拷贝可以使用sprintf、strcpy、以及memcpy函数,请问这些函数有什么区别?你喜欢哪一个?为什么?
这些函数的区别在于实现功能以及操作对象不同。
-strcpy:函数操作的对象是字符串,完成从源字符串到目的字符串的拷贝功能。
-sprintf: 这个函数主要用来实现(字符串或基本数据类型)向字符串的转换功能。如果源对象是字符串,并且指定%s格式符,也可实现字符串拷贝功能。
-memcpy: 函数顾名思义就是内存拷贝,实现将一个内存块的内容复制到另一个内存块这一功能。内存块由其首地址以及长度确定。因此,memcpy的操作对象适用于任意数据类型,只要能给出对象的起始地址和内存长度信息、并且对象具有可操作性即可。鉴于memcpy函数等长拷贝的特点以及数据类型代表的物理意义,memcpy函数通常限于同种数据类型或对象之间的拷贝,其中当然也包括字符串拷贝以及基本数据类型的拷贝。
对于字符串拷贝来说,用上述三个函数都可以实现,但是其实现的效率和使用的方便程度不同:
-strcpy 无疑是最合适的选择:效率高且调用方便。
-sprintf 要额外指定格式符并且进行格式转化,麻烦且效率不高。
-memcpy 虽然高效,但是需要额外提供拷贝的内存长度这一参数,易错且使用不便;并且如果长度指定过大的话(最优长度是源字符串长度+1),还会带来性能的下降。其实strcpy函数一般是在内部调用 , memcpy函数或者用汇编直接实现的,以达到高效的目的。因此,使用memcpy和strcpy拷贝字符串在性能上应该没有什么大的差别。
- 对于非字符串类型的数据的复制来说,strcpy和sprintf一般就无能为力了,可是对memcpy却没有什么影响。但是,对于基本数据类型来说,尽管可以用memcpy进行拷贝,由于有赋值运算符可以方便且高效地进行同种或兼容类型的数据之间的拷贝,所以这种情况下memcpy几乎不被使用。memcpy的长处是用来实现(通常是内部实现居多)对结构或者数组额拷贝,其目的是或者高效,或者使用方便,甚或两者兼有。
sprintf、strcpy、memcpy使用上有什么要注意的地方
- strcpy是一个字符串拷贝的函数,它的函数原型为strcpy(char dst, const char src);
- 将src开始的一段字符串拷贝到dst开始的内存中去,结束的标志符号为'\0',由于拷贝的长度不是由我么自己控制的,所以这个字符串拷贝很容易出错。
- 具备字符串拷贝功能的函数有memcpy,这是一个内存拷贝函数,它的函数原型为memcpy(char dst, const char srrc, unsigned int len);将长度为len的一段内存,从src拷贝到dst中去,这个函数的长度可控。但是会有内存读写错误。(比如len的长度大于要拷贝的空间或目的空间);
- sprintf是格式化函数。将一段数据通过特定的格式,格式化到一个字符串缓冲区中去。sprintf格式化的函数的长度不可控,有可能格式化后的字符串会超出缓冲区的大小,造成溢出。
三、static关键字的作用
- 隐藏。编译多个文件时,所有未加static前缀的全局变量和函数都全局可见。
- 保持变量内容的持久。全局变量和static变量都存储在静态存储区,程序开始运行就初始化,只初始化一次。static控制了变量的作用范围。
- 默认初始化为0,在静态数据区,内存中的所有字节都是0x00,全局变量和static变量都是默认初始化为0.
static关键字区别:
- static全局变量与普通的全局变量有什么区别:static全局变量只初始化一次,防止在其他文件单元中被引用;
- static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
- static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。
四、关键字const
- const int a; int const a; 作用是一样:a是一个常整型数
- const int *a; int const *a; a是一个指向常整型数的指针(整型数是不可修改的、但指针可以)
- int * const a; a是一个指向整型数的常指针(指针指向的整型数是可以修改的,但指针是不可修改的)
- int const * const a;a是一个指向常整型数的常指针(指针指向的整型数是不可修改的,同时指针也是不可修改的)
五、堆栈
- 管理方式: 对于栈来讲,是由编译器自动管理、无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生内存泄漏(memory leak)。
- 申请大小:
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在windows下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的、自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活、也比较大。 - 碎片问题:
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出。 - 分配方式:
堆是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloc函数进行分配。但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现。
5.分配效率
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持;分配专门的寄存器存放栈的地址、压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的。












网友评论