C/C++基础知识
数组和指针的区别
- 退化的意义:C语言只会以值拷贝的方式传递参数,参数传递时,如果拷贝整个数组,效率会大大降低,并且在参数位于栈上,太大的数组拷贝将会导致栈溢出。
- 因此,C语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。
指针数组和数组指针
指针数组本质是数组,数组指针本质是指针,谁优先级高,本质是谁
- 优先级问题:[]的优先级比*高
- 说明arr是一个数组,而int*是数组里面的内容
- 这句话的意思就是:arr是一个含有 * 和 int* 的数组
- 由于[]的优先级比*高,因此在写数组指针的时候必须将*arr用括号括起来
- arr先和*结合,说明p是一个指针变量
- 这句话的意思就是:指针arr指向一个大小为8个整型的数组。
字符数组和字符串常量
char arr[]=“hello”; //字符数组
char *arr2=“hello”; //字符串常量
const char arr[]=“hello”; //这里hello本来是在栈上的,但是编译器可能会做某些优化,将其放到常量区
const char *arr2=“hello”; //字符串hello保存在常量区,const本来是修饰arr2指向的值不能通过arr2去修改,但是字符串hello在常量区,本来就不能改变,所以加不加const效果都一样
inline和宏的区别
- inline是在编译期间展开,宏在预处理时替换
- inline函数直接嵌入到目标代码内,宏只是简单的文本替换
- inline函数会进行类型安全检查和参数有效性检查,而宏不会
- inline函数是函数,宏不是
- 宏定义时有可能产生二义性,而inline函数不会
- inline可以不展开,只是对编译器的建议,而宏必须展开
引用和指针的区别
C++中class和struct的区别
C中的strcut不能有函数,但C++中可以。C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。struct能包含成员函数吗? 能!struct能继承吗? 能!!struct能实现多态吗? 能!!!
new/delete和malloc/free区别
malloc和calloc间的主要区别在于后者在返回指向内存的指针之前把它初始化为0。另一个区别是calloc的参数包括所需的元素的数量和每个元素的字节数
new运算符的原理
-
内存分配
- 调用相应的 operator new(size_t) 函数,动态分配内存。如果 operator new(size_t) 不能成功获得内存,则调用 new_handler()函数用于处理new失败问题。如果没有设置 new_handler() 函数或者 new_handler() 未能分配足够内存,则抛出 std::bad_alloc 异常
-
构造函数
- 在分配到的动态内存块上 初始化相应类型的对象(构造函数)并返回其首地址。如果调用构造函数初始化对象时抛出异常,则自动调用 operator delete(void*, void*) 函数释放已经分配到的内存。
malloc的内存分配机制
malloc内存分配机制是怎么样的,在哪里分配内存,最大可以申请多大的内存?
栈和堆的区别
- 管理方式不同
- 对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
- 空间大小不同
- 一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M
- 能否产生碎片不同
- 对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
- 生长方向不同
- 对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长
面向对象和面向过程的区别
面向对象就是高度实物抽象化、面向过程就是自顶向下的编程
const关键字(不可修改)
static关键字(对外不可见)
extern关键字
extern关键字主要修饰变量或函数,表示该函数可以跨文件访问,或者表明该变量在其他文件定义,在此处引用.
- 在c头文件中通过#ifdef __cplusplus extern “C” { #endif来定义
- 在对应的c文件中实现
- 在cpp文件中通过“extern “C” 函数名”调用,或者包含c头文件
注意: extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间,但若需要调用的函数太多,还是直接包含头文件吧.
volatile关键字
explicit关键字
explicit关键字的作用就是防止对象间实现 = 赋值(赋值构造函数),防止类构造函数的隐式自动转换,类构造函数默认情况下即声明为implicit(隐式),
另外explicit只用于单参数的构造函数,或者除了第一个参数以外的其他参数都有默认值.
class Person{
public:
Person(){
}
//有参构造初始化数据
explicit Person(const char* str_){
str = (char *)malloc(sizeof(char)*100);
strcpy(str,str_);
}
~Person() {
if (str != NULL){
free(str);
str = NULL;
}
}
char *str;
};
void test05(){
//Person p = "abc"; 隐式调用
Person p ("abc"); //显式调用
}
类成员属性
类的成员有三个权限:公有权限(public),私有C权限(private),保护权限(protected)。
大小端序的定义和代码判断
定义
一个16进制的地址,存放在内存中从低地址开始存储,如16进制的地址为0x1234,对于地址而言,从右往左是从低到高
- 大端
- 若16进制的低地址存放在内存的高地址,则为大端字节序,34存储在高位,12存储在低位
- 小端
- 若16进制的低地址存放在内存的低地址,则为小端字节序,12存储在高位,34存储在低位
代码判断
可以通过联合体来判断,联合体是同一块内存被联合体中的所有成员公用,如果后续成员对内存重新赋值,会覆盖内存中原有数据
union U{
int a;
char b;
};
int main(){
U u;
u.a = 0x01020304;
if (u.b == 0x04){
cout << u.b << endl; //输出char字符
cout << "little" << endl;
}
else if (u.b == 0x01){
cout << u.b << endl;
cout << "big" << endl;
}
system("pause");
return 0;
}
代码判断32位和64位系统
写一个指针,输出指针所占的字节大小
线程A和线程B不加锁,对同一个变量,各自执行 i++ 100次,结果是啥
最小值2,最大值200。
最小值2
- 初始条件,内存值为0
- A线程从内存中取出值,进行第一次++操作,还未写回内存就被挂起,此时A寄存器 1 ,内存 0 ;
- B线程从内存中取出值,进行第一次++操作,还未写回内存就被挂起,此时B寄存器 1 ,内存 0 ;
- A连续执行到第99次++操作,每次都写回到内存,此时A寄存器 99 ,内存 99 ;
- B继续执行,将自己寄存器的值写回到内存,覆盖了内存中的99,此时B寄存器 1 ,内存 1 ;
- A执行第100次++操作,取出内存中的值1,然后++,还未写回内存就被挂起,此时A寄存器 2 ,内存 1 ;
- B执行完自己所有剩余操作,此时B寄存器为100, 内存为100;
- A继续执行,将自己的寄存器的值写回内存,内存为2。
最大值200
每个线程的每次执行都将值写回内存
C++指定内存分配对象
placement new
首先分配足够大的内存;然后用placement new
语法生成对象:new(ptr) xxx()
,其中ptr
是足够容纳所指对象的指针。
class Person {
private:
int age;
std::string name;
public:
// methods
};
int main(int argc, char** argv) {
auto mem = malloc(sizeof(Person));
auto p = new(mem) Person();
p->show();
free(mem);
return 0;
}
/*
Person
Lily's age is 10
*/
allocator
先分配内存,然后在其上装载对象,可以使用allocator
。allocator
定义在头文件中,能对指定类型分配合适的内存,并可手动调用对象的构造函数和析构函数。
class Person {
private:
int age;
std::string name;
public:
Person() {
age = 10;
name = "Lily";
cout << "Person" << endl;
}
void show() {
cout << name << "'s age is " << age << endl;
}
~Person() {
cout << "~Person" << endl;
}
};
int main() {
std::allocator<Person> alloc;
auto p = alloc.allocate(1); // 分配一个Person对象的内存
alloc.construct(p); // 调用Person的构造函数,如果构造函数有参数,参数写在p之后
// p 现在是一个指向Person的指针,且其指向对象被初始化过
// 对p进行一些操作
p->show();
// 销毁对象,但不释放内存,等同于调用p->~Person()
alloc.destroy(p);
// 释放内存
alloc.deallocate(p, 1);
return 0;
}
/*
Person
Lily's age is 10
~Person
*/