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