C/C++的内容又多又杂,常常看到有人罗列相关书单,觉得毫无意义,我不相信他们真的完全掌握了其中任何一本。学习任何东西,首先要掌握基本概念,基础不牢地动山摇,因为高级的内容都是通过低级的概念来描述的。当基本概念都没理解透,学习再多都是空中楼阁。这里罗列了一些听基本的问题,虽然看着不难,但是精确理解每句话中的每个词真的并不容易。
-
变量声明和定义区别?
-
声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。
-
相同变量可以再多处声明(外部变量extern),但只能在一处定义。
-
-
"零值比较"?
-
bool类型:if(flag)
-
int类型:if(flag == 0)
-
指针类型:if(flag == null)
-
float类型:if((flag >= -0.000001) && (flag <= 0. 000001))
-
-
strlen和sizeof区别?(编译期&&运行期)
-
sizeof是运算符,并不是函数,结果在编译时得到而非运行中获得;strlen是字符处理的库函数。
-
sizeof参数可以是任何数据的类型或者数据(sizeof参数不退化);strlen的参数只能是字符指针且结尾是'\0'的字符串。
-
因为sizeof值在编译时确定,所以不能用来得到动态分配(运行时分配)存储空间的大小。
-
-
同一不同对象可以互相赋值吗?(深浅拷贝)
-
可以,但含有指针成员时需要注意。
-
对比类的对象赋值时深拷贝和浅拷贝。
-
-
-
结构体内成员按照声明顺序存储,第一个成员地址和整个结构体地址相同。
-
未特殊说明时,按结构体中size最大的成员对齐(若有double成员),按8字节对齐。
-
-
static作用是什么?在C和C++中有何区别?
-
static可以修饰局部变量(静态局部变量)、全局变量(静态全局变量)和函数,被修饰的变量存储位置在静态区。对于静态局部变量,相对于一般局部变量其生命周期长,直到程序运行结束而非函数调用结束,且只在第一次被调用时定义;对于静态全局变量,相对于全局变量其可见范围被缩小,只能在本文件中可见;修饰函数时作用和修饰全局变量相同,都是为了限定访问域。
-
C++的static除了上述两种用途,还可以修饰类成员(静态成员变量和静态成员函数),静态成员变量和静态成员函数不属于任何一个对象,是所有类实例所共有。
-
static的数据记忆性可以满足函数在不同调用期的通信,也可以满足同一个类的多个实例间的通信。
-
未初始化时,static int变量默认值为0。
-
-
结构体和类的区别?
- 结构体的默认限定符是public;类是private。
C++中结构体也可以继承。
-
malloc和new的区别?
-
malloc和free是标准库函数,支持覆盖;new和delete是运算符,并且支持重载。
-
malloc仅仅分配内存空间,free仅仅回收空间,不具备调用构造函数和析构函数功能,用malloc分配空间存储类的对象存在风险;new和delete除了分配回收功能外,还会调用构造函数和析构函数。
-
malloc和free返回的是void类型指针(必须进行类型转换),new和delete返回的是具体类型指针。
-
-
指针和引用区别? - C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同;指针是具体变量,需要占用存储空间。
-
引用在声明时必须初始化为另一变量,一旦出现必须为typename refname &varname形式;指针声明和定义可以分开,可以先只声明指针变量而不初始化,等用到时再指向具体变量。
-
引用一旦初始化之后就不可以再改变(变量可以被引用为多次,但引用只能作为一个变量引用);指针变量可以重新指向别的变量。(常量指针)
-
不存在指向空值的引用,必须有具体实体;但是存在指向空值的指针。
-
-
宏定义和函数有何区别?
-
宏在编译时完成替换,之后被替换的文本参与编译,相当于直接插入了代码,运行时不存在函数调用,执行起来更快;函数调用在运行时需要跳转到具体调用函数。
-
宏函数属于在结构中插入代码,没有返回值;函数调用具有返回值。
-
宏函数参数没有类型,不进行类型检查;函数参数具有类型,需要检查类型。
-
宏函数不要在最后加分号。
-
-
宏定义和const区别?
-
宏替换发生在编译阶段之前,属于文本插入替换;const作用发生于编译过程中。
-
宏不检查类型;const会检查数据类型。
-
宏定义的数据没有分配内存空间,只是插入替换掉;const定义的变量只是值不能改变,但要分配内存空间。
-
-
宏定义和typedef区别?
-
宏主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名。
-
宏替换发生在编译阶段之前,属于文本插入替换;typedef是编译的一部分。
-
宏不检查类型;typedef会检查数据类型。
-
宏不是语句,不在在最后加分号;typedef是语句,要加分号标识结束。
-
注意对指针的操作,typedef char * p_char和#define p_char char *区别巨大。
-
-
宏定义和内联函数(inline)区别?
-
在使用时,宏只做简单字符串替换(编译前)。而内联函数可以进行参数类型检查(编译时),且具有返回值。
-
内联函数本身是函数,强调函数特性,具有重载等功能。
-
内联函数可以作为某个类的成员函数,这样可以使用类的保护成员和私有成员。而当一个表达式涉及到类保护成员或私有成员时,宏就不能实现了。
-
-
条件编译#ifdef, #else, #endif作用?
-
可以通过加#define,并通过#ifdef来判断,将某些具体模块包括进要编译的内容。
-
用于子程序前加#define DEBUG用于程序调试。
-
应对硬件的设置(机器类型等)。
-
条件编译功能if也可实现,但条件编译可以减少被编译语句,从而减少目标程序大小。
-
-
区别以下几种变量?
const int a; int const a; const int *a; int *const a;
-
int const a和const int a均表示定义常量类型a。
-
const int *a,其中a为指向int型变量的指针,const在 * 左侧,表示a指向不可变常量。(看成const (*a),对引用加const)
-
int *const a,依旧是指针类型,表示a为指向整型数据的常指针。(看成cons
-
t(a),对指针const)
-
关键问题点:const 属于修饰符 ,关键是看const 修饰的位置在那里
1、const int *a
这里const 修饰的是int,而int定义的是一个整值
因此*a 所指向的对象 值 不能通过 *a 来修改,但是 可以重新给 a 来赋值,使其指向不同的对象
eg:
const int *a = 0;
const int b = 1;
int c = 1;
a = &b // 额外:注意不能通过a 来修改 b值
a = &c // 额外:虽然c本身不是一个常量
*a = 2 //error 为题就在这里,不能修改通过 *a 所指向的对象值,最后赋值得对象是c,因此不能通过*a 来修改c值。
2、int *const a
这里const修饰的是 a ,a代表的是一个指针地址
因此不能赋给a其他的地址值,但可以修改a指向的值
这有点和cont int *a相反的意味,例子就不说了
3、至于int const *a 和 const int *a 的意义是相同的 他们两个的作用等价
补充:
4、const int * const a
这个代表a所指向的对象的值以及它的地址本身都不能被改变
5、 const int *const a
a所指向的对象及对象的值均不可改变。
关于const的点滴补充:
1、const 对象的地址只能赋值给指向const 对象的指针
2、指向const 对象的指针可以 被赋 以 一个非const 对象的地址
3、指向const 得指针常被用作函数的形式参数,保证被传递给函数的实际对象在函数得实际对象在函数中不会被修改
4、常量在定义后就不能被修改,所以它必须被初始化。未初始化的常量定义将导致编译错误(上面都是在说明const得问题,所以没有赋值,实际语句中要赋值的)
-
volatile有什么作用?
-
volatile定义变量的值是易变的,每次用到这个变量的值的时候都要去重新读取这个变量的值,而不是读寄存器内的备份。
-
多线程中被几个任务共享的变量需要定义为volatile类型。
-
-
什么是常引用?
-
常引用可以理解为常量指针,形式为const typename & refname = varname。
-
常引用下,原变量值不会被别名所修改。(不能通过引用修改原值)
-
原变量的值可以通过原名修改。
-
常引用通常用作只读变量别名或是形参传递。
-
-
区别以下指针类型?
int *p[10] int (*p)[10] int *p(int) int (*p)(int)
-
int *p[10]表示指针数组,强调数组概念,是一个数组变量,数组大小为10,数组内每个元素都是指向int类型的指针变量。
-
int (*p)[10]表示数组指针,强调是指针,只有一个变量,是指针类型,不过指向的是一个int类型的数组,这个数组大小是10。
-
int *p(int)是函数声明,函数名是p,参数是int类型的,返回值是int *类型的。
-
int (*p)(int)是函数指针,强调是指针,该指针指向的函数具有int类型参数,并且返回值是int类型的。
-
-
常量指针和指针常量区别?
-
常量指针是一个指针,读成常量的指针,指向一个只读变量。如int const *p或const int *p。
-
指针常量是一个不能给改变指向的指针。如int *const p。
-
-
a和&a有什么区别?
假设数组int a[10]; int (*p)[10] = &a;
-
a是数组名,是数组首元素地址,+1表示地址值加上一个int类型的大小,如果a的值是0x00000001,加1操作后变为0x00000005。*(a + 1) = a[1]。(数组指针也是迭代器,可以进行加减运算)
-
若(int *)p ,此时输出 *p时,其值为a[0]的值,因为被转为int *类型,解引用时按照int类型大小来读取。
-
int arr[5] = {1,2,3,4,5}; int* p = &arr[2]; int j = p[1]; //j==arr[3] 即相当于 *(p+1) int k = p[-2]; //k==arr[0] 即相当于 *(p-2)
-
-
数组名和指针(这里为指向数组首元素的指针)区别?
-
二者均可通过增减偏移量来访问数组中的元素。
-
数组名不是真正意义上的指针,可以理解为常指针,所以数组名没有自增、自减等操作。
-
当数组名当做形参传递给调用函数后,就失去了原有特性,退化成一般指针,多了自增、自减操作,但sizeof运算符不能再得到原数组的大小了。
-
-
野指针是什么?
-
也叫空悬指针,不是指向null的指针,是指向垃圾内存的指针,指向未定义的内存空间。
-
产生原因及解决办法:
-
指针变量未及时初始化 => 定义指针变量及时初始化,要么置空。
-
指针free或delete之后没有及时置空 => 释放操作后立即置空。
-
-
-
堆和栈的区别?
-
申请方式不同。
-
栈由系统自动分配。
-
堆由程序员手动分配。
-
-
申请大小限制不同。
-
栈顶和栈底是之前预设好的,大小固定,可以通过ulimit -a查看,由ulimit -s修改。
-
堆向高地址扩展,是不连续的内存区域,大小可以灵活调整。
-
-
申请效率不同。
-
栈由系统分配,速度快,不会有碎片。
-
堆由程序员分配,速度慢,且会有碎片。
-
-
-
delete和delete[]区别?
-
delete只会调用一次析构函数。
-
delete[]会调用数组中每个元素的析构函数。
-