C语言面试基础知识整理

时间:2021-05-05 03:39:31

一、预处理

1、什么是预编译?何时需要预编译?

(1)预编译又称预处理,是做些代码文本的替换工作,即程序执行前的一些预处理工作。主要处理#开头的指令,如拷贝#include包含的文件代码、替换#define定义的宏、条件编译#if等。

(2)何时需要预编译:

a.总是使用不经常改动的大型代码体;

b.程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。

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

#define MIN(x, y) ((x)<(y)?(x):(y))  //注意结尾没有;C语言面试知识

3、#与##的作用?

#是把宏参数转化为字符串的运算符,##是把两个宏参数连接的运算符。

例如:

#define STR(arg) #arg          //则宏STR(hello)展开时为”hello”

#define NAME(y) name_y      //则宏NAME(1)展开时仍为name_y

#define NAME(y) name_##y    //则宏NAME(1)展开为name_1

#define DECLARE(name, type) typename##_##type##_type,

//则宏DECLARE(val, int)展开为int val_int_type

4、如何避免头文件被重复包含?

例如,为避免头文件my_head.h被重复包含,可在其中使用条件编译

#ifndef _MY_HEAD_H

#define _MY_HEAD_H    /*空宏*/

/*其他语句*/

#endif

5、#include<file.h> 与 #include "file.h"的区别?

前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

二、关键字

1、register:

(1)register关键字的作用:

请求CPU尽可能让变量的值保存在CPU内部的寄存器中,减去CPU从内存中抓取数据的时间,提高程序运行效率。

(2)register作用的实现原理:

扩展:CPU组成,计算机系统组成,数据处理流程 。

(3)什么时候使用register?

一般,我们将频繁被访问的变量,用register修饰

(因为CPU内存资源是有限的,是稀缺的,不可能将所有变量都声明为register变量)

(4)使用register关键字应注意什么?

a.只有局部变量才可以被声明用register修饰;

(register不能修饰全局变量和函数的原因:全局变量可能被多个进程访问,而用register修饰的变量,只能被当前进程访问)

b.不能用取地址获取用register修饰的变量的地址;

(原因:变量保存在寄存器中,而取地址获取的地址的是内存的地址)

c.用register修饰的变量一定要是CPU所接受的数据类型。

2、static:

(1)static关键字的作用:

static既可以修饰变量,也可以修饰函数,修饰变量时,既可以修饰局部,也可修饰全局

static修饰静态局部变量,延长变量的生命周期,直至程序结束,这个变量才释放;

static修饰全局变量,使其只可在本文件可访问,其他文件不可见;

(static修饰的变量都保存在数据段静态数据区中,未初始化时,系统将默认初始化为0)

static修饰函数,使其只可在本文件可调用,其他文件不可调用;

(2)什么时候使用static修饰变量?

当希望一个变量直至程序结束才释放时,用static修饰静态局部变量;

当希望一个全局变量只可在本文件可访问,其他文件不可见时,用Static修饰全局变量;

当希望一个函数只可在本文件可调用,其他文件不可调用时,用Static修饰函数;

3、extern:

(1)extern关键字的作用:

extern用来外部声明一个全局变量,这个全局变量在另一个文件中被定义。

(2)使用extern关键字应注意什么?

标明数据类型,例:extern int count;

(3)什么时候使用extern修饰变量?

在a.c文件中想使用b.c文件中的全局变量时,用extern 外部声明。

(4)用于extern  "C"

当文件为CPP文件时,通过extern “C”告诉C++编译器,extern “C”{}里包含的函数都用C的方式来编译:

a.可以是单一语句;

extern "C" double sqrt(double);

b.可以是复合语句, 相当于复合语句中的声明都加了extern "C";

extern "C"

{

double sqrt(double);

int min(int, int);

}

c.可以包含头文件,相当于头文件中的声明都加了extern "C";

extern "C"

{

#include <cmath>

}

d.不可以将extern "C" 添加在函数内部;

e.如果函数有多个声明,可以都加extern "C", 也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则;

f.除extern "C", 还有extern "FORTRAN" 等。

4、const:

(1)const关键字的作用:

const修饰变量,是这个变量变成只读变量,变量对应的空间的值是可变的,但不能用变量名来修改空间中的值。

(2)使用const关键字应注意什么?

使用const关键字修饰的变量,一定要对变量进行初始化

int *const p = &num;   p++ ✘

const int *p = &num;   (*p)++ ✘

int const *p = &num;   (*p)++ ✘

(离谁进,谁就不可以改变)

(3)什么时候使用const修饰变量?

a.声明常变量,使得指定的变量不能被修改;

const int a = 5;/*a的值一直为5,不能被改变*/

const int b; b = 10;/*b的值被赋值为10后,不能被改变*/

const int *ptr; /*ptr为指向整型常量的指针,ptr的值可以修改,但不能修改其所指向的值*/

int *const ptr;/*ptr为指向整型的常量指针,ptr的值不能修改,但可以修改其所指向的值*/

const int *const ptr;/*ptr为指向整型常量的常量指针,ptr及其指向的值都不能修改*/

b.修饰函数形参,使得形参在函数内不能被修改,表示输入参数;

如int fun(const int a);或int fun(const char *str);

c.修饰函数返回值,使得函数的返回值不能被修改。

const char *getstr(void);使用:const *str= getstr();

const int getint(void);  使用:const int a =getint();

(4)const与#define 相比,有何优点?

(1)const作用:定义常量、修饰函数参数、修饰函数返回值

被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

(2)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

(3)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

5、typedef:

(1)typedef关键字的作用:

声明一个已经存在的数据类型的同义字,给数据类型定义一个新名字,提高了移植性;简化复杂的类型声明,提高编码效率;解释数据类型的作用。

6、voliate:

(1)voliate关键字的作用:

volatile指定的变量可能被系统、硬件、进程/线程改变,强制编译器每次从内存中取得该变量的值,而不是从被优化后的寄存器中读取

简单来说,就是自己所定义的变量在程序运行过程中一直会变,如果希望这个值被正确处理,就需要每次从内存中去读这个值,这样就不会有错误了。

(2)什么情况下用volatile关键字?

a.中断服务程序中修改的供其他程序检测的变量需要加volatile;

b.多任务环境下各任务间共享的标志应该加volatile;

c.存储器映射的硬件寄存器通常也要加volatile说明,因此每次对它的读写都可能有不同意义。

(3)一个参数既可以是const还可以是volatile吗?

可以,例如只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

(4)一个指针可以是volatile 吗?

可以,尽管这并不常见。例如当一个中服务子程序修该一个指向buffer的指针时。

7、inline:

(1)inline关键字的作用:

内联inline是给编译器的优化提示,如果一个函数被编译成inline的话,那么就会把函数里面的代码直接插入到调用这个函数的地方,而不是用调用函数的形式。

(2)使用inline关键字应注意什么?

内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

8、sizeof:

(1)sizeof关键字的作用:

sizeof是在编译阶段处理,且不能被编译为机器码。sizeof的结果等于对象或类型所占的内存字节数。sizeof的返回值类型为size_t。

(2)sizeof的取值:

变量:int a;  sizeof(a)为4;

指针:int *p;  sizeof(p)为4;

数组:int b[10]; sizeof(b)为数组的大小,4*10;int c[0]; sizeof(c)等于0;

结构体:struct (int a; char ch;)s1; sizeof(s1)为8 ,因为结构体字节需要对齐。

注意:不能对结构体中的位域成员使用sizeof

sizeof(void)等于1

sizeof(void *)等于4

三、变量

1、全局变量和局部变量在内存中的区别?

全局变量储存在静态数据区,局部变量在堆栈中

2、局部变量能否和全局变量重名?

能,局部会屏蔽全局。要用全局变量,需要使用"::"。

局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内

3、如何引用一个已经定义过的全局变量?

可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

4、全局变量能否定义在被多个.C文件包含的头文件中?

可以,在不同的C文件中以static形式来声明同名全局变量

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

三、结构体

1、结构体的赋值?

C语言中对结构体变量的赋值或者在初始化或者在定义后按字段赋值

方式1:初始化

struct tag

{

char a;

int b;

}x = {‘A’, 1};/*初始化*/

struct tag

{

char a;

int b;

};

struct tag x = {‘A’,1};/*在定义变量时初始化*/

方式2:定义变量后按字段赋值

struct tag

{

char a;

int b;

};

struct tag x;/*定义变量*/

x.a =‘A’;/*按字段赋值*/

x.b = 1; /*按字段赋值*/

此时使用初始化的方式来赋值时,如x = {‘A’,1};则出错。

方式3:结构变量间的赋值

struct tag

{

chara;

int b;

};

struct tag x,y;

x.a=’A’;

x.b=1;

y = x;/*结构变量间直接赋值*/

2、结构体变量如何比较?

虽然结构体变量之间可以通过=直接赋值,但不能通过比较符(==)来比较,因为比较符只作用于基本数据类型。这个时候,只能通过int memcmp(const void *s1, const void *s2, size_t n);来进行内存上的比较。

3、结构体位域

(1)位域的定义:

位域是一个或多个位的字段,不同长度的字段(如声明为unsigned int类型)存储于一个或多个其所声明类型的变量中(如整型变量中)。

(2)位域的类型:

可以是char、short、int,多数使用int,使用时最好带上signed或unsigned。

(3)位域的特点:

字段可以不命名,如unsigned int :1;可用来填充;unsigned int :0; 0宽度用来强制在下一个整型(因此处是unsigned int类型)边界上对齐。

(4)位域的使用:

struct st1

{

unsigned char a:7;/*字段a占用了一个字节的7个bit*/

unsigned char b:2;/*字段b占用了2个bit*/

unsigned char c:7;/*字段c占用了7个bit*/

}s1;

sizeof(s1)等于3。因为一个位域字段必须存储在其位域类型的一个单元所占空间中,不能横跨两个该位域类型的单元。也就是说,当某个位域字段正处于两个该位域类型的单元中间时,只使用第二个单元,第一个单元剩余的bit位置补0。

struct st2

{

unsigned int a:31;

unsigned int b:2;/*前一个整型变量只剩下1个bit,容不下2个bit,所以只能存放在下一个整型变量*/

unsigned int c:31;

}s2;

于是可知sizeof(s2)等于3*sizeof(int)即12。

(5)位域的好处:

a.有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。这样节省存储空间,而且处理简便。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

b.可以很方便的利用位域把一个变量给按位分解。比如只需要4个大小在0到3的随机数,就可以只rand()一次,然后每个位域取2个二进制位即可,省时省空间。

(6)位域的缺点:

不同系统对位域的处理可能有不同的结果,如位段成员在内存中是从左向右分配的还是从右向左分配的,所以位域的使用不利于程序的可移植性

4、结构体成员数组大小为0

结构体数组成员的大小为0是GNU C的一个特性。好处是可以在结构体中分配不定长的大小。如:

typedef struct st

{

int a;

int b;

char c[0];

}st_t;

sizeof(st_t)等于8,即char c[0]的大小为0。

5、结构体与联合体的区别?

(1)结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。

(2)对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

四、数据类型和函数

1、bool,int,float,指针类型的变量a 与0如何比较?

(1)bool :   if ( !a ) or if(a)
(2)int :   if ( a == 0)
(3)float :  const EXPRESSION
EXP = 0.000001
          if ( a < EXP
&& a >-EXP)
(4)pointer : if ( a != NULL) or if(a
== NULL)

2、函数参数入栈顺序

C语言函数参数入栈顺序是从右向左的,这是由编译器决定的,更具体的说是函数调用约定决定了参数的入栈顺序。C语言采用是函数调用约定是__cdecl的,所以对于函数的声明,完整的形式是:int __cdecl
func(int a, int b);

3、什么函数不能声明为虚函数?

Constructor

五、数组与指针

1、“引用”与指针的区别?

(1)引用必须被初始化,指针不必。

(2)引用初始化以后不能被改变,指针可以改变所指的对象。

(3)不存在指向空值的引用,但是存在指向空值的指针。

(4)指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。

(5)流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。

2、数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(1)修改内容上的差别

char a[] = “hello”;

a[0] = ‘X’;

char *p = “world”; // 注意p 指向常量字符串

p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

(2)用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。

//计算数组和指针的内存容量

char a[] = "hello world";

char *p = a;

cout<< sizeof(a) << endl; // 12 字节

cout<< sizeof(p) << endl; // 4 字节

注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。

void Func(char a[100])

{

cout<< sizeof(a) << endl; // 4 字节而不是100 字节

}

六、内存分配回收

1、malloc/free与new/delete的区别?

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

(2)对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

(3)不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。

(4)既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,结果也会导致程序出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。

2、malloc(0)返回值是什么?

如果请求的长度为0,则标准C语言函数malloc返回一个null指针或不能用于访问对象的非null指针,该指针能被free安全使用。

3、程序的内存分配是怎样的?

一个由C/C++编译的程序占用的内存分为以下几个部分:

(1)栈区(stack由编译器自动分配释放存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的

(2)堆区(heap—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

(3)全局区(静态区)(static全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。

(4)文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。

(5)程序代码区—存放函数体的二进制代码

例子程序:

//main.cpp

int
a=0;    //全局初始化区

char *p1;   //全局未初始化区

main()

{

intb;栈

char s[]="abc";   //栈

char *p2;         //栈

char *p3="123456";  
//123456\0在常量区,p3在栈上。

static int c=0;   //全局(静态)初始化区

p1
= (char*)malloc(10);

p2
= (char*)malloc(20);  //分配得来得10和20字节的区域就在堆区。

strcpy(p1,"123456");  
//123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。

}

4、堆(heap)和栈(stack)的区别?

(1)申请方式

stack:由系统自动分配。

例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间。

heap:需要程序员自己申请,并指明大小,在C中用malloc函数,

如p1=(char*)malloc(10);

但是注意p1本身是在栈中的。

(2)申请后系统的响应

栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。

(3)申请大小的限制

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。

(4)申请效率的比较:

栈:由系统自动分配,速度较快。但程序员是无法控制的。

堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

另外,在WINDOWS下,最好的方式是用Virtual Alloc分配内存,他不是在堆,也不是在栈,而是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。

(5)堆和栈中的存储内容

栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的

当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。

堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。

(6)存取效率的比较

char s1[]="aaaaaaaaaaaaaaa";

char *s2="bbbbbbbbbbbbbbbbb";

aaaaaaaaaaa是在运行时刻赋值的;

而bbbbbbbbbbb是在编译时就确定的;

但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)

比如:

#include

voidmain()

{

char a=1;

char c[]="1234567890";

char *p="1234567890";

a = c[1];

a = p[1];

return;

}

对应的汇编代码

10:a=c[1];

004010678A4DF1movcl,byteptr[ebp-0Fh]

0040106A884DFCmovbyteptr[ebp-4],cl

11:a=p[1];

0040106D8B55ECmovedx,dwordptr[ebp-14h]

004010708A4201moval,byteptr[edx+1]

004010738845FCmovbyteptr[ebp-4],al

第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。

(7)堆栈溢出的原因?

没有回收垃圾资源;层次太深的递归调用。

5、为什么需要内存对齐?

为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。

其他

1、不能做switch()的参数类型?

switch的参数不能为实型。

2、语句for( ;1 ;)有什么问题?

和while(1)相同,无限循环。

3、do……while和while……do有什么区别?

前一个循环一遍再判断,后一个判断以后再循环

4、三种基本的数据模型?

按照数据结构类型的不同,将数据模型划分为层次模型、网状模型和关系模型。

5、判断一段程序是由C 编译程序还是由C++编译程序编译的?

#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif

6、无限循环的几种写法?

(1)while(1){}

(2)for(;;){}

(3)Loop: ... goto Loop;

7、ASSERT()的作用?

ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,可以这样写程序:

......

ASSERT( n != 0);

k = 10/ n;

.....

ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。

assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。

8、system("pause");的作用?

系统的暂停程序,按任意键继续,屏幕会打印,"按任意键继续。。。。。"省去了使用getchar();

9、C++的类和C里面的struct有什么区别?

C++中的类具有成员保护功能,并且具有继承,多态这类OO特点,而C里的struct没有。C里面的struct没有成员函数,不能继承,派生等等。

10、动态连接库的两种方式?

(1)载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向系统提供了载入DLL时所需的信息及DLL函数定位。

(2)运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或LoadLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了。

11、程序什么时候应该使用多线程?

(1)耗时的操作使用线程,提高应用程序响应

(2)并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。

(3)CPU系统中,使用线程提高CPU利用率

(4)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。

其他情况都使用单线程。

12、多进程与多线程的区别?

(1)进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。

(2)线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

(3)两者都可以提高程序的并发度,提高程序运行效率和响应时间。

(4)线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

13、用变量a给出下面的定义

a) 一个整型数(An integer)

b) 一个指向整型数的指针(A pointer to an integer)

c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)

d) 一个有10个整型数的数组(An
array of 10 integers)

e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)

f) 一个指向有10个整型数数组的指针(A
pointer to an array of 10 integers)

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and
returns an integer)

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer

argument and return an integer )

答案是:

a) int a; // An integer

b) int *a; // A pointer to an integer

c) int **a; // A pointer to a pointer to an
integer

d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointers
to integers

f) int (*a)[10]; // A pointer to an array
of 10 integers

g) int (*a)(int); // A pointer to a
function a that takes an integer argument and returns an integer

h) int (*a[10])(int); // An array of 10
pointers to functions that take an integer argument and return an integer

-----------END----------

C语言面试基础知识整理

(中庭地白树栖鸦,冷露无声湿桂花。今夜月明人尽望,不知秋思落谁家。)