C语言复习

常量指针

语法: const 数据类型 * 变量名;

const修饰指针时,不能通过解引用的方法,改变变量的值,但是用原始的变量名是可以更改的,但是可以更改指向的对象

一般用于修饰函数的形参,表示不希望在函数中修改内存地址中的值

指针常量

语法:数据类型 * const 变量名;

指向的变量(对象)不可改变

在定义的同时必须要初始化

可以通过解引用的方法修改内存地址中的值

不能重新指向新的对象

常指针常量

指向的对象不可改变,不能通过解引用的方法改变内存地址中的值

void *

不能对void声明变量,他不能代表一个真实的变量

不能对void*指针直接解引用(需要转换成其他类型的指针)

把其他类型的指针赋给void*指针不需要转换

把void*指针赋值给其他指针类型需要转换

栈和堆的主要区别

二级指针       

空指针

野指针

(g1) 野指针是指向不可用内存的指针,当指针被创建时,指针不可能自动指向NULL,这时,默认值是随机的,此时的指针成为野指针。

        (g2) 当指针被free或delete释放掉时,如果没有把指针设置为NULL,则会产生野指针,因为释放掉的仅仅是指针指向的内存,并没有把指针本身释放掉。

        (g3) 第三个造成野指针的原因是指针操作超越了变量的作用范围。

(h)如何避免野指针?
        (h1)对指针进行初始化。


        (h2)指针用完后释放内存,将指针赋NULL。

一维数组与指针

函数指针与回调函数

指针函数

返回值为地址的函数就是指针函数

C语言关键字总结

static

static修饰全局变量,会改变全局变量的作用域(不能通过extern外部引用到别的文件中使用);但是不会改变全局变量的生命周期;

static修饰函数是会切断函数的外部链接属性的;说人话就是,限定你的函数只能在本源文件内使用,不能被其它源文件文件调用(但是可以通过包装,函数嵌套函数实现外部引用);

static修饰局部变量,改变了局部变量的生命周期(在静态区开辟空间,存贮局部变量的值),并未改变局部变量的作用域;

const

const修饰变量

const修饰的数据类型是指常类型,常类型的变量或对象的值是不能被更新的。被const修饰的变量只具有可读性,不具有可写性,但是可以通过指针间接改变变量的值。

const修饰指针

常量指针

语法: const 数据类型 * 变量名;

const修饰指针时,不能通过解引用的方法,改变变量的值,但是用原始的变量名是可以更改的,但是可以更改指向的对象

一般用于修饰函数的形参,表示不希望在函数中修改内存地址中的值

指针常量

语法:数据类型 * const 变量名;

指向的变量(对象)不可改变

在定义的同时必须要初始化

可以通过解引用的方法修改内存地址中的值

不能重新指向新的对象

常指针常量

指向的对象不可改变,不能通过解引用的方法改变内存地址中的值

const修饰函数

修饰函数表示这是一个常量函数,即它的指针或引用在声明后不能被修改,而且它返回的结果也不会改变。

volatile

用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统硬件或者其他线程等。(其他线程对变量进行更改时)遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

volatile就是要求你每次读取都从内存开始,数据从内存读到寄存器中,进而再被CPU利用;

volatile虽然有易变的意思并不是要求变量一定要改变,而是说变量可能会发生改变,要求编译器放弃优化;

 typedef和 define的区别

typedef与define都是替一个对象取一个别名,以此来增强程序的可读性

(a)原理不同

 #define是C语言中定义的语法,它是预处理指令,在预处理时进行简单而机械的字符串替换,不做正确性检査,不管含义是否正确照样代入,只有在编译已被展开的源程序时,才会发现可能的错误并报错。

typedef是关键字,它在编译时处理,所以 typedef具有类型检查的功能。它在自己的作用域内给一个已经存在的类型一个别名,但是不能在一个函数定义里面使用标识符 typedef。

(b)功能不同

 typedef用来定义类型的别名,这些类型不仅包含内部类型(int、char等),还包括自定义类型(如 struct),可以起到使类型易于记忆的功能。

c)作用域不同
        #define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而 typedef有自己的作用域。

全局变量和局部变量的区别是什么

b1)全局变量的作用域为程序块,而局部变量的作用域为当前函数。

(b2)内存存储方式不同,全局变量(静态全局变量,静态局部变量)分配在全局数据区(静态存储空间),后者分配在栈区。

        (b3)生命周期不同。全局变量随主程序创建而创建,随主程序销毁而销毁,局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在了。

        (b4)使用方式不同。通过声明为全局变量,程序的各个部分都可以用到,而局部变量只能在局部使用。 

全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
        可以,在不同的C文件中以static形式来声明同名全局变量。

        可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

(d)局部变量能否和全局变量重名?
        能,局部会屏蔽全局。

        局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。 对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。

C语言中内存的分配方式

C语言中内存分配的方式有几种?
(a)静态存储区分配

内存分配在程序编译之前完成,且在程序的整个运行期间都存在,例如全局变量、静态变量等。 (b)栈上分配

在函数执行时,函数内的局部变量的存储单元在栈上创建,函数执行结束时这些存储单元自动释放。局部变量、函数内参数都在栈上。

(c) 堆上分配  New开辟的空间在堆上

  栈的空间由操作系统自动分配/释放,堆上的空间手动分配/释放。

栈在C语言中有什么作用?


(a)C语言中栈用来存储临时变量,临时变量包括函数参数和函数内部定义的临时变量。函数调用中和函数调用相关的函数返回地址,函数中的临时变量,寄存器等均保存在栈中,函数调动返回后从栈中恢复寄存器和临时变量等函数运行场景。

(b)多线程编程的基础是栈,栈是多线程编程的基石,每一个线程都最少有一个自己专属的栈,用来存储本线程运行时各个函数的临时变量和维系函数调用和函数返回时的函数调用关系和函数运行场景。 操作系统最基本的功能是支持多线程编程,支持中断和异常处理,每个线程都有专属的栈,中断和异常处理也具有专属的栈,栈是操作系统多线程管理的基石。

内存泄漏

 简单地说就是申请了一块内存空间,使用完毕后没有释放掉。

    它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。

如何判断内存泄漏?
        1. 良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露。当程式稳定之后,在来检测内存泄露时,无疑增加了排除的困难和复杂度。使用了内存分配的函数,一旦使用完毕,要记得要使用其相应的函数释放掉。

        2. 将分配的内存的指针以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表。

        3. Boost 中的smart pointer。

        4. 一些常见的工具插件,如ccmalloc、Dmalloc、Leaky等等

new/delete与malloc/free的区别是什么?


        在C++中,申请动态内存和释放动态内存,用new/delete 和 malloc/free都可以,new和malloc动态申请的内存都位于堆中,无法被操作系统回收,需要对应的delete/free来释放空间。

        void *malloc(int size);

        说明:malloc向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。

        对于类的对象而言,malloc/free无法满足动态对象的要求,对象在创建时要自动执行构造函数,在对象消亡之前要自动执行析构函数,而malloc/free 不在编译器控制权限之内,无法执行构造函数和析构函数。

        当然对于没有资源要清理的类,不调用析构函数也没有太大的问题,即使用free或delete没有区别。但万一有一些类的成员是指针,而这个指针又在堆上开辟了空间,这时不调用析构函数去释放这个指针指向的这段空间,就会造成内存泄漏。delete会调用析构函数,释放指针成员变量的空间,再销毁对象本身的空间;而free只释放了对象本身的空间,而指针成员所指向的空间没有被释放

        1)new 能够自动计算需要分配的内存空间,而malloc需要手工计算字节数。

        2) new与delete带具体类型的指针,malloc与free返回void类型的指针。

        3)new 将调用构造函数,而malloc不能;delete将调用析构函数,而free不能。

        4)malloc/free 需要库文件<stdlib.h>支持,而new/delete不需要库文件支持。

        5)new操作可以重载,可以自定义内存分配策略,不做内存分配,或者分配到非内存设备上。而malloc不能。

        delete和free被调用后,内存不会不会立即收回,指针也不会指向空,delete或free仅仅是告诉操作系统,这一块内存被释放啦,还可以做其他用途。由于没有对这块内存进行写操作,所以内存中的变量数值并没有发生变化,出现野指针的情况,因此,释放完内存后需要将指针向量置为空。

头文件的作用有哪些

 1. 通过头文件来调用库功能。出于对源代码保密的考虑,源代码不便(或不准)向用户公布,只要向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口是怎么实现的。编译器会从库中提取相应的代码。

        2. 头文件能加强类型安全检查。当某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,大大减轻程序员调试、改错的负担。

C语言中 struct与 union的区别是什么?
       

struct(结构体)与 union(联合体)是C语言中两种不同的数据结构,两者都是常见的复合结构,其区 别主要表现在以下两个方面。

        (a)结构体与联合体虽然都是由多个不同的数据类型成员组成的,但不同之处在于联合体中所有成员共 用一块地址空间,即联合体只存放了一个被选中的成员,而结构体中所有成员占用空间是累加的, 其所有成员都存在,不同成员会存放在不同的地址。在计算一个结构型变量的总长度时,其内存空 间大小等于所有成员长度之和(需要考虑字节对齐),而在联合体中,所有成员不能同时占用内存空间,它们不能同时存在,所以一个联合型变量的长度等于其最长的成员的长度。

        (b)对于联合体的不同成员赋值,将会对它的其他成员重写,原来成员的值就不存在了,而对结构体的 不同成员赋值是互不影响的。

        假设为32位机器,int型占4个字节, double型占8个字节,char型占1个字节,而DATE是一个联合型变 量,联合型变量共用空间,uion里面最大的变量类型是int[5],所以占用20个字节,它的大小是20,而 由于 union中 double占了8个字节,因此 union是要8个字节对齐,所占内存空间为8的倍数。为了实现 8个字节对齐,所占空间为24.而data是一个结构体变量,每个变量分开占用空间,依次为 sizeof(int) + sizeof(DATE)+ sizeof( double)=4+24+8=36按照8字节对齐,占用空间为40,所以结果为 40+24=64

什么是大端和小端?


        大端:高地址存低字节,低地址存高字节
        小端:低地址存低字节,高地址存高字节

++a和a++有什么区别?两者是如何实现的?
        a++的具体运算过程为: 先用a,再执行++

        ++a的具体运算过程为:  先执行++,再用a

虚函数是什么?
       

虚函数只能是类的成员函数, 而不能将类外的普通函数声明为虚函数. 虚函数的作用是允许在派生类中对基类的虚函数重新定义 (函数覆盖), 只能用于类的继承层次结构中.

        虚函数能有效减少空间开销. 当一个类带有虚函数时, 编译系统会为该类构造一个虚函数表 (一个指针数组), 用于存放每个虚函数的入口地址.

        什么时候应该使用虚函数:

        判断成员函数所在的类是不是基类, 非基类无需使用虚函数

        成员函数在类被继承后有没有可能被更改的功能, 如果希望修改成员函数功能, 一般在基类中将其声明为虚函数;

        我们会通过对象名还是基类指针访问成员函数, 如果通过基类指针过引用去访问, 则应当声明为虚函数

数组和链表的区别

  1. 数组内存连续,链表内存不连续。
  2. 数组访问速度比链表快
  3. 链表增删操作比数组快
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值