谭浩强《c语言程序设计》第四版--重点难点总结

1、什么叫结构化程序设计,主要内容是什么?
* P33页

2、while 和do–while的执行方式和区别?P111
* do 在循环体之前执行,这种循环体至少执行一次。
* while:先判断再执行循环体;do–while 先执行再判断;
* 重点:循环次数的准确计算。
* 使用环境:当循环体至少需要执行一次时;
* while(6) 表达式为真? c语言规定:0为逻辑假;非零为逻辑真;所以表达式计算结果非零也是逻辑真;
* 语法格式:do{}while();
* while 语句可以使用break和continue语句。break终止循环,continue终止本次循环;

3、预处理指令?
* #define
* #define A ** 任何文本替换。
* #define A a\

                                       b
* 如果#define 的定义较长,采用\ 运算符。除了最后一行,其他行加\。程序中的结构体定义和初始化。
* 宏与函数的区别?宏常用于执行简单的计算。
* 原因:调用和从函数返回的代码很可能比执行这个小型代码计算工作的代码大,所以使用宏比使用函数再程序的规模和速度方面都更胜一筹。
* 函数的参数必须声明为一种特定类型,函数只有在类型合适时使用。而宏可用于整形,浮点型等。宏与参数类型无关。
* 调用函数时求值一次并返回,表达式结果更容易预测,而宏的操作符优先级需要又上下文决定;
* 由于调用宏和调用函数的方式相同,因此常用大写描述宏,用小写描述函数;如: MAX(a,b);  max(a,b);
* #undef
* 用于移除一个宏定义;
* #if
* #elif
* #else
* endif
* 条件编译。可选择性对某条语句进行编译或被忽略。可方便用于代码调试阶段,不建议应用于产品版本。
* 可用于不同销售版本,如同步机代码和异步机代码条件编译;
* 类似与代码注释屏蔽。
* 如:  
* #if DEBUG
*     statement;
* #endif
* #define DEBUG 1
* #ifdef 
* #endif
* 表达是否定义问题;
* #include
* 表达文件包含,文件取代该指令。
* 注意:当头文件被包含时,位于头文件内的所有内容都要被编译;
* 注意多重包含问题,头文件往往与条件编译配合使用;
* 如果可能,应避免多重包含,会拖慢编译速度;
* 不同集合的声明分离到不同的头文件以改善信息隐藏;

4、常变量和符号常量的区别?
* 常变量: const float pi=3.13159;
* 符号常量: #define PI 3.14159
* 常变量占据存储空间;
* []用法区别?

5、什么是枚举类型?有哪些应用?
* enum
* 变量只有几种可能的取值。
* http://c.biancheng.net/view/2034.html
* 可用于状态机或多分支代码的选择执行,比较直观。
* 汇川M380代码举例:
* enum RUN_STATUS
* {
* RUN_STATUS_WAIT, // 等待启动
* RUN_STATUS_ZERO, // 零频运行
* RUN_STATUS_START, // 启动
* RUN_STATUS_NORMAL, // (正常)运行
* RUN_STATUS_STOP, // 停机
* RUN_STATUS_JOG, // 点动运行
* RUN_STATUS_POS_CTRL, // 位置控制
*
* RUN_STATUS_TUNE, // 调谐运行
* RUN_STATUS_DI_BRAKE_DEC, // DI端子直流制动频率减速
* RUN_STATUS_DI_BRAKE, // DI端子的直流制动(非启动直流制动和停机直流制动)
* RUN_STATUS_LOSE_LOAD, // 掉载运行
* RUN_STATUS_SHUT_DOWN // shut down, 关断
* };
* extern enum RUN_STATUS runStatus;

6、共用体类型?
* 同一个内存单元存储不同的类型变量,变量覆盖存储。
* 方便内存的不同访问形式;
* M380代码举例:
* union RUN_FLAG
* {
* Uint16 all;
* struct RUN_FLAG_BITS bit;
* };
* extern union RUN_FLAG runFlag;

7、DSP2812的整形,长整型等字节数为多少?
* 32位CPU的含义:内存编址位32位;即寻址位宽32位;
* C语言不确定不同数据类型的字节数,提高了C语言的通用性,具体字节数由编译器决定。
* 意义:确定数据范围,防止数据截断及溢出。
* 位宽:
* #ifndef DSP28_DATA_TYPES
* #define DSP28_DATA_TYPES
* typedef int int16;
* typedef long int32;
* typedef long long int64;
* typedef unsigned int Uint16;
* typedef unsigned long Uint32;
* typedef unsigned long long Uint64;
* typedef float float32;
* typedef long double float64;
* #endif
* 程序特别采用typedef对数据类型位数进行解释。

8、负数的二进制描述?
* 补码存放原则。 对应的正数的二进制 正数取反加一。
* -14~~~14(0000 1110)~11110001~11110010
*

9、除法运算A/B,当B为0时?10、数据类型的混合运算?P54页
* 如果运算符两侧的数据类型不同,则先自动进行类型转换,使两者成为同一种数据类型,然后进行运算。
* 强制类型转换的括号问题。

11、强制类型转换和自动类型转换?
* (int)a,原始的a的类型未改变;
* 括号运算符优先级最高;
* 自动类型转换是由低类型往高类型自动转换;若要让变量由高往低转换,需要用强制类型转换;
* 取余运算%,需要强制类型转换,否则不合法;实参和形参对应必须采用强制类型转换;

12,13、inti;i=3.68,i的值为多少?
* i为3.浮点数赋值给整形时,先对浮点数取整,再传递;
* 占字节多的整形变量赋值给字节少的整型时,只将低字节原封不动送到被赋值变量。“即会发生截断问题”。
* 整型数据传递属于按存储形式直接传送;实型与整型传递需要变换存储形式;

14、int a,b,c=5;的赋值含义?
* a,b,c为整形变量,但只对c赋值;
* 需要对多个变量赋值的写法:int a=5,b=5,c=5;

15、若a=4;b=5;求a&&b,a||b,a&b,a|b?
* 逻辑运算符:&& || 结果为真或假。 a,b为非零数,所以a&&b为1,a||b为1;
* 位运算符。 & | ^ ~ a&b为:0100&0101 .
* 异或,相同为0,不同为1;
* 位运算符的操作数要求为整型;

16、Max=(a>b)?a:b;
* 条件运算符:
* 运算符的优先级很低,是c语言唯一的一个三目运算符。
* 首先计算表达式1,若为真(非零)那么表达式的值为表达式2,表达式3不求值,否则为表达式3,此时表达式2不求值;

17、switch语句中若case语句没有break,会出现什么问题?
* case是入口标号。执行完一个case标号后面的语句后,就从此标号开始执行下去,不再进行判断;
* 因此若没有break,将继续执行;
* M380代码举例:
* case SYNC_SVC:
* case SYNC_VF:
* case ASYNC_VF:
* if(gMainStatus.RunStep == STATUS_SPEED_CHECK)
* {
* gOutVolt.Volt = gFeisu.VoltCheck;
* gMainCmd.FreqSyn = gFeisu.SpeedCheck;
* gMainCmd.FreqToFunc = gMainCmd.FreqSyn;
* gVFPar.FreqApply = gMainCmd.FreqSyn;
* gOutVolt.VoltPhaseApply = (gFeisu.SpeedLast > 0) ? 16384 : -16384;
* }
* break;
* switch(表达式),表达式的值必须为整型。

18,19、for语句的执行过程计算sum值。
* for(expression1;exp2;exp3),exp1为初始化部分,只在循环开始时执行一次;exp2为条件部分,在循环体每次执行前都要执行一次;exp3为调整部分,在循环体每次执行完毕后,在条件部分执行前执行。
* 等价于:
* exp1;
* while(exp2)
* {
* 循环体;
* exp3;
* }
* for语句中也可以使用break和continue。break指直接退出整合for循环;continue指结束本次循环,直接执行调整部分。
* for用于循环处理对比while的优点?
* for循环将操作循环的表达式收集在一次,当循环体较大时,该优点较突出;

20、如何为二维数组赋值?
* 二维数组: float A[4][4];行列引用都从0开始。A[4][4]的引用是错误的;
* 初始化方式1:
* A[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}};
* 初始化方式2:
* A[4][4]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
* 初始化方式3:
* A[4][4]={{1},{2},{3}};
* 部分赋值;

21、字符数组末尾’\0’的作用,常量数组末尾有无‘\0’?
* 为弱化字符数组的宽度,采用’\0’作为字符数组的结束标志;

22、有参函数调用,什么是实际参数,什么是形式参数?
* 主调函数吧实际参数的值传递给被调用函数的形式参数;
* 定义函数时,函数名后为形参列表;调用函数时:函数名后时实参列表;
* 类型名 函数名(形参列表)
* {
* 函数体
* }
* 有参函数调用采用值传递;指针作为形参,相当于传址调用。会更改实参的值;
* 函数返回: return x; 或 return(x);返回值类型由函数类型决定。

23、定义函数时,需要在函数名前增加类型名,类型名与什么有关?
* 定义函数时需要指定函数的类型标识符。以便指定函数带回来的值的类型。无返回值的函数类型用void。
* 类型名决定了函数返回值的数据类型。

24、函数的声明?作用?怎么进行声明?
* 当要被调用函数的定义位置在调用它的函数的后面,应该在主调函数中对被调用的函数做声明。
* 函数声明:把函数名,函数参数个数,参数类型等信息通知编译系统,以便将进行合法性检查。确保函数被正确调用。
* 进行原型声明最安全的方式:
* 将原型置于一个单独的文件,当其他源文件需要这个函数的原型时,采用#include 包含该文件;
* 避免多次拷贝错误,也方便维护。
* 举例:M380–MotorSvcInclude.h
* /供外部引用函数声明****/
* void ResetSVC(void);
* void SVCCalRotorSpeed(void);
* void SvcCalOutVolt(void);
* void SVCCalFlux_380(void);
* void SvcCalOutVolt_380(void);
* void SVCCalRotorSpeed_380(void);
* #include “MotorSvcInclude.h”

25,26、变量的定义方式?main里定义的变量能否在其他函数中应用?
* 局部变量和全局变量
* 定义变量的3种情况;
* 1) 在函数的开头定义;
* 2)在函数内的复合语句内定义;
* if (tickerTempDeal >= TEMPERATURE_CALC_PERIOD / TEMPERATURE_CALL_PERIOD) // 时间错开
* {
* Uint32 k;
* LINE_STRUCT aiLine = LINE_STRTUCT_DEFALUTS;
*
* Uint16 p = &funcCode.code.aiCalibrateCurve[2].before1; // 实测电压
* #define MOTOR_T_SENSOR_AI_MAX_VOLTAGE_IDEA 3300 // 3300mv
* // 获取温度检测的采样电压
* k = (4095L << 4) * tickerTempDeal / MOTOR_T_SENSOR_AI_MAX_VOLTAGE_IDEA;
* temperatureVoltageOrigin = tempSampleSum / k;
* // 电机温度传感器AI2采样校正曲线
* aiLine.mode = 1; // 不限幅
* aiLine.y1 = ((int32)(int16)(
(p + 0)) * 0x7FFF) / MOTOR_T_SENSOR_AI_MAX_VOLTAGE_IDEA; // (理想)输入电压1,精密仪器测量电压
* 3)在函数的外部定义;
* 1)2)都属于局部变量,3)为全局变量;作用域为定义位置以后;
* main中定义的变量也只在main中有效,并不因为在中函数中定义二在整个文件或程序中有效;
* 局部变量由于作用范围小,因此不同函数可以采用相同的局部变量名;全局变量在程序执行过程中都占用存储单元;全局变量时程序耦合复杂,降低程序的可读性和可维护性,因此应减少全局变量的使用。

27,28,29,30、什么是静态存储区,动态存储区,动态存储区存放哪些数据?
* 静态存储区指程序运行期间,由系统分配固定的存储空间的方式。而动态存储区是程序运行期间根据需要进行动态的分配存储空间的方式。
* 全局变量存放在静态存储区;
* 函数形参,自动变量,函数调用的现场保护和返回地址存放在动态存储区;即堆栈中。
* 变量定义由两部分构成:存储类型,数据类型。存储类型:auto, static, register, extern
* auto
* 不声明为static的局部变量、形参都是auto类型;auto可以省略;
* static
* 局部变量在函数调用结束后不消失,而保留原值,存储单元不释放;用static。即静态局部变量;
* 局部变量加static后,作用域仍然在该代码块中或该函数中有效;
* 会降低程序可读性;
* 全局变量 加 static
* 将外部变量的作用域限制在本文件中
* 静态外部变量,便于程序的模块化;
* register
* 寄存器变量存储在CPU的硬件寄存器中,存取速度快;
* extern
* 外部变量声明;用于作用域扩展;
* 全局变量作用域扩展到其他文件方法
* 在一个文件中定义外部变量;
* 在另一个文件中用extern对该变量声明;

31、变量的定义和声明的区别?
* 定义也称:定义型声明,声明也称:引用型声明;
* 定义会建立存储空间;
* 变量的定义只能出现一次,声明可以出现多次;
* int a;为定义 ;extern int a;为声明;
* 1. 不要把变量定义放入.h文件,这样容易导致重复定义错误。
* 2. 尽量使用static关键字把变量定义限制于该源文件作用域,除非变量被设计成全局的。
* 3. 可以在头文件中声明一个变量,在用的时候包含这个头文件就声明了这个变量。
* function.c的神奇之处。

32、内部函数和外部函数的区别?
* 内部函数
* static 类型名 函数名(形参列表);
* 表示该函数只能被本文件中的其他函数调用;
* 外部函数
* 定义函数时,加extern表示为外部函数,省略extern则默认为外部函数,可供其他文件调用;

33,34、指针是什么?什么是指针变量?
* 指针就是变量的地址,或者函数首地址;地址形象化称为指针;
* 指针的两个信息:内存编号,指向的数据类型(只有指定数据类型,才能确定寻址方式);
* 指针是带类型的地址;
* 如果用一个变量专门存放另一个变量的地址,则它称为指针变量;
* DSP2812为32位处理器,意味着指针变量需要32位表示;
* 运算符: * (指针运算符) & (取地址运算符)
* 指针变量的定义和引用:
* int p;指:p为指向整型数据的指针变量; 而不是int p;
* 变量是p,而不是*p;
* p=&a;
* 例子: *100=25? 错误
* 正确写法:
* *(int *)100=25; //*的对象必须是指针变量,因此先将100强制类型转换为整形指针变量,再赋值,即将地址100中放置整型数据25。
* 指针变量作为形参实现传址调用的应用
* MD380传址调用代码举例:
* void inline UVWToAlphBetaAxes(UVW_STRUCT_Q24 * uvw, ALPHABETA_STRUCT * AlphBeta)
* {
* AlphBeta->Alph = ((llong)uvw->U * 23170L)>>15;
* AlphBeta->Beta = ((llong)((long)uvw->V - (long)uvw->W) * 13377L)>>15;
* }
* inline 关键字 :表示内联函数。 内联函数在可读性方面与函数是相同的,而在编译时是将函数直接嵌入调用程序的主体,省去了调用/返回指令,这样在运行时速度更快。
* C语言允许直接访问物理地址,可直接对硬件进行操作。C语言同时具有高级语言的功能,又具有低级语言的功能,C语言的这种双重性,使c语言不仅成为系统描述语言优势通用的程序设计语言。

35、采用指针引用数组变量:
* 指针可以进行加法和减法运算。计算结果是另一个指针。由于指针存放的是地址,所以指针加法的含义是指向n后的地址。n由指针变量的类型确定。
* p+=3指数组的第四个元素的地址;

36、int *p[4]; 和 int (p)[4]的区别是什么?
* int p[4]; 指针数组。
* []的优先级高于
,所以P[4]首先是一维数组,之后和
结合,表示对数组元素间接访问操作后是一个整形。所以:每个元素中存放的是一个整形变量的地址;也就是说该数组存放了四个变量的地址。
* int (*p)[4] 一维数组的指针变量。
* *p表示p是一个指针;[4] 表示指针指向的对象的类型是具有4个元素的数组;int 表示该数组的元素是int类型的.
* (*p)[4]表示每个元素是一维数组的首地址,一共保存了4个一维数组的首地址。

37、什么是函数指针,函数指针的应用有哪些?
* 函数编译时,会把函数的源代码转换为可执行代码并分配存储空间。这段代码有一个起始地址。每次调用该函数时,从该入口开始执行。函数名就是该入口地址。因此函数名就是函数的指针。代表函数的起始地址。
* 可以定义一个指向函数的指针变量。存放某一个函数的起始地址。
* int (*p)(int,int);
* p=max;c=(*p)(a,b);
*
* 用函数名调用函数只能调用一个函数,而采用函数指针调用函数,可以通过函数指针的不同赋值调用不同的函数。
* 比如调用同步机,异步机的矢量控制代码。

38、函数指针定义格式和引用格式?
* 类型名 (*指针变量名)(函数参数列表)
* eg: int (*p)(int ,int )
* 特征: 指针变量后有括号。说明是函数指针。
* 函数指针只能指向具有特定特征的函数。因而所有被同一指针运用的函数必须具有相同的参数和返回类型。
* 引用格式:

	*      1、函数指针的定义;

		*      typedef struct {  int32_lib Ref;      int32_lib Fdb;      int32_lib Err;      int32_lib Kp;      int32_lib Up;      int32_lib Ui;      int32_lib Ud;      int32_lib OutPreSat;      int32_lib OutMax;      int32_lib OutMin;      int32_lib Out;      int32_lib SatErr;      int32_lib Ki;      int32_lib Kc;      int32_lib Kd;      int32_lib Up1;      int16_lib Kamp;      void  (*calc)();      void  (*calc32)();      void  (*reset)();        } PID_LIB;            typedef PID_LIB *PID_handle_lib;
	*      2、指针赋值;

		*   PID_LIB   pid_Udc_lib = PID_DEFAULTS_LIB;  
		*    #define PID_DEFAULTS_LIB { 0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      0, \      (void (*)(Uint32_lib))pid_calc_lib, \      (void (*)(Uint32_lib))pid32_calc_lib, \      (void (*)(Uint32_lib))pid_reset_lib}
		* void pid_reset_lib(PID_handle_lib);void pid_calc_lib(PID_handle_lib);void pid32_calc_lib(PID_handle_lib);
  1. Int *p(int x,int y);和int (*p)(int x,int y)的区别?
    • 指针函数。 Int *p(int x,int y) p先与括号运算符结合,表示p为函数形式。再与 * 运算符结合表明p是指针。
    • Int *p(int x,int y) 返回值为变量的地址。
    • 含义:函数计算完成后返回一个地址作为返回参数。
    • 运算符优先级中:括号运算符优先级最高。

40、什么是动态内存分配?
* malloc所分配的是一块连续的内存,以字节为单位,并且不带任何的类型信息free用于将动态内存归还系统void* malloc(size_t size);void free(void* pointer);
* 返回指针的值是所分配内存区域中第一个字节的地址,当分配内存失败时,返回空指针。
* 动态内存分配是C语言中的强大功能程序能够在需要的时候有机会使用更多的内存

41、如何声明结构体类型?
* 结构体是用户自定义类型的一种。
* 是一种聚合数据类型。
* 声明的几种形式。
* 1.先声明结构体类型,再定义该类型变量:struct UDP_Server_Thread_Para{void *pData;int Len;};UDP_Server_Thread_Para UDPThreadPara1,UDPThreadPara2;2.在声明的同时定义变量:struct UDP_Server_Thread_Para{void *pData;int Len;}UDPThreadPara1,UDPThreadPara2;3.不指定类型名直接定义结构体类型变量:struct{void *pData;int Len;}UDPThreadPara1,UDPThreadPara2;42、什么是结构体数组,如何声明结构体数组?
* struct STUDENT stu[10];
* 该结构体数组,共有 10 个元素,每个元素都是一个结构体变量,都包含所有的结构体成员。

43、P.student, (*p).student , p->student 的区别和联系?
*

44、关键字 typedef的作用?
* typedef是在计算机编程语言中用来为复杂的声明定义简单的别名,它与宏定义有些差异。
* 在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明typedef int int16_lib;typedef long int32_lib;typedef unsigned int Uint16_lib;typedef unsigned long Uint32_lib;typedef float float32_lib;typedef long double float64_lib;typedef long long int64_lib;typedef unsigned long long Uint64_lib;

软件工程师的基本素养:
* 编程语言的熟悉,既编程速度的加快;
* 代码质量的提高,既编写出的代码漏洞少、缺陷密度低;
* 以及编程风格改善,代码易于阅读、鲁棒性好;
* 代码安全性好,能考虑到多种安全场景,避免遗留安全方面的坑。

静态测试 是对代码进行扫描分析,检测它的语法规则复杂度等是否符合要求,它主要是为软件的质量保证提供依据,以提高软件的可靠性和易维护性。静态测试主要包括:
* 编程标准验证;
* 数据流分析技术;
* 质量度量信息;
* 代码结构可视化显示;
* 测试外壳的创建
*
* 动态测试 是使被测代码在相对真实环境下运行,从多角度观察程序运行时能体现的功能、逻辑、行为、结构等的行为,以发现其中的错误现象。对于嵌入式系统,要想保证测试的真实性,就需要将被测代码下载到目标板运行,并且测试系统不要影响目标系统的运行,就需一定硬件支持。动态测试主要包括:
* 功能的测试;
* 代码覆盖率 (CodeCoverage);
* 性能分析测试;
* 内存分析;
* 逻辑触发执行跟踪;
* 实时多任务操作系统分析。

* 另一种静态分析工具:logiscope
* 1、audit 审查检查工具
* 2、rulechecker 静态百合

参考文献:
c语言入门

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值