C总结归纳

  1.  对于基本数据类型,要掌握:
  1. 各种数据类型的长度(任何数据类型的指针都是4字节长度,包括对象指针);

C语言中的六种基本的数据类型:short,int,long,char,float,double;

 

  1. 记住<char,unsigned char,unsigned int>这些数据类型的取值范围,如果越界,会是什么情况?比如unsigned char 的取值范围是0-255,定义一个char a = 255,对其a+1,结果为0,如果赋值为0,对其a-1,结果为255;

  计算机中数据是以补码的方式存储的,当取值越界时,所取值的补码的溢出为将被舍去,并按照余下位来计算:

在char型中:-128 原码1 1000 0000 取反 1 0111 1111 补码 1 1000 0000 即1000 0000;

            -129 原码1 1000 0001 取反 1 0111 1110 补码 1 0111 1111 即0111 1111;

故在计算机中-128-1 = 127;

2. 对union,struct的用法要掌握:

1)内存空洞:可能会struct里套union来计算长度,要记住union的长度特点,而且union也遵守字对齐,并且长度等于最长的数据成员长度;

结构体中各成员的起始地址不同,结构体变量在内存中的长度为各成员长度之和;

共用体中各成员的起始地址相同,共用体变量所占的内存长度为最长的成员的长度。

2)对于union第二个特点一定要领会,各个数据成员的存放地址是相同的,且造成的缺陷是会导致数据覆盖,例如,定义一个union nn{int a,char b[2]};nn.b[0] = 1;nn.b[1] = 10,那么printf(nn.a = %d\n,nn.a);结果是多少?在回答此类问题要注意大端还是小端!

0000 0000 0000 0000 0000 1010 0000 0001 = 2561

注:小端模式 -- 字数据的高字节对应高地址,低字节对应低地址;

    大端模式 -- 字数据的高字节对应低地址,低字节对应高地址;

  1. 对于++,--的用法要掌握:
  1. 会计算(++i)+(++i)+(++i)+(++i) 以及 (i++)+(i++)+(i++)+(i++);

(++i) + (++i) + (++i) + (++i) = 2 + 2 + 3 + 4 = 11

(i++) + (i++) + (i++) + (i++) = 0 + 0 + 0 + 0 = 0

  1. 会计算int i = 2,i = i * ((i++)+(++i));打印i的值是多少?

i = i * ((i++)+(++i)) = 3 * (3 + 3) = 18

i++ = 19

  1.  static的作用(C,C++):回答这个问题的时候,最好能加上在C++里的作用,比如说是修饰变量是类变量,修饰函数是类成员函数(这个标记为重点);

(1)在函数体内,一个被声明为静态的变量在这一函数被调用过程中维持其值不变(该变量存放在静态变量区)。

(2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

(3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

5.#define和typedef的比较;

(1)作用时间不同:#define是宏定义,发生在预处理阶段,只进行简单的字符串替换,不进行正确性检查;而typedef在编译阶段有效,有类型检查的功能;

(2)功能不同:#define不只是可以为类型取别名,还可以定义常量、变量、编译开关等;

typedef用来定义类型的别名,包括内部类型、自定义类型、机器无关的类型等;

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

(4)修饰指针类型时作用不同:typedef int * pint; const pint p;  //p的地址不可改;

  #define int * Pint; const Pint P;  //P指向的值不可改;

  1. #define和 const的比较;

(1) define宏是在预处理阶段展开;const常量是编译运行阶段使用。

(2) define宏没有类型,不做任何类型检查,仅仅是展开;const常量有具体的类型,在编译阶段会执行类型检查。

(3) 存储方式不同:define宏仅仅是展开,有多少地方使用,不会分配内存;

  const常量会在内存中分配(可以是堆中也可以是栈中)。

  1. const定义的常量在程序运行过程中只有一份拷贝,而define定义的常量在内存中有若干个拷贝。
  1. #define 定义的宏函数和自定义函数的比较(优缺点);

(1)代码长度:#define宏:每次使用时,宏代码都被插入到程序中,程序的长度将大幅度增长;

   函数:函数代码只出现于一个地方:每次使用这个函数时,都调用那个地方的同一份代码;

  (2)执行速度:#define宏:更快

函数:存在函数调用、返回的额外开销;

(3)操作符优先级:#define宏:宏参数的求值是在所有周围表达式的上下文环境里,除非它们加上括号,否则邻近操作符的优先级可能产生不可预料的结果。

  函数:函数参数只在函数调用时求值一次,它的结果值传递给函数。

(4)参数求值:#define宏:参数用于宏定义时,每次都将重新求值,由于多次求值,具有副作用的参数可能会产生不可预测的结果。

函数:参数在函数调用前只求值一次,在函数中多次使用参数并不会导致多次求值过程;

(5)参数类型:#define宏:宏与类型无关,只要参数的操作是合法的,它可以用于任何参数类型。

函数: 函数的参数是与类型有关系的,如果参数的类型不同,就需要使用不同的函数,即使它们执行的任务是相同的。

  1. #define定义宏函数的时候记着加括号,比如定义一个宏函数实现比较两个数的大小;

#defined Max(x,y) ((x>y) ? (x):(y))

  1. #define和枚举enum的区别;

(1)#define宏常量是在预编译阶段进行简单替换。枚举常量则是在编译的时候确定其值。

(2)一般在编译器里,可以调试枚举常量,但是不能调试宏常量。

(3)枚举可以一次定义大量相关的常量,而#define宏一次只能定义一个。

10.掌握数组指针的定义,指针数组的定义,函数指针的定义及使用,函数指针数组的定义;

(1)数组指针:int (*p)[ ];(一个指向数组的指针)

(2)指针数组:int *p[ ];(一个数组,数组内存放着指针)

(3)函数指针:int (*p)(int);(一个指向函数地址的指针)

(4)函数指针数组:int (*p[ ])(int);(一个数组,数组内存放着指向函数地址的指针)

11.内存的分配方式有几种?造成内存泄漏的原因以及怎么防止内存泄漏?

(1) 内存的分配方式:

1、从静态存储区域分配内存。程序编译的时候内存已经分配好了,并且在程序的整个运行期间都存在,例如全局变量。

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

3、在堆上分配内存,亦称动态内存分配,程序在运行的时候用malloc函数或new运算符申请任意大小的内存,程序员要用free函数或delete运算符释放内存。

(2) 内存泄漏的原因:没有释放向系统申请的内存;

(3) 防止内存泄漏的方法:

1、良好的编码规范;

    2、使用内存泄漏的检测工具(ccmalloc、Dmalloc等)

  1. 请简单描述Windows内存管理的方法;

内存管理有块式管理,页式管理,段式和段页式管理。现在常用段页式管理

块式管理:把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。这样会造成很大的浪费,平均浪费了50%的内存空间,但是易于管理。

页式管理:把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。

段式管理:把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上。

段页式管理:结合了段式管理和页式管理的优点。把主存分为若干页,每一页又分为若干段。

  1. malloc与new的区别?Realloc等函数的作用?

(1)malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。 

(2)对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc /free. 

(3)因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以一个能完成清理与释放内存工作的运算符delete.注意new/delete不是库函数。 

(4)C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存

(5)new可以认为是malloc加构造函数的执行。New出来的指针是直接带类型信息的。而malloc返回的都是void指针

  1. 位操作的掩码的使用:&(位与)|(位或)~(取反)^(或)>>(位右移)<<(位左移)

打开位:打开位置1,其它位置0,源数据 = 源数据 OR 位掩码。

关闭位:关闭位置0,其它位置1,源数据 = 源数据 AND 位掩码。

转置位:利用位与或的特性,0^B为B,1^1为0,1^1为0,就把想转置的位转置了。

a = a^b;

b = a^b;   利用异或运算,不设置第三个变量就可以实现整型变量值的交换;

a = a^b;

  1. Sizeof与strlen的区别;

(1)strlen是库函数,遇到\0结束,字符串长度不包括\0;sizeof是运算符,字符串长度包括\0;

(2)strlen的结果在运行的时候才能计算出来,用来计算字符串的长度,不是计算占内存的大小的;

(3)sizeof操作符的结果类型是size_t(int类型);

(4)sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以\0结尾的。sizeof还可以用函数做参数(如sizeof(fun()) );

(5)sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧;

(6)数组做sizeof的参数不退化(还表示的是数组),传递给strlen后退化为指针;

(7)当用于一个结构类型活变量时,sizeof返回实际的大小 ,sizeof操作符不能返回动态地分配了的数组或外部的数组的尺寸。

  1. 指针与数组的区别:(至少总结七条)
  1. 数组长度固定,指针可以动态分配内存。
  2. 指针可以随时指向任意类型的内存块,而数组可以在静态存储区被创建。
  3. 修改的内容不同。

(4)在内存中所占的字节数不同。

  1. 怎么杜绝野指针?

(1)良好的编码习惯;

(2)凡定义的指针变量初始化为NULL;指针变量被free或delet之后将其值赋为NULL;

  1. 按值传递与按地址传递

数据传递方式有两种方式:按值传递与按地址传递。

(1)按值传递参数按值传递参数时,是将实在参数的值复制一个形式参数中,如果在调用过程中改变了形式参数的值,不会影响实在参数本身,即实在参数保持调用前的值不变。

(2)按地址传递:按地址传递参数时,把实在参数的地址传送给被调用过程,形式参数和实在参数共用内存的同一地址。在被调用过程中,形式参数的值一旦改变,相应实参的值也跟着改变。

  1. 源文件到可执行文件要经过那几个步骤?每个步骤做什么?比如说预处理都做什么工作,做好能够详细的阐述链接过程所要做的工作,一定要背下来并且理解;

源程序(*.c)—  预处理  编译(*.s)  汇编*.o  链接 — 可执行文件

1.预处理过程主要处理的是#include、#define、#if、#else、#ifdef、#endif等指令以及处理注释、行号(用于调试)等工作

     (1)头文件展开;(2)宏替换;(3)条件编译;

2.编译将源码编译为汇编代码

3.汇编将汇编代码汇编为目标代码

4.链接将目标代码链接为可执行文件;(静态链接,动态链接)

猜你喜欢

转载自blog.csdn.net/cainiao000001/article/details/81635703
今日推荐