C/C++基础知识总结

数组和指针的不同点

  1. 概念不同:指针相当于一个变量,它存放的数据在内存中的地址;数组是用来存放多个同类型数据的集合。
  2. 赋值方式不同:同类型的指针变量可以相互赋值,而数组不行,数组只能一个个元素进行赋值或拷贝。
  3. 访问数据方式不同:指针是间接访问数据,获取指针,先解引用,再访问指针所指向地址的内容;数组是直接访问。
  4. 指针和数组名差异:指针和数组名都可以表示地址,但是指针是变量可以修改,数组是常量,不可以修改赋值。
  5. 传参:数组传参时会退化成指针。

进一步解析退化的意义:C语言只会以值拷贝的方式传递参数,参数传递参数时,如果只拷贝整个数据,则会使得处理效率大大降低,并且当参数位于栈上时,太大的数组拷贝将会导致栈溢出。因此,C语言将数组的传参进行退化,将整个数组拷贝传入函数时,将数组名看成常量指针,传数组首元素的地址。

指针数组和数组指针

指针数组是数组(如 int* arr[8],只是一个数组,数组中存放着每个元素都是一个指针类型的元素),数组指针是指针(如int (*arr)[8],这是一个指针,该指针指向一个数组)。

这边分析下优先级:

[]的优先级比*高,故int* arr[8]中arr是一个数组(arr是一个含有8个int*元素的数组),int*是数组里面的元素;int (*arr)[8]中需要用括号来使得*arr表示的是一个指针(指针arr指向一个大小为8个整型的数组。)。

字符数组和字符串常量

char arr[] = "world" ---- 字符数组

char *arr = "world" ----字符串常量


  • char arr[] = "world" ,此处的赋值是将常量区的字符串"world"拷贝到了堆栈区的数组arr的空间了。数组arr是在堆栈区开辟了空间,此时是可以修改字符串的值,因为修改的是堆栈区的字符串的值。另外此时的数组名p是堆栈区中的"world"的首地址。
  • char *arr = "world",指针arr2是存储在堆栈区,但字符串是常量,存储在常量区,只是指针arr指向了存储在常量区的字符串首地址,此时不能改变常量区的字符串的值。

引用和指针的区别

  1. 指针有内存分配,引用只是一个别名,并且引用声明必须初始化,从而指向一个已经存在的对象。
  2. 引用可以看作是一个常量指针,初始化时指向被指定的对象,以后不能改变,但是指定对象的内容可以修改;而指针是一个存储地址的变量,可以被重新赋值指向另外一个对象。
  3. 引用创建时必须进行初始化且不能为空,指针创建时可以为空。
  4. 指针和引用的自增运算符意义不同,指针是内存地址自增,而引用是对值的自增。
  5. sizeof的意义不同:使用sizeof看一个指针的大小是4,而引用则是被引用对象的大小
  6. 没有引用常量,但是有指针常量
  7. 参数出传递:作为参数传递时,指针需要被解引用才可以对对象进行操作,而对引用的直接修改都会改变引用所指向的对象。
  8. 指针可以多级(**p),而引用只能一级

C和C++中class和struct的区别

C中的strcut不能有函数,但C++中可以。C++中的struct对C中的struct进行了扩充,使得struct能包含成员函数,能继承,能实现多态。

默认的继承访问权限. struct是public的,class是private的

定义模板参数. class这个关键字还用于定义模板参数,就像typename。但关键字struct不用于定义模板参数。

new/delete和malloc/free区别

  • 属性不同: new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持c语言
  • 申请的内存所在位置:new操作符从自由存储区(free store)上为对象动态分配内存空间,而malloc函数从堆上动态分配内存.
  • 返回类型安全性: new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,故new是符合类型安全性的操作符。 而malloc内存分配成功则是返回void*类型,需要通过强制类型转换将空类型指针转换成我们需要的类型。
  • 内存分配失败时的返回值: new内存分配失败时,会抛出bac_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。
  • 是否需要指定内存大小:使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的大小 int *pi=new int;//在自由存储区中分配创建了一个整形对象,并返回一个指向该对象的地址来初始化指针pi。 int *pi=new int();//对指针pi指向的地址的值进行了初始化为0 int *pi=new int(1024);//初始化为1024。 int *p=(int *)malloc(100);//指向整型的指针p指向一个大小为100字节的内存的地址 int *p=(int )malloc(25sizeof(int));//指向整型的指针p指向一个25个int整型空间的地址
  • 数组分配内存(与前一个特点类似):new中分配内存对于变量和数组不同,malloc分配内存则相同 int *pi=new int[]; //指针pi所指向的数组未初始化 int *pi=new int[n]; //指针pi指向长度为n的数组,未初始化 int *pi=new int; //指针pi所指向的地址初始化为0
  • 是否调用构造函数/析构函数. new会先调用operator_ new函数,申请足够的内存(通常底层使用malloc实现)。然后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator_ delete函数释放内存(通常底层使用free实现)。malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
  • 能否重载. opeartor_new/operator_delete允许重载,malloc/free不允许重载
  • 已分配内存的扩充. malloc/free可以通过realloc函数扩充,new/free无法直观地处理
  • 能否相互调用. operator_new /operator _delete的实现可以基于malloc/free,而malloc的实现不可以去调用new。

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内存分配机制是怎么样的,在哪里分配内存,最大可以申请多大的内存?

  • 首先会扫描之前由free()所释放的空闲内存块列表,以求找到尺寸大于或等于要求的一块空闲内存。如果这一内存块的尺寸正好与要求相当,就将它返回给调用者,如果是一块较大的内存,那么将对其进行分割,在将一块大小相当的内存返回给调用者的同时,把较小的那块空闲内存块保留在空闲列表中

栈和堆区别

  • 管理方式不同:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
  • 空间大小不同:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M
  • 能否产生碎片不同:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出
  • 生长方向不同:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长

面向对象和面向过程的区别

面向过程是自顶向下的编程,面向对象是高度实例抽象化。

  • 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用
  • 面向对象是把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为
  • 面向过程的优缺点 1. 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。 2. 缺点:没有面向对象易维护、易复用、易扩展
  • 面向对象的优缺点 1. 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护 2. 缺点:性能比面向过程低

const关键字(不可修改)

  • 修饰变量,说明该变量不可以被改变;
  • 修饰指针,分为指向常量的指针和指针常量;int *const p和const int *p
  • 常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改;
  • 修饰成员函数,说明该成员函数内不能修改成员变量,本质是const this指针。

static关键字(对外不可见)

  • 修饰普通变量, 修改变量的存储区域和生命周期,使变量存储在静态区,在 main 函数运行前就分配了空间,在整个程序运行期间一直存在,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它,自动初始化为0。 全局变量作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。 局部变量作用域:仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
  • 修饰普通函数, 其只能在定义它的源文件中使用,不能在其他源文件中被引用
  • 修饰类成员变量和成员函数, 它们是属于类的,而不是某个对象,所有对象共享一个静态成员。静态成员通过<类名>::<静态成员>来使用。在 static 函数内不能访问非静态成员

extern关键字

extern关键字主要修饰变量或函数,表示可以跨文件访问,或者表明在其他文件定义,在此处引用。

volatile关键字

  • 不可优化性.。volatile 关键字是一种类型修饰符,用它声明的类型变量表示不可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改。所以使用 volatile 告诉编译器不应对这样的对象进行优化。
  • 易变性.。volatile 关键字声明的变量,每次访问时都必须从内存中取出值(没有被 volatile 修饰的变量,可能由于编译器的优化,从 CPU 寄存器中取值)

explicit关键字

explicit关键字的作用就是防止对象间实现=赋值,防止类构造函数的隐式自动转换,类构造函数默认情况下即声明为implicit(隐式),另外explicit只用于单参数的构造函数,或者除了第一个参数以外的其他参数都有默认值.

类成员属性

类的成员有三个权限:公有权限(public),私有权限(private),保护权限(protected)。

  • 私有权限:私有成员在类内部可以访问,类外不可访问,一般推荐将成员变量设置为私有成员;
  • 公有权限:类内类外都可以进行访问;
  • 保护权限:类内和当前类的子类可以访问,类外不可访问。

代码判断32位和64位系统

用指针大小来判断

使用sizeof(指针名)函数32为指针大小为4,64位指针大小为8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值