赞
踩
(1)代码总体原则 —— 清晰第一
清晰性是易于维护、易于重构的程序必需具备的特征。一般情况下,代码的可阅读性高于性能,只有确定性能是瓶颈时,才应该主动优化。
(2)代码总体原则 —— 简洁为美
简洁就是易于理解并且易于实现。代码越长越难以看懂,也就越容易在修改时引入错误。
(3)代码总体原则 ——代码风格
选择合适的风格,与代码原有风格保持一致 。 在公司已有编码规范的指导下,审慎地编排代码以使代码尽可能清晰。
对于C语言来说,头文件的设计体现了大部分的系统设计。不合理的头文件布局是编译时间过长的根因。合理的头文件划分体现了系统设计的思想。
说明:
头文件是模块(Module)或单元(Unit)的对外接口。头文件中应放置对外部的声明,如对外提供的函数声明、宏定义、类型定义等。
内部使用的函数声明不应放在头文件中。
内部使用的宏、枚举、结构定义不应放入头文件中。
变量定义不应放在头文件中,应放在.c文件中。
变量的声明尽量不要放在头文件中,亦即尽量不要使用全局变量作为接口。
变量是模块或单元的内部实现细节,不应通过在头文件中声明的方式直接暴露给外部,应通过函数接口的方式进行对外暴露。即使必须使用全局变量,也只应当在.c中定义全局变量,在.h中仅声明变量为全局的。
说明:
头文件的包含关系是一种依赖,一般来说,应当让不稳定的模块依赖稳定的模块,从而当不稳定的模块发生变化时,不会影响(编译)稳定的模块。
说明:
如果一个.c文件不需要对外公布任何接口,则其就不应当存在,除非它是程序的入口,如main函数所在的文件。
另外,一旦把私有定义、声明放到独立的头文件中,就无法从技术上避免别人include,难以保证这些定义最后真的只是私有的。
本规则反过来并不一定成立。有些特别简单的头文件,如命令ID定义头文件,不需要有对应的.c存在。
说明:简单的说,自包含就是任意一个头文件均可独立编译。如果一个文件包含某个头文件,还要包含另外一个头文件才能工作的话,就会增加交流障碍,给这个头文件的用户增添不必要的负担。
注意:该规则需要与“.c/.h文件禁止包含用不到的头文件”规则一起使用,不能为了让a.h自包含,而在a.h中包含不必要的头文件。a.h要刚刚可以自包含,不能在a.h中多包含任何满足自包含之外的其他头文件。
说明:多次包含一个头文件可以通过认真的设计来避免。如果不能做到这一点,就需要采取阻止头文件内容被包含多于一次的机制。通常的手段是为每个文件配置一个宏,当头文件第一次被包含时就定义这个宏,并在头文件被再次包含时使用它以排除文件内容。
说明:在头文件中定义变量,将会由于头文件被其他.c文件包含而导致变量重复定义。
函数设计的精髓:编写整洁函数,同时把代码有效组织起来。
整洁函数要求:代码简单直接、不隐藏设计者的意图、用干净利落的抽象和直截了当的控制语句将函数有机组织起来。
代码的有效组织包括:逻辑层组织和物理层组织两个方面。逻辑层,主要是把不同功能的函数通过某种联系组织起来,主要关注模块间的接口,也就是模块的架构。物理层,无论使用什么样的目录或者名字空间等,需要把函数用一种标准的方法组织起来。例如:设计良好的目录结构、函数名字、文件组织等,这样可以方便查找。
说明:本规则仅对新增函数做要求,对已有的代码建议不增加嵌套层次。函数的代码块嵌套深度指的是函数中的代码控制块(例如:if、for、while、switch等)之间互相包含的深度。每级嵌套都会增加阅读代码时的脑力消耗,因为需要在脑子里维护一个“栈”(比如,进入条件语句、进入循环)。应该做进一步的功能分解,从而避免使代码的阅读者一次记住太多的上下文。
说明:可重入函数是指可能被多个任务并发调用的函数。在多任务操作系统中,函数具有可重入性是多个任务可以共用此函数的必要条件。共享变量指的全局变量和static变量。
编写C语言的可重入函数时,不应使用static局部变量,否则必须经过特殊处理,才能使函数具有可重入性。
说明:
扇出是指一个函数直接调用其它函数的数目,而扇入是指有多少上级函数调用它。
扇出过大,表明函数过分复杂,需要控制和协调过多的下级函数;而扇出过小,例如:总是1,表明函数的调用层次可能过多,这样不利于程序阅读和函数结构的分析,并且程序运行时会对系统资源如堆栈空间等造成压力。通常函数比较合理的扇出(调度函数除外)通常是3~5。
扇出太大,一般是由于缺乏中间层次,可适当增加中间层次的函数。扇出太小,可把下级函数进一步分解多个函数,或合并到上级函数中。当然分解或合并函数时,不能改变要实现的功能,也不能违背函数间的独立性。
扇入越大,表明使用此函数的上级函数越多,这样的函数使用效率高,但不能违背函数间的独立性而单纯地追求高扇入。公共模块中的函数及底层函数应该有较高的扇入。
较良好的软件结构通常是顶层函数的扇出较高,中层函数的扇出较少,而底层函数则扇入到公共模块中。
说明:不变的值更易于理解/跟踪和分析,把const作为默认选项,在编译时会对其进行检查,使代码更牢固/更安全。
说明:带有内部“存储器”的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样的函数既不易于理解又不利于测试和维护。
说明:如果一个函数只是在同一文件中的其他地方调用,那么就用static声明。使用static确保只是在声明它的文件中是可见的,并且避免了和其他文件或库中的相同标识符发生混淆的可能性。
建议定义一个STATIC宏,在调试阶段,将STATIC定义为static,版本发布时,改为空,以便于后续的打热补丁等操作。
#ifdef _DEBUG
#define STATIC static
#else
#define STATIC
#endif
一些常见可以缩写的例子: argument 可缩写为 arg buffer 可缩写为 buff clock 可缩写为 clk command 可缩写为 cmd compare 可缩写为 cmp configuration 可缩写为 cfg device 可缩写为 dev error 可缩写为 err hexadecimal 可缩写为 hex increment 可缩写为 inc initialize 可缩写为 init maximum 可缩写为 max message 可缩写为 msg minimum 可缩写为 min parameter 可缩写为 para previous 可缩写为 prev register 可缩写为 reg semaphore 可缩写为 sem statistic 可缩写为 stat synchronize 可缩写为 sync temp 可缩写为 tmp
说明:单个文件内部可以使用static的全局变量,可以将其理解为类的私有成员变量。全局变量应该是模块的私有数据,不能作用对外的接口使用,使用static类型定义,可以有效防止外部文件的非正常访问。
说明:通讯报文中,字节序是一个重要的问题,cpu类型复杂多样,大小端、32位/64位的处理器也都有,如果结构会在报文交互过程中使用,必须考虑字节序问题。由于位域在不同字节序下,表现看起来差别更大,所以更需要注意。对于这种跨平台的交互,数据成员发送前,都应该进行主机序到网络序的转换;接收时,也必须进行网络序到主机序的转换。
说明:避免直接暴露内部数据给外部模型使用,是防止模块间耦合最简单有效的方法。
说明:因为宏只是简单的代码替换,不会像函数一样先将参数计算后,再传递。
示例:
说明:更好的方法是多条语句写成do while(0)的方式
说明:使用魔鬼数字的弊端:代码难以理解;如果一个有含义的数字多处使用,一旦需要修改这个数值,代价惨重。使用明确的物理状态或物理意义的名称能增加信息,并能提供单一的维护点。
解决途径:对于局部使用的唯一含义的魔鬼数字,可以在代码周围增加说明注释,也可以定义局部const变量,变量命名自注释。
对于广泛使用的数字,必须定义const全局变量/宏;同样变量/宏命名应是自注释的。
0作为一个特殊的数字,作为一般默认值使用没有歧义时,不用特别定义。
说明:宏对比函数,有一些明显的缺点:
宏缺乏类型检查,不如函数调用检查严格。
宏展开可能会产生意想不到的副作用,如#define SQUARE(a) (a) * (a)这样的定义,如果是SQUARE(i++),就会导致i被加两次;如果是函数调用double square(double a) {return a * a;}则不会有此副作用。
以宏形式写的代码难以调试难以打断点,不利于定位问题。
宏如果调用的很多,会造成代码空间的浪费,不如函数空间效率高。
说明:
1、正确性,指程序要实现设计要求的功能。
2、简洁性,指程序易于理解并且易于实现。
3、可维护性,指程序被修改的能力,包括纠错、改进、新需求或功能规格变化的适应能力。
4、可靠性,指程序在给定时间间隔和环境条件下,按设计要求成功运行程序的概率。
5、可测试性,指软件发现故障并隔离、定位故障的能力,以及在一定的时间和成本前提下, 进行测试设计、测试执行的能力。
6、性能高效,指是尽可能少地占用系统资源,包括内存和执行时间。
7、可移植性,指为了在原来设计的特定环境之外运行,对系统进行修改的能力。
8、个人表达方式/个人方便性,指个人编程习惯。
坚持下列措施可以避免内存越界:
(1)数组的大小要考虑最大情况,避免数组分配空间不够;
(2)避免使用危险函数sprintf /vsprintf/strcpy/strcat/gets;
(3)操作字符串,使用相对安全的函数snprintf/strncpy/strncat/fgets代替;
(4)使用memcpy/memset时一定要确保长度不要越界字符串考虑最后的’\0’,确保所有字符串是以’\0’结束指针加减操作时,考虑指针类型长度数组下标进行检查使用时sizeof或者strlen计算结构/字符串长度,避免手工计算。
坚持下列措施可以避免内存泄漏:
(1)异常出口处检查内存、定时器/文件句柄/Socket/队列/信号量/GUI等资源是否全部释放;
(2)删除结构指针时,必须从底层向上层顺序删除;
(3)使用指针数组时,确保在释放数组时,数组中的每个元素指针是否已经提前被释放了;
(4)避免重复分配内存;
(5)小心使用有return、break语句的宏,确保前面资源已经释放;
(6)检查队列中每个成员是否释放。
坚持下列措施可以避免引用已经释放的内存空间:
(1)内存释放后,把指针置为NULL,使用内存指针前进行非空判断。
(2)耦合度较强的模块互相调用时,要仔细考虑其调用关系,防止已经删除的对象被再次使用。避免操作已发送消息的内存。
实例:
说明:goto语句会破坏程序的结构性,所以除非确实需要,最好不使用goto语句。可以利用goto语句方面退出多重循环;同一个函数体内部存在大量相同的逻辑但又不方便封装成函数的情况下,譬如反复执行文件操作,对文件操作失败以后的处理部分代码(譬如关闭文件句柄,释放动态申请的内存等等),一般会放在该函数体的最后部分,再需要的地方就
goto到那里,这样代码反而变得清晰简洁。实际也可以封装成函数或者封装成宏,但是这么做会让代码变得没那么直接明了。
说明:例如,使用线程池机制,避免线程频繁创建、销毁的系统调用;使用内存池,对于频繁申请、释放的小块内存,一次性申请一个大块的内存,当系统申请内存时,从内存池获取小块内存,使用完毕再释放到内存池中,避免内存申请释放的频繁系统调用。
说明:如果编译器支持inline,可以采用inline函数。在做这种优化的时候一定要注意下面inline函数的优点:其一编译时不用展开,代码SIZE小。其二可以加断点,易于定位问题,例如对于引用计数加减的时候。其三函数编译时,编译器会做语法检查。
例子:
说明:说明:除了少数操作符(函数调用操作符 ( )、&&、| |、? : 和 , (逗号))之外,子表达式所依据的运算次序是未指定的并会随时更改。注意,运算次序的问题不能使用括号来解决,因为这不是优先级的问题。将复合表达式分开写成若干个简单表达式,明确表达式的运算次序,就可以有效消除非预期副作用。
示例:
1、自增或自减操作符
x = b[i] + i++;
b[i] 的运算是先于还是后于 i ++ 的运算,表达式会产生不同的结果,把自增运算做为单独的语句,可以避免这个问题。
x = b[i] + i;
i ++;
2﹑函数参数
说明:函数参数通常从右到左压栈,但函数参数的计算次序不一定与压栈次序相同。
示例:x = func( i++, i); 应该修改代码明确先计算第一个参数:i++; x = func(i, i);
3、函数指针
说明:函数参数和函数自身地址的计算次序未定义。
p->task_start_fn(p++);
求函数地址p与计算p++无关,结果是任意值。必须单独计算p++:
p->task_start_fn§;
p++;
4﹑函数调用
编译器可能先计算fun1(),也可能先计算fun2(),由于x的结果依赖于函数fun1()/fun2()的计算次序(fun1()/fun2()被调用时修改和使用了同一个全局变量),则上面的代码存在问题。
应该修改代码明确fun1/ fun2的计算次序:
int x = fun1();
x = x + fun2();
5、嵌套赋值语句
说明:表达式中嵌套的赋值可以产生附加的副作用。不给这种能导致对运算次序的依赖提供任何机会的最好做法是,不要在表达式中嵌套赋值语句。
6、volatile访问
说明:限定符volatile表示可能被其它途径更改的变量,例如硬件自动更新的寄存器。编译器不会优化对volatile变量的读取。
示例:下面的写法可能无法实现作者预期的功能:
/* volume变量被定义为volatile类型*/
UINT16 x = ( volume << 3 ) | volume;
/* 在计算了其中一个子表达式的时候,volume的值可能已经被其它程序或硬件改变,导致另外一个子表达式的计算结果非预期,可能无法实现作者预期的功能 */
所以说volatile并不是可以解决所有的问题,例如:线程同步
7.避免把复合表达式与数学表达式混淆
例如:if(a < b < c)
看着有点懵
有些人会认为是if((a < b) && (b < c))
然而呢,是这样的
If((a < b) < c)
说明:不能假定用户输入都是合法的,因为难以保证不存在恶意用户,即使是合法用户也可能由于误用误操作而产生非法输入。用户输入通常需要经过检验以保证安全,特别是以下场景:
1、用户输入作为循环条件
2、用户输入作为数组下标
3、用户输入作为内存分配的尺寸参数
4、用户输入作为格式化字符串
5、用户输入作为业务数据(如作为命令执行参数、拼装sql语句、以特定格式持久化)
这些情况下如果不对用户数据做合法性验证,很可能导致DOS、内存越界、格式化字符串漏洞、命令注入、SQL注入、缓冲区溢出、数据破坏等问题。
6、可采取以下措施对用户输入检查:
7、用户输入作为数值的,做数值范围检查
8、用户输入是字符串的,检查字符串长度
9、用户输入作为格式化字符串的,检查关键字“%”
10、用户输入作为业务数据,对关键字进行检查、转义
说明:C语言中‟\0‟作为字符串的结束符,即NULL结束符。标准字符串处理函数(如strcpy()、strlen())依赖NULL结束符来确定字符串的长度。没有正确使用NULL结束字符串会导致缓冲区溢出和其它未定义的行为。
为了避免缓冲区溢出,常常会用相对安全的限制字符数量的字符串操作函数代替一些危险函数。如:
用strncpy()代替strcpy()
用strncat()代替strcat()
用snprintf()代替sprintf()
用fgets()代替gets()
用vsprintf代替vsnprintf
说明:边界不明确的字符串(如来自gets()、getenv()、scanf()的字符串),长度可能大于目标数组长度,直接拷贝到固定长度的数组中容易导致缓冲区溢出。
正确写法:计算字符串的实际长度,使用malloc分配指定长度的内存
说明:有时从带符号整型转换到无符号整型会发生符号错误,符号错误并不丢失数据,但数据失去了原来的含义。
带符号整型转换到无符号整型,最高位(high-order bit)会丧失其作为符号位的功能。如果该带符号整数的值非负,那么转换后值不变;如果该带符号整数的值为负,那么转换后的结果通常是一个非常大的正数。
说明:将一个较大整型转换为较小整型,并且该数的原值超出较小类型的表示范围,就会发生截断错误,原值的低位被保留而高位被丢弃。截断错误会引起数据丢失。使用截断后的变量进行内存操作,很可能会引发问题。
说明:调用格式化I/O函数时,不要直接或者间接将用户输入作为格式化字符串的一部分或者全部。攻击者对一个格式化字符串拥有部分或完全控制,存在以下风险:进程崩溃、查看栈的内容、改写内存、甚至执行任意代码。
说明:strlen()函数用于计算字符串的长度,它返回字符串中第一个NULL结束符之前的字符的数量。因此用strlen()处理文件I/O函数读取的内容时要小心,因为这些内容可能是二进制也可能是文本.
说明:字符I/O函数fgetc()、getc()和getchar()都从一个流读取一个字符,并把它以int值的形式返回。如果这个流到达了文件尾或者发生读取错误,函数返回EOF。fputc()、putc()、putchar()和ungetc()也返回一个字符或EOF。
如果这些I/O函数的返回值需要与EOF进行比较,不要将返回值转换为char类型。因为char
是有符号8位的值,int是32位的值。如果getchar()返回的字符的ASCII值为0xFF,转换为char类型后将被解释为EOF。因为这个值被有符号扩展为0xFFFFFFFF(EOF的值)执行比较。
确做法:使用int类型的变量接受getchar()的返回值。
对于sizeof(int) == sizeof(char)的平台,用int接收返回值也可能无法与EOF区分,这时要用feof()和ferror()检测文件尾和文件错误。
说明:C99函数system()通过调用一个系统定义的命令解析器(如UNIX的shell,Windows的CMD.exe)来执行一个指定的程序/命令。类似的还有POSIX的函数popen()。 如果system()
的参数由用户的输入组成,恶意用户可以通过构造恶意输入,改变system()调用的行为。
示例:system(sprintf(“any_exe %s”, input)); 如果恶意用户输入参数: happy; useradd attacker
最终shell将字符串“any_exe happy; useradd attacker”解释为两条独立的命令:
正确做法:使用POSIX函数execve()代替system()。
1.对于如下1和2的描述,以下哪种说法是正确的∶ (D)
1.堆内存释放后可以再访问,没有安全风险;
2.栈内存释放后可以再访问,没有安全风险
A. 只有1对
B.只有2对
C.1,2都对
D.1.2都不对
2.选择出正确的宏定义: (C)
A.#define RECT_AREA( a, b ) ( a * b )
B.#define RECT_AREA( a, b ) ( a )+ ( b )
C.#define RECT_AREA( a, b ) ( ( a ) * ( b ) )
D.#define RECT_AREA( a, b ) a * b
3.下列关于头文件或宏的描述错误的是 (C)
A.每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接口,同时.c/.h文件禁止包含用不到的头文件
B.禁止在头文件中定义变量
C.应尽可能使用函数代替宏,建议使用宏代替const定义常量
D.使用宏时,不允许参数发生变化
4.如果a.c包含了头文件a.h , a.h包含了头文件b.h , b.c也包含了b.h,那么当b.h发生改变时,哪些文件将会被重新编j译 (D)
A.b.c
B.a.h
C.a.c和a.h
D.a.c和b.c
5.关于程序效率的规范要求,哪一项描述是错误的 (A)
A.应该把执行概率较大的分支放在if else判断后面处理
B.将循环中与循环无关,不是每次循环都要做的操作,移到循环外部执行
C.创建资源库(线程池、内存池),以减少分配对象的开销
D.将多次被调用的“小函数”改为inline函数或者宏实现
6.关于命名正确的是 (C)
A.#define EXAMPLE_O_TEST_ #define EXAMPLE_1_TEST_
B.代码文件命名建议统-采用首字母大写其余字母小写的形式命名
C. int error_number; int number_of_completed_connection;
D.DWORD GetJinchengMulu( DWORD BufferLength,LPTSTR Buffer );
7.以下哪种措施不可以避免内存操f作越界 (D)
A.数组的大小要考虑最大情况,避免数组分配空间不够
B.字符串考虑最后的’\O’,确保所有字符串是以\0’结束
C.指针加减操作时,考虑指针类型长度
D.通过手工计算结构/字符串长度
8.下面说法措误的是 (D)
A.程序中要及时清除存储在可复用资源中的敏感信息
B.删除或修改没有效果的代码
C.删除或修改没有使用到的变量或值
D.非动态申请的内存也可以使用free手动释放
9.关于const的描述错误的是 (C)
A. const intA=1;定义了一个整型常量A,值为1,在任何时候值都不可变B.const char*A=“abc”;指针A指向的内容不可变
C. char * const A=“abc”;指针A指向的内容不可变.
D.const char * const A="abc” ;指针A和指向的内容都不可变
10.下面说法正确的是 (B)
.A一些存在的代码(声明或表达式),即使它被执行后,也不会对代码的结粜或数据的状态产生任何的彰响,所以可以不用理会这些代码
B.在使用像memcpy、strcpy、stncpy、sscanfO)、sprintf)、snprintfO和wcstombs0这样的函数时,必须检查源地髦和昌的地址是适存在震要的内存区域
C.调用格式化1/O函数时,可以直接或者间接将用户输入作为格式化字符串的一部分
D.申请内存的函数可以使用malloc、calloc及 alloca
11.对于字符串"abcdefghi0"使用多大字符数组进行存储最为合适(B)
A.10.0
B.11.0
C.12.0
D.20.0
12 空
13.下列关于整数的操作正确的是: ( INT32表示32位有符号整数,UINT32表示32位无符号整数,NT8表示8位有符号整数) (A)
A.INT32 Func(UINT32 ui1,UINT32 uf2,UINT32* ret) ( if( NULL m= ret ) ( returnERROR ,} if(UINT_MAX- uil) < ui2)(return ERROR; } else { *ret = uil+ ui2; } return OK;)
B.INT32 Func(INT32 si1, INT32 5i2,INT32 *ret) ( if ( NUL mm ret ) { return ERROR } 'ret = si1 * si2,return OK:}
C.INT32 Func(UINT32 ul, INT8 *ret) 【 if( NULL me ret ) { return ERROR; } ret =(INT8)ul; return (OK);}
D.#define BUF_SZE 10 int main(int argc, char argv(D) ( int length; char buf(BUF_SIZE if (argc != 3) ( return -1: } len
gth = atoi(argv[1]); if (length < BUF_SIZE) ( memcpy(but, argv[2], length); printf("Data copiedin “T; ) else ( printf(Too many dataln”); })
14.如果函数foo仅在a.c内可见,则应当如下声明 (B)
A.在a.c的头部声明int foo(void);
B.在a.c的头部声明static int foo(void);
C.在a.h的头部声明static int foo(void);
D.在a.c的头部声明extern int foo(void);
15.以下关于安全编码的说法,错误的是 (D)
A.无论是明文口令还是密文口令,都应该禁止保存在日志文件中
B.删除或修改一些即使执行后、也不会有任何效果的代码
C.程序在运行时应该只分配能完成其任务的最小权限
D.程序执行任务完毕时,应该尽快收回其权限,对于权限的撤销顺序可以不用考虑
16 空
17.需要对指定申请内存大小的整数值进行合法性校验,是因为∶ ( C)
1.使用0字节长度去申请内存的行为是没有定义的,在引用内存申请函数返回的地址时会引发不可预知或不能立即发现的问题﹔
2.使用负数长度去审请内存,负数会被当成一个很大的无符号整数,从而导数因申请内存过大而出现失败
A.只有1对
B.只有2对
C.1,2都对
D.1,2都不对
18.下列哪种方式产生的随机数是不安全、容易被预测的 (A)
A.C99的rand()
B.Unix/Linux下读取/dev/random文件
C. Windows使用随机数生成函数
D.以上选项都不对
19.针对函数的设计的说法,下面哪个是正确的 (B)
A.应尽量设计多用途面面俱到的函数;函数的参数个数可以超过5个
B.在源文件范围内声明和定义的所有函数,除非外部可见,否则应该增加static关键字
C.设计高扇入,台理扇出(小于7)的函数;新增函数的代码块嵌套不超过5层
D.函数中的冗余代码只要不影响函数的效率,不需要删除
20.下面哪个算法是禁止使用的弱加密算法
A.SHA2
B.RSA
C.DSA
D.DES
21.以下说法错误的有 (C,D)
A.必须对指定申请内存大小的整数值进行合法性校验
B.禁止重复释放内存
C.堆内存释放不可以再访问,栈内存释放后可以再访问
D.为了使用方便,建议使用alloca函数申请内存
22.如果a.c只对外提供一个void bar()函数作为接口,而bar函数的实现部分需要使用b.c中的void foo()函数,以下做法中错误的是 (A)
A.在b.c中声明extern void foo(),在a.c中声明extern void foo()
B.在b.h中声明extern void foo(),在a.c中声明extern void foo()
C.在b.h中声明extern void foo(),在a.c中#include b.h
D.在b.h中声明extern void foo(),在a.h中#include b.h
23.关变量,正确的说法是 (A,C,D)
A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度
B.若全局或全仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度
C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题
D.函教内部定义的自动变量过大,可能会导致堆栈溢出
24.坚持下列那些措施可以避免内存泄露 (A,B,C,D)
A.异常出口处检查内存、定时器/文件句柄/Socket/队列/信号量/GUI等资源是否全郜释放
B.删除结构指针时,必须从底层向上层顺序删除
C.使用指针数组时,确保在释放数组时,数组中的每个元素指针是否已经提前被释放了
D.避免重复分配内存
25.如果不使用常量,直接在程序中填写数字或字符串,将会有哪些麻烦(A,B,C)
A.程序的可读性(可理解性)变差。程序员自己会忘记那些数字或字符串是什么意思,维护人员则更加不知它们从何处来、表示什么
B.如果要修改数字或字符串,则会在很多地方改动,既麻烦又容易出错
C.在程序的很多地方轮入同样的数字威字符串,难保不发生书写错误
D.程序无法编译
26.对于防止头文件被重复包含,描术正在的有
A.多次包含一个头文件可以请过认真的设计来避免.如果不能做到这一点,就需要采取阻止头文件内容被包含多余一次的机制
B.防止重复包含的通常手段是:为每个来件配置1个宏,当头文件第一次被包含时就定义这个宏,并在头文件被再次包含时使用它以排除文件内容
C.保护宏的名称要唯一,不能重复
D.不应在防止重复包含的保护宏外部故置代码
27.下列定义中正确的是 (A,B,D)
A.char *a = "abcd ";
B.char a[] = “abcd”;
C.char a[4]= “abcd”;
D.char a[ ]= " ";
28.以下说法正确的有 (A,B,C,D)
A.在引用内存之前需要注意是否该内存已被成功初始化
B.在访问内存前需要注意该内存是杏有效,是杏已在其他地方被释放过C.放内存时需要注意是否是已释放过的内存,注意不要重复释放
D.释放内存时需要注意不要释放非动态申请的内存
29.关于全局变弹的注释,哪些描述是正确的 (A,B,D)
A.全局变量要有较详细的注释
B.对全局变量注释,可包括功能、.取值范围等
C.全局变量应该是本模块内使用的,用法自己自然是知遵的,可以不加注释
D.对全局变属注释需包括对该全局变量存取时的注意事琐等
30.以下哪些输入不可信输入 (A,B,C,D)
A.用户键盘输入
B.配置文件输入
C.环境变量
D.网络数值
31.对于整数溢出问题,下面说法正确的是 (A,B,C)
A.无符号整数运算时↓要保证结果不能出现反转
B.有符号整数运算时,要保证结果不能出现溢出
C. 整型转换时避免出现截断危险
D.整數溢出一 般只会导致逻辑错误,不会产生安全问题
32.编码时应该使用下面哪些函数来安全运行程序 (C,D)
A. popen()
B. system()
C. exec()系列函数
D. CreateProcess()
33.安全编程规范中,安全用途的随机数产生方式,推荐的有 (A,B,D)
A. Unix/Linux下采取建议读取/dev/random文件来获取真随机数 B.Windows推荐使用随机数生成函数CryptGenRandom0
C.其他平台可以使用srandom()+random0的方式
D.开源组件openssl或华为自主封装的iPSl组件
34.多线程、多进程设计中,什么情况下必须加锁保护 (A,C,D)
A访问全局变量
B.调用函数
C.访问静态变量
D.操作共享内存
35.存储在可复用资源中的敏感信息如果没有正确的清除则很有可能被低权限用户或者攻击者所获取和利用。因此敏惑信怠在可复用资源中保存应该讵循存储时问最短原则。可复用资源包括以下几个方面 (A,B,C,D)
A.堆( heap )
B.栈(stack )
C.数据段( data segment]
D.数据库的映射缓存
36.关于变量的内存分配区城及作用域的描述,以下说法正确的有 (B,D)
A.全局变量存放在内存的静态存储区域,在整个工程文件内部有效
B.静态全局变里存放在内存的静态存储区域,只在定义它的文件内有效
C.静态局部变量存放在内存的栈区,只在定义它的函数内存效,只是程序仅分配一次内存,函数返回后,该变量不会消失
D.局部变量存放在内存的栈区,在定义它的函数内有效,但是函数返回后失效
37.下列代码片断哪些不符合安全编码规范要求 (A)
A.unsigned int add(unsigned int a, unsigned int b) ( unsigned int sum; sum = a + b; return sum; }
B. int foo(char* str) ( size_t len = strlen(str); if (len > = MAX_BUF_SIZE - 1) { return -1; | char buf = (char)malloc(len+1); if (buf == NULL) ( return -1: } stncpy[(buf,str, len); buflen] =’ 10’ ;/"对 buf的其它处理*/ free(buf; buf = NULL;}
C.int file_ops(char* file_name] { FILE fp: fp = fopen(file_name, 'w"): if fp == NULL) ( return -1;}产其他处理*/ if (fclose(fp) ! = 0) { retun -1; } if (remove(file_name) != 0) ( return -1; ] return 0; }
D. int id_gen0 { enum flen = 12); char idlen]; int r, int num; r = rand0; num = snprintf(id, len “ID%-d”, ri;产生成1D其他处理}
38.文件I/O安全中,以下哪些是正确的做法 (A,B,C)
A.使用int类型来接收字符输入/输出函数的返回值
B.创建文件时,指定了合适的访问权限
C.文件名称使用白名单字符,杜绝出现"…/…"之类的目录跨越符号
D.访问时始终使用文件名代替文件描述符,以避免竞争条件
39.一条语句过长且不能拆分时,应该换行,换行时应参考的规则有 (A,D)
A.换行时,新行要增加─级缩进,使代码可读性更好
B.低优先级操作符处划分新行;换行时操作符不应该放下来,放在行尾
C.换行时应该按照单词换行,在最接近一行的边界处换行
D.换行是建议一个完整的语句放在一行,不要根据字符数断行
40.以下关于宏全编码的说法,王确的是 (A,B,D)
A无论是明文口令还是密文口令,都应该禁止保存在日志文件中
B.程序在运行时应该只分配能完成其任务的最小权限
C程序执行任务完毕时,应该尽快收回其权限,对于权限的撤销顺序可以不用考虑
D如果没有充分考虑字符串的“\0”结束符,则很可能会导致缓冲区溢出等安全漏洞
41.输入校验中如果没有特殊要求,应当首先考虑来用“白名单”校验形式
(√)
42.sizeof(“abcdef”)的输出值是7,strlen(" abcdef”)的输出值是6 (√)
43.内存释放后,把指针置为NULL;使用内存指针前进行津空判断 (√)
44.允许返回函数中定义的局部指针变量 (×)
45.所有的if … else if结构应该由else子句结束; switch语句必须有default分支 (√)
46.全局变量应增加“S_”前缀 (×)
47.通讯过程中使用的结构,必须注意字节序 (√)
48.使用strcpy会有缓冲区溢出的风险,使用strncpy就能避免该风险 (×)
49.抛出异常时,为了提高传递效率,应该抛出指针 (×)
50.可以用rand()函数产生用于安全用途的随机数 (×)
51.把整型表达式比较赋值为一种更大类型之前必须用这种更大类型对它进行求值 (√)
52.格式化输出函数的格式化参数和实参类型必须匹配 (√)
53.访问时始终使用文件名代替文件描述符,以避免竞争条件 (×)
54.对于局部使用的唯一含义的魔鬼数字,可以在代码用困增加说明注释,也可以定义局部const变量 (√)
56.可重入函数应避免使用共享变量;若需要使用,则必须通过互斥手段((关中断、信号量)对其加以保护 (√)
57.产品不能使用MD5算法对口令进行加密,原因是MD5算法是对称加密算法 (×)
58.对于字符数组或动态内存分配,可以不预留’\0’结束符,因为程序员知道该内存区大小,操作时以大小作为边界条件就不会出错 (×)
59.一个变量只有一个功能,不能把一个变量用作多种用途 (√)
60.位操作符(~、>>、<<、&、^、)应该只用于无符号整型操作数 (√)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。