您的位置:首页 > 编程语言 > C语言/C++

深入学习C语言知识点checklist

2014-09-19 00:13 218 查看
深入学习C语言知识点checklist——测试你掌握C的程度的答案

    前些日子在网上转载了一篇日志《关于深入学习C语言知识点checklist——测试你掌握C的程度》,这篇文章的引用真是无所不在,但找不到关于它的答案。于是突发奇想,用了两天时间整理了篇答案。全是自己的见解,希望对大家的学习有帮助。

一.字符串

(1)strlen()函数的返回值是什么类型的? 

    strlen()函数的返回类型是unsigned int,在此需要注意unsigned int的一些操作。两个unsigned int的操作结果还为unsigned int,unsigned int和int的操作结果会自动转换为int。所以如果使用if(strlen(a) - strlen() < 0)这样的判断语句,结果永远为真。

(2)字符串strlen()的值,是否和他占据的内存空间相同? 

    strlen()函数得到的长度是从字符串始地址开始到空字符的字节数,并不包括最后的空字符。

(3)你是否知道strcpy函数存在的潜在风险?如何避免? 

    strcpy(char *a, char *b)函数将b字符串整个复制到从a开始的地址处,复制后字符串a和b完全一样。如果b的长度大于a初始长度,就会将a后面的内存部分覆盖掉。标准C这样设计库函数体现了C语言的灵活性,这需要程序员自己设计安全的程序。要想避免溢出,必须确保b的长度不大于a的初始长度。或者给a重新分配更大长度的内存地址,将b复制进去。C中有限制复制长度的标准函数strcpy(char *a, char *b, size_t len),这个函数只从b中复制len个字符到a,但a的结果将不是以NUL字节结尾。如果使用这个函数,最后必须自己添加代码,将适当内存写为‘\0’,一般a[len - 1] = '\0';

(4)如果一个字符串没有字符串结束符,而调用str开头的库函数,会发生什么? 

    如果一个字符串没有字符串结束符,而调用str开头的库函数,返回的结果是从初始地址开始到它之后为'\0'的内存处为止中间的字节数。

(5)Strcpy(),strcat(),strcmp(),strncpy(),strncat(),strncmp()内部到底是如何运行的?这些函数到底对源字符串和目标字符串做了些什么?你是否观察过它们运行时两个字符串内存的变化?

    strcpy(char *a, char *b)函数将b中的字符串复制到字符串a的起始地址处,将a原来的字符串覆盖,以'\0'结尾。

    strcat(char *a, char *b)函数从字符串a的下一个地址(即从‘\0’开始)为初始地址,将b复制到此处,以'\0'结尾。如果a和b重叠,结果不可预测。

    strcmp(char const *a, char const *a),按照字典排序的顺序,如果a小于b,返回小于零的值;如果a大于b,返回大于零的值。如果两字符串相等,返回0。

    strncpy,strncat和strncmp分别比strcpy,strcat,strcmp多了一个参数size_t len。和strcpy一样,strncpy把源字符串的字符复制到目标数组。然而它总是正好向a写入len个字符。如果strlen(b)的值小于len,a数组就用额外的nul字节填充到len长度。如果strlen(b)大于len,那么只有len个字符被复制到a中。注意!它的结果不会以nul字节结尾。

    strncat和strcat类似,不过它只会复制len个字符添加到a的后面。和strncpy不同的是,它以nul作为结尾。strncat()最多向目标数组复制len个字符(再加一个结尾的nul字节),它才不管目标a除去原来存在的字符串后留下的空间够不够。

    strncmp()最多只比较前len个字符,功能和strcmp()类似。

    具体实现见标准函数库,个人觉得实现没有难度,很容易实现。

(6)上面这些函数使用时,各有哪些需要注意的地方?你会几种字符串查找操作?c语言中有字符串这个数据类型吗?对字符串进行操作的时候,是否为字符串结尾符预留存储位置不然的话容易造成非法访问内存。

    上面几个函数需要注意的地方都已指出。

    个人觉得这些字符串操作都要熟悉!

    其实char *就相当于字符串类型,string.h中的所有库函数几乎都是针对字符指针操作的。

    答案是:是!字符串必须以'\0'结尾,才能识别到结束,否则会一直向下搜索。在字符串声明 操作中都必须预留结尾符的存储位置。

二.数组

(1)你知道几种数组初始化的方法?

    数组可以在声明时静态初始化,也可以在以后动态赋值,数组初始化前是不可以使用数组值的。 

(2)数组和指针有千丝万缕的联系而又不同,你是否对他们在不同情况下的使用进行过详细的总结?

    这个答案在我的另一篇日志“C数组和指针的区别---自己总结的”里有详细介绍。

(3)“int calendar[10][20];”,这是一个什么样的数组?它拥有10数组类型的元素,还是20个?

    这个很显然是二位数组,它包含十个数组类型元素。

(4)“int a[10];”,数组名a在本质上是一个什么?你是否打印过a的值?

    数组名a相当于一个指针,指向数组a的第一个元素。a作为右值,是该数组第一个元素的地址。

(5)你知道几种获取数组某元素的方法?

    只要知道了该元素的数据类型和地址,就可以很容易的获取该元素。例a
,*(a + (m * n))......

(6)指针和数组相同吗?什么时候相同?什么时候不同?

    上面第二个问题中已回答。需要注意的是,声明数组时会给该数组分配相应的内存空间,而声明指针时只分配一个4字节的内存空间保存指针值。

(7)用指针和下标访问数组元素,那种方式更快?

    指针表达式可能比下表表达式效率更高,但下标表达式绝不可能比指针表达式效率更高!(C和指针)

三.结构体

(1)你知道什么是位域结构体吗?如何定义它?如何使用它?

    位域结构体和一般的结构体类似,不过它的成员是一个或多个的位段,这些不同长度的位段实际上存储于一个或多个整形变量中。

    定义:位段的声明和普通结构成员声明相同。但有两个例外,首先,位段必须声明为int,unsigned int活signed int类型;其次,在成员名的后面是一个冒号和一个整数,整数指定位段占用的位的书目。

    使用:保存在整形变量中,可以直接对这些整形变量进行位操作。

(2)你知道字节对齐对结构体占用内存空间大小的影响吗?如何计算结构体占用内存的大小?

    编译器按照成员列表的顺序一个接一个的给每个成员分配内存,每个成员都满足边界对齐,他们之间可能存在额外的内存空间。

    如果某个机器的整形长度为4字节,并且该结构的起始存储位置能够被4整除,那么所有成员的起始位置必须被4整除!我们可以按照这个原则给结构体分配内存空间,也很容易计算出它实际占有的内存空间。

四.宏

(1)你知道宏的本质是什么吗?函数?语句?类型定义?或者其他?

    #define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)。宏就是宏,不要将它理解为一个函数,语句或类型定义或者任何已存在的C语法,因为宏有自己的语法规定。

(2)你知道语言设计者为什么设计宏吗?这些原因目前是否仍然成立?

    设计宏的原因:(1)C文件中替换方便;(2)替代函数调用,避免函数调用时的巨大开销。现在这些原因仍然成立。

(3)你会设计带有参数的宏吗?

    设计带参数的宏时须注意两点:(1)给参数带上括号,避免替换后混乱;(2)谨慎使用具有副作用的参数。

(4)你知道使用宏的参数的的时候的注意事项吗?

    见第三个问题。

(5)你会设计带有可变参数的宏吗?

    见第三个问题。

(6)你知道使用宏有什么劣势吗?

    宏的劣势:(1)很容易产生非作者本意的表达式(例如因为缺少括号);(2)参数如果带有副作用,往往产生错误结果;(3)使用宏的另一个危险是宏展开后可能产生非常庞大的表达式,占用的空间远远超过了预期。

你有没有更好的替代方案?

五.枚举

(1)是否可以指定枚举中各项的值?

    可以显式初始化。

(2)如果不指定值,枚举的第一个值是多少?

    从0开始,递增!

(3)枚举的值是否可以是负数?

    枚举的值的类型是int,只要在int范围内都正确!

(3)定义枚举的时候,你是否专门定义了枚举的最小值和最大值?

    ??定义这个有用吗。

六.Switch

(1)switch(c)中的c的数据类型有哪些?

    数据类型必须为整形!

(2)你是否在所有的switch中都加了default语句?

    在所有switch中都加入default是个很好的习惯,即使能保证数据类型不外乎之前的分支,最后也要加上default。

(3)是否在所有的case中都加了break语句(一般情况的做法)?如果你不加break,将会发生什么?

    如果不加break,将会一直判断下去。所有的语句都执行直到switch结束。

七.Static

(1)static的三个主要作用是什么?

Static三个主要作用:(1)当它用于函数定义时,或用于代码块之外的变量声明时,static关键字用于修改标识符的连接属性,从external改为internal,但标识符的存储类型和作用域不受影响。用这种方式声明的函数或变量只能在声明它们的源文件中访问;(2)当static作用于代码块内部的变量声明时,static关键字用于修改变量的存储类型,从自动变量修改为静态变量,存储在静态存储区域!但变量的连接属性和作用域不受影响。(3)用这种方式声明的变量在程序执行前辈创建,并默认初始化为0,并在程序的执行期间一直存在。

(2)static的修饰的局部变量是保存在什么地方的?全局变量那?

    被static修饰的局部变量成为静态变量,保存在静态存储区域!

(3)static修饰的全局变量和函数,在其他的文件中是否可以访问?如何访问?

    不可以访问,避免了命名冲突。如果需要使用,可以显式extern该变量,函数。

(4)你知道static是c语言中实现封装和隐藏的利器吗?你是否经常使用?

    第一个问题已回答。

(5)定义在不同源文件中的static全局变量,编译器是否允许他们的变量名称相同?他们在内存中的地址是否相同?函数那?

定义static全局变量可以避免命名冲突。

属于不同的源文件,彼此并不冲突,地址当然不同

八.Const

(1)你是否经常使用const来表明不能够被更改的变量?

    是,effective C++中的一个规则。

(2)你是否经常使用const常量来代替宏?

    我一直不明白多人为什么总用宏。使用宏后可读性变差,更容易出错,虽然程序运行速度可能稍微快点,我觉得得不偿失。记得一位老师说过,如果你想让你的代码看起来“牛”点,就用宏吧,呵呵~

(3)下面四种情况,你知道是各表示什么意思吗?

int i_value = 10;

const int* pvalue = &i_value

int const *pvalue = &i_value

const int* const pvalue = &i_value

你知道const常量如何初始化吗?

Const int * pvalue声明一个int指针pvalue,它指向的内容是常量。

Int cosnt * pvalue声明一个int指针,该指针指向的内容为常量。

int * const pvalue声明一个int指针,该指针为常量。

Cosnt int * const pvalue声明一个int指针,该指针为常量,它指向的内容也为常量。

九.Sizeof

(1)对于字符数组,strlen和sizeof的值是否相同?

    Strlen(cosnt char *a)函数从a的起始地址开始到第一个’\0’结束的字节数;而sizeof()返回的则是字符数组实际分配的内存空间的字节大小。

(2)Sizeof本质上是函数还是宏?

    两点就彻底明白它:1,它是个运算符;2,编译器具体实现的时候,大多数是个系统宏。

(3)Sizeof的返回值是什么类型?

    是C的size_t类型,size_t在宏定义中定义为unsigned int。

十.指针

(1)“int *p;”&p,p,*p他们的值分别表示什么含义?

    这个有点简单,看课本。

(2)你定义的指针初始化了没?

    指针必须初始化后再用,否则指向不可预计的内容。

(3)你理解指针的指针的概念吗?你会使用吗?

    自己看书。。。。。。

(4)“int *pi_value; pi_value = 0x100000;” pi_value + 1的值是是多少?

    指针的加减根据它所指向的内容所占用的字节计算。

(5)你会定义函数指针吗?你会使用函数指针调用函数吗?

    见书。在系统编程中,函数指针非常有用,使用很普遍。系统编程我还不太熟悉,下一步打算好好学习这方面。

(6)关于指针和数组的区别和联系?

请参见知识点数组。

十一. 动态分配内存

(1)动态分配的内存是保存在什么地方的?

    动态分配的内存在用户内存空间的内存池中。

(2)什么情况下使用动态分配内存?

    当需要使用的内存空间在编译时才知道时,就要动态分配内存。

(3)动态申请内存一定要释放,否则会内存泄露。你是否使用过内存检测工具?

    在VC++ 6.0中可以通过Debug模式和CRT库来方便的检测程序中的内存泄漏,具体可以参见在线文档,文档的URL:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvc60/html/memleaks.asp。

十二. 函数

(1) 如何查看函数在内存中的地址?

    提取函数的地址。

(2) 如何给一个函数指针赋值?

见函数指针的用法。

(3)你是否会定义可变入参函数

可变参数列表是通过宏来实现的,这些宏定义于stdarg.h头文件,是标准库的一部分。这个头文件声明了一个类型va_list和三个宏---va_start,va_arg和va_end。我们可以声明一个类型为va_list的变量,与这几个宏配合使用,访问参数的值。

(4)你是否可以区分函数的形参与实参?

    ……这个问题比较弱智

(5)如何定义函数名,以准确的表达函数的用途?

    养成一个好的编程风格很重要,参见林锐《高质量C++/C编程指南》第三章---命名规则。

(6)你是否使用const来修饰函数入参和返回值,以表达特定的含义?

    Yes,如果传入的参数或者返回值逻辑上不需要更改,最好用const来修饰,让编译器来保证它不被修改。

(7)递归如何使用?

    ……

十三. 变量

(1) 全局变量,局部变量,常量分别保存在内存中的什么地方?

      全局变量和常量都保存在静态存储区,局部变量保存在运行时堆栈中。

(2) 不同类型的变量,你是否知道其作用域?

      全局变量在整个程序中有效,局部变量在生命它的函数或代码块之内有效。一般,改变变量的存储区域不能改变它们的作用域。

(3) 全局变量和局部变量是否可以重名?你是否在这样做?

      可以重名。会产生覆盖。

(4) 局部变量在函数退出后是否有效,为什么?

      在函数退出后会被销毁,如果声明static类型,函数退出仍存在,但仍只能在函数内部使用。

(5) 全局变量为什么不允许定义在头文件中?有何危害?

      注意头文件中不可以放变量的定义!!!一般情况下头文件中只放变量的声明,因为头文件要被其他文件包含(即#include),如果把定义放到头文件的话,就可能多次定义变量,C++不允许多次定义变量,一个程序中对指定变量的定义只有一次,声明可以无数次。 不过有三个例外,一下三中实体的定义也可放到头文件中。 1.值在编译时就已知的const 变量的定义可以放到头文件中    如:const int num(10);
2.类的定义可以放到头文件中 3.inline 函数 这三个实体可以定义在多个源文件中,只要在每个源文件中的定义相同。  

                                                            参考------c++ primer 

      

十四. 链接(linux)

(1)链接位于编译过程的那个阶段?

    链接位于编译后执行,将编译后的各个文件以及标准函数库链接起来。

(2)动态链接库和静态链接库使用时有何区别?

    静态库在程序运行时就会装入内存,而动态库在调用的时候才装入!

(3)如何对动态链接库进行动态加载(不用重启程序而加载链接库)?

    在C中加载和卸载DLL是一件很容易的事,LoadLibrary和FreeLibrary让你能够轻易的在程序中加载DLL,然后在任何地方卸载。

(4)动态链接有何优点?

    前面已说过。

(5)动态链接库中是否定义了非static的全局变量?你是否知道这是一个非常危险的动作?

    可能产生命名冲突。

(6)动态库中的全局变量(非static)和函数(非static)是否可以和上层全局变量和函数重名?重名后会发生什么事情?

    不可以重名,重复定义产生错误。

十五. 运行时的数据结构(linux)

(1)你知道什么是段的概念吗?

    段是二进制文件中简单的区域,里面保存了和某种特定类型(如符号表条目)相关的所有信息。一个段一般包含几个section。

(2)可执行程序可以分为几个段?每个段保存的是什么内容?

    可以分为5段。前两段包含该文件的一些属性数据等内容,bss段保存未经初始化的数据,数据段保存经过初始化的数据,文本段保存可执行文件的指令。

(3)如何查看可执行程序各个段的大小?

    这个问题。。。。。。望牛人给予解答。我再慢慢想想。

(4)当函数被调用时发生了什么?

    堆栈中的一些操作,状态保存。

(5)你有没有试过程序的栈空间最大有多大?程序超过此大小会发生什么?

    对于一个线程,系统默认的栈空间是8MB。有堆栈保护程序,应该可以避免发生访问错误。

(6)你使用的系统的栈是向下生长的,还是向上生长的?

    一般的编译环境都是向下生长。

十六. Include

(1)如何避免对同一头文件的多次include?

    可用#ifdef、#ifndef 

十七. 声明

(1)什么是声明,什么是定义?

    略过

(2)你是否会运用c语言声明的优先级规则?

C/C++
经典问题系列

http://www.360doc.com/myfiles.aspx?reg=1&app=1&type=3
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: