c语言面试题-基本概念

第一部分:基本概念及其问答

1、关键字static的作用是什么?
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2)在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外
其他函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可以被这一模块内的其他函数调用。那就是,这个哈桑农户被限制在声明它的模块的本地范围内使用。

2、“引用”与指针的区别是什么?
1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元,即指真是一个实体;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。
2)可以有const指针,但是没有const引用;
3)指针可以有多级,但是引用只能是一级(int **p合法 而 int &&a不合法)
4)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
5)指针的值在初始化后可以改变,即指向其他的存储单元,而引用在进行初始化后就不会再改变了;
6)“sizeof引用”得到的是所指向的变量的大小,而“sizeof指针”得到的是指针本身的大小;
7)指针和引用的自增运算(++)意义不一样

3、.h头文件中的ifndef/define/endif 的作用?
防止头文件被重复引用

4、#include<file.h> 与#include "file. h"的区别?
前者是从Standard Library 的路径寻找和引用file.h,后者是从当前工作路径搜寻并引用file.h。

5、描述实时系统的基本特性
实时系统是指在系统工作时,能在特定的时间内完成特定的任务,其各种资源可以根据需要进行动态的分配,因此其处理事务的能力强,速度快。

6、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
全局变量:在函数外被定义的变量,自定义位置到结束都有效。
局部变量:在函数内部被定义的变量,只在本函数范围内有效。
全局变量一般定义时第一个字母大写。

7、什么是平衡二叉树?
每棵子树中的左子树和右子树的树深度差不能超过1;
二叉树中每一棵树都要求时平衡二叉树;

8、堆栈溢出一般是由什么原因导致的?
1.函数调用层次太深。函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归无法返回。再有,当函数调用层次过深时也可能导致栈无法容纳这些调用的返回地址而造成栈溢出。
2.动态申请空间使用之后没有释放。由于C语言中没有垃圾资源自动回收机制,因此,需要程序主动释放已经不再使用的动态地址空间。申请的动态空间使用的是堆空间,动态空间使用不会造成堆溢出。
3.数组访问越界。C语言没有提供数组下标越界检查,如果在程序中出现数组下标访问超出数组范围,在运行过程中可能会内存访问错误。
4.指针非法访问。指针保存了一个非法的地址,通过这样的指针访问所指向的地址时会产生内存访问错误。

9、冒泡排序算法的时间复杂度是什么?
冒泡排序法:遍历原始数据,从第一个数开始,到倒数第二个数结束,比较这个数和下一个数的大小,如果这个数比下一个数大,则交换这两个数。这样便可以将数据中最大的数转移到数组的最后。
之后再次遍历原始数据,但是变为从第一个数开始,到倒数第三个数结束,比较这个数和下一个数的大小,如果这个数比下一个数大,则交换这两个数。这样便可以将第二大的数转移到数组的倒数第二位。
重复执行上述过程,一直到从第一个数开始,到第二个数结束,从而完成了排序过程。
由于这个循环过程就像泡泡上浮的过程,所以被称为冒泡排序法。

10、什么函数不能声明为虚函数?
虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public,在基类的类定义中定义虚函数的一般形式:

virtual(虚拟) 函数返回值类型 虚函数名(形参表)  { 函数体 }
1:只有类的成员函数才能说明为虚函数;
2:静态成员函数不能是虚函数;
3:内联函数不能为虚函数;
4:构造函数不能是虚函数;
5:析构函数可以是虚函数,而且通常声明为虚函数。

11、队列和栈有什么区别?
队列(Queue):是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
栈(Stack):是限定只能在表的一端进行插入和删除操作的线性表。
1)操作的名称不同。
队列的插入称入队,队列的删除成为出队。栈的插入为进栈,栈的删除为出栈。
2)操作的限定不同
队列是队尾入队,队头出队。栈的进栈和出栈都是在栈顶进行的,无法对栈底进行操作。
3)操作规则不同。
队列先进先出,栈先进后出。
4)遍历速度不同
队列是基于地址指针进行遍历,而且可以从头部或尾部进行遍历,但不能同时遍历,无需开辟空间,因为在遍历的过程中不影响数据结构,所以遍历速度快。栈只能从顶部取数据,也就是说最先进入栈底的,需要遍历整个栈才能取出来,而且在遍历数据时需要为数据开辟临时空间,保持数据在遍历前的一致性。

12、不能做switch()的参数类型
switch语句后的控制表达式只能是short,char,int,long,整数类型和枚举类型,不能float,double和boolean类型。String类型是java7开始支持。

13、局部变量能否和全局变量重名?
能,局部会屏蔽全局要使用全局变量,需要使用“::”;
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量比如在两个循环体内都定义一个同名的局部变量,而那个全局变量的作用域就在那个循环体内。

14、如何引用一个已经定义过的全局变量?
extern(外部的)
可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

15、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
可以,在不同的C文件中以static形式来声明同名全局变量。   可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错

16、语句for( ; 1 ; )有什么问题?它是什么意思?
和while(1)相同 循环条件永远为真,不停地进行循环操作,除非循环体中有强制退出循环的语句才能终止循环。

17、do… while和while…do有什么区别?
前者先循环一遍再判断,后者先判断再循环

18、static全局变量与普通的全局变量有什么区别? static 局部变量和普通局部变量有什么区别? static 函数与普通函数有什么区别?
全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。
  从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
  static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
  static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;   static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;   static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

19、简述程序的内存分配
栈区(stack):由编译器自动分配与释放,存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。
堆区(heap):一般由程序员自动分配,如果程序员没有释放,程序结束时可能有OS回收。其分配类似于链表。
全局区(静态区static):存放全局变量、静态数据、常量。程序结束后由系统释放。全局区分为已初始化全局区(data)和未初始化全局区(bss)。
常量区(文字常量区):存放常量字符串,程序结束后有系统释放。
代码区:存放函数体(类成员函数和全局区)的二进制代码。

20、解释堆和栈的区别
管理方式不同:栈是由编译器自动申请和释放空间,堆是需要程序员手动申请和释放;
空间大小不同:栈的空间是有限的,在32位平台下,VC6下默认为1M,堆最大可以到4G;
能否产生碎片:栈和数据结构中的栈原理相同,在弹出一个元素之前,上一个已经弹出了,不会产生碎片,如果不停地调用malloc、free对造成内存碎片很多;
生长方向不同:堆生长方向是向上的,也就是向着内存地址增加的方向,栈刚好相反,向着内存减小的方向生长。
分配方式不同:堆都是动态分配的,没有静态分配的堆。栈有静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 malloc 函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无需我们手工实现。
分配效率不同:栈的效率比堆高很多。栈是机器系统提供的数据结构,计算机在底层提供栈的支持,分配专门的寄存器来存放栈的地址,压栈出栈都有相应的指令,因此比较快。堆是由库函数提供的,机制很复杂,库函数会按照一定的算法进行搜索内存,因此比较慢。

21、什么是预编译,何时需要预编译?
预编译又称为预处理 , 是做些代码文本的替换工作。
处理以# 开头的指令 , 比如拷贝 #include 包含的文件代码,#define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段。

主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。

C 编译系统在对程序进行通常的编译之前,首先进行预处理。
c提供的预处理功能主要有以下三种:
1)宏定义
2)文件包括
3)条件编译
何时需要预编译:
总是使用不经常改动的大型代码体。
程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含的文件预编译为一个“预编头”

22、关键字const是什么含意?
const意味着“只读”

23、关键字volatile 有什么含意并给出三个不同的例子。
volatile本意是“易变的”
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量

24、三种基本的数据模型
层次模型,网状模型,关系模型。

25、结构与联合有何区别?

  1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻,
    联合中只存放了一个被选中的成员, 而结构的所有成员都存在。
    2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存
    在了, 而对于结构的不同成员赋值是互不影响的。

26、描述内存分配方式以及它们的区别?

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

27、请说出const 与#define 相比, 有何优点?
用 #define 定义一个标识符来表示一个常量。其特点是:定义的标识符不占内存,只是一个临时的符号,预编译后这个符号就不存在了。
(1)就起作用的阶段而言: #define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用。
(2)就起作用的方式而言: #define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
(3)就存储方式而言:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
(4)从代码调试的方便程度而言: const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉了
const优点:
(1)const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
(2)有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
(3)const可节省空间,避免不必要的内存分配,提高效率

28、简述指针与数组的区别?
数组:数组是用于存储多个相同类型数据的集合。
1.赋值
指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其他变量在内存中的地址。
同类型指针变量可以相互赋值,数组不行,智能一个一个元素的拷贝或赋值。
2.存储方式
数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下标进行访问的,多维数组在内存中是按照一维数组存储的,只是逻辑上是多维的。
数组的存储空间不是在静态区就是在栈上。
指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。
指针:由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。
3.求sizeof
数组:
数组所占存储空间的内存:sizeof(数组名)
数组的大小:sizeof(数组名)/sizeof(数据类型)
指针:
在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8.

29、分别写出BOOL, int, float, 指针类型的变量a与“零”的比较语句

`BOOL  : if ( !a ) or if(a)
 
int   : if ( a == 0)
 
float : const EXPRESSION EXP = 0.000001
         if ( a < EXP&& a >-EXP)
 
pointer : if ( a != NULL) or if(a == NULL)

30、如何判断段程序是由C编译程序还是由 C++编译程序编译的?
如果是要你的代码在编译时发现编译器类型,就判断_cplusplus或_STDC_宏,通常许多编译器还有其他编译标志宏

31、论述含参数的宏与函数的优缺点
1.函数调用时,先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。

2.函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开则是在编译时进行的,在展开时并不分配内存单元,不进行值的传递处理,也没有“返回值”的概念。

3.对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。

4.调用函数只可得到一个返回值,而用宏调用可以设法得到几个结果。

32、用两个栈实现一个队列的功能?要求给出算法和思路!
堆栈的特征:FILO 先进后出
队列的特征:FIFO 先进先出

所以,用两个栈s1和s2模拟一个队列时,s1作输入栈,逐个元素压栈,以此模拟队列元素的入队。

当需要出队时,将栈s1退栈并逐个压入栈s2中,s1中最先入栈的元素,在s2中处于栈顶。

s2退栈,相当于队列的出队,实现了先进先出。

显然,只有栈s2为空且s1也为空,才算是队列空。

33、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
while(1),for( ; 1 😉

34、位操作(Bit manipulation)
位操作是程序设计中对位模式按位或二进制数的一元和二元操作。

35、访问固定的内存位置(Accessing fixed memory locations)
嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:

36、什么是中断(Interru pts)
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

37、动态内存分配(Dynamic memory allocation)
动态内存是相对静态内存而言的。所谓动态和静态就是指内存的分配方式。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存。
前面所写的程序大多数都是在栈上分配的,比如局部变量、形参、函数调用等。栈上分配的内存是由系统分配和释放的,空间有限,在复合语句或函数运行结束后就会被系统自动释放。而堆上分配的内存是由程序员通过编程自己手动分配和释放的,空间很大,存储自由。

38、请简述typedef的意义
typedef关键字就是给这些数据类型起一个别的名字
39、用变量a给出下面的定义
a)一个整型数( An integer) int a;
b)一个指向整形数的的指针(A pointer to an integer)int *a;
c) 一个指向指针的的指针,它指向的指针是指向一个整形数(A pointer to a pointer to an integer) int **a;
d)一个有10个整型数的数组 (An array of 10 integers) int a[10];
e)一个有10个指针的数组,该指针指向一个整形数的(An array of 10 pointers to integers) int *a[10];
f)一个指向有10个整形数数组的指针(A pointer to an array of 10 integers)int (*a)[10];
g)一个指向函数的指针, 该函数有一个整型参数并返回一个整型数 int (*a)(int);
(A pointer to a function that takes an integer as an argument and returns an integer)
h)一个有10个指针的数组,该指针指向一个函数, 该函数有一个整型参数并返回一个整型数 (An array of ten poiners to functions that take an integer argument and return an
integer) int (*a[10])(int);

40、写一个“标准”宏,这个宏输入两个参数并返回较小的一个

#define min(a,b)((a)<=(b)?(a):(b))

41、A.c和B.c两个c文件中使用了两个相同名字的static 变量,编译的时候会不会有问题?这两个static 变量会保存到哪里(栈还是堆或者其他的) ?
不会
static只能作用在定义它的源文件中,这两个static变量会保存到静态存储区
42、一个单向链表,不知道头节点,一个指针指向其中的一个节点, 问如何刪除这个指针指向的节点?
既然要删除的节点不知道其前节点,那么就把待删除的节点和它的后节点交的数据进行交换,这样待删除的节点就有了前节点。

就可以进行删除,再进行连接。

所以设指向待删除节点的指针为p,设其后节点为q;

q=p->next;

//数据交换

temp = q->data;

q->data = p->data;

p->data = temp;

p->next = q->next;//重新建立连接

free(q);

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值