21 函数可以嵌套定义和调用吗?

函数不可以嵌套定义,但是可以嵌套调用。

c语言必背入门代码解释(小白入门C语言20问20答2)(1)

22 变量的定义和声明是一样的吗?

不一样。变量的定义是指要分配存储空间的,而声明并不需要;此外,一个变量只能被定义一次,不能重复定义多次,但是可以多次声明。以外部变量为例来说,一个外部变量只能在所有函数的外面定义一次,但是它可以在函数内外被多次声明。对变量的初使化只能在定义时进行,而不能在声明的时候进行。

23 指针变量的基类型具有什么作用?

指针变量的取值都是内存单元的地址,即是一个无符号的整数,就其取值而言,指针变量是没有类型概念的。那么指针变量的基类型是什么意思呢?

实际上,指针变量的基类型是指示当使用指针变量操作内存单元时,需要操作多少个内存单元。另外,指针算术运算时,编译器也需要以基类型为基础来参与运算。编译器在处理编译一个单元的int与一个单元的float是不同的。例如,假设p是一个指针,表达式p 是表示将指针p往后移动一个元素所占的存储单元,而到底是几个字节要由p所指示的元素本身的数据类型所决定。例如:int *p1; char *p2; 此时,p1 表示p1往后移动两个字节,而p2 表示p2往后移动一个字节。

24 表达式*p 是自增 p 还是 p 所指向的变量值?

因为 和*运算符的优先级相同,因此,这个表达式的运算顺序要根据其结合方向来判断。由于其结合方向应该是自右向左,因此上面表达式等价于*(p )。那么,它的含义就应该是自增p,但是返回值是p自增之前所指向的变量的值,即*p的值。由于类似情况会经常遇到,所以也提醒读者应该养成在适当的时候使用括号的习惯,以免产生程序歧义。

25 什么是空指针?

空指针与同类型的其它所有指针都不相同, 也与任何对象或函数的指针都不相等。也就是说, 用取地址操作符&永远也不能得到空指针, 同样对 malloc() 的成功调用也不会返回空指针, 除非调用失败, malloc() 才返回空指针。

实际上,空指针表示“未分配”或者“尚未指向任何地方”的指针。它在概念上不同于未初始化的指针。因为,空指针可以确保不指向任何对象或函数,而未初始化指针则可能指向任何不确定的地方。

C语言中的NULL是C语言中定义的预处理宏,它一般代表0 或者 ((void *)0),用于表示一个空指针。在C程序中,NULL实际上和0完全等价的。需要注意的是:NULL只能用作指针常量来使用。

*void:一种通用指针未确定类型的指针, 可以通过类型转换强制转换为任何其它类型的指针。

26 数组名和指针是否相同?

并不相同。例如有如下定义:char a[ 4];char *p;此时,字符数组名a是一段确定数据空间的起始地址,但字符指针p却还没有指向任何确定的空间。此外,指针是可以移动的,数组名却不能移动。

但是,在很多C语言的教材中都有数组名和指针“等价”的说法。需要注意的是,这里的等价不是指的相同,只是想说明在算法定义中可以用指针的形式来访问数组的数据,这样做起来操作更方便。尤其是当使用数组名作为函数参数的时候,往往是可以采用与之对应的指针来替换的。

27 C语言如何实现动态数组?

C语言中是不允许定义动态数组的,即数组中元素个数的大小必须在定义数组时确定。在以下的数组的定义语法中:

数据类型 数组名[元素个数表达式]

元素个数表达式必须是常量表达式,其中不能含有变量。因此在C程序中不能根据程序运行的状态动态在定义元素个数可变的数组,例如以下的数组定义是错误的,在编译时将产生语法错误:

int n; scanf("%d",&n); int a[n];

然而,在有些问题中数据的个数是可变的,如果数组定义得过小或过大,都不能满足程序的需要。此时可以通过C语言的动态内存分配功能来实现对动态数组的模拟。

动态分配内存的含义是指:在程序运行时根据需要从操作系统申请内存空间,在使用完毕后再释放归还给系统。动态分配内存使用函数malloc,其原型声明为:

void * malloc(unsigned size)

其中size表示需要分配的内存空间的字节数大小。

malloc函数的返回值是指向所分配的内存单元的首地址指针。由于在分配内存时,系统并不知道用户将要如何使用这些内存单元,因此malloc函数的返回值为void *,即无类型的指针。在用户程序中应根据需要将其强制转换为适当的基类型。例如从内存中分配用于存放5个整数的内存空间,并将其首地址指针赋值给整型指针变量p,应使用以下的代码:

int * p; p=(int *)malloc(5*sizeof(int));

28 一个指针变量到底会分配多少内存空间?

当定义了一个指针变量以后,实际上只是给这个指针分配了能够容纳指针本身的内存,而没有分配指针所指向的内容的存储空间。例如:int *p;定义之后,系统为指针p分配的内存空间为sizeof(int *),而并没有分配一个整型变量所占的字节数。

29 枚举元素的值是多少?

在C语言中,枚举元素是作为常量来处理的,程序编译时会给一个枚举变量中的元素依次赋初值0,1,…。但是,如果在定义时人为的改变了某个枚举元素的值,那么,其后面所有元素的值也会跟着改变。

30 程序设计中的文件概念

在计算机系统中,不论是程序还是数据都是存储在外部存储器中的文件。文件既是操作系统的重要概念,也是程序设计中的重要内容。在实际的数据处理问题中,大量的数据往往都是事先存储在具有一定格式的数据文件中,程序通过读写文件完成数据的取得和结果的保存。

根据文件的组织形式,文件分为ASCII文件和二进制文件两种基本类型,C语言中分别使用不同的方式操作这两类文件。

当程序需要操作文件中的数据时,应首先将文件打开,然后进行读写操作,当操作完成后,应及时关闭文件,以保证文件中数据的安全性和完整性。

在C语言中的文件操作中,对于每一个打开的文件都使用一个称为“文件类型指针”的指针指向该文件,当文件被成功打开后,通过相应的文件类型指针即可以对文件进行读操作,而无须再关心文件的物理存储位置、文件名等相关文件信息。

为了表示已经打开的文件的相关信息,C语言在stdio.h中定义了FILE结构体,其数据成员定义如下(以下出自stdio.h):

c语言必背入门代码解释(小白入门C语言20问20答2)(2)

当打开一个文件后,即会产生一个以FILE为基类型的指针FILE *,程序中通过这个指针完成对文件的读写操作。

所谓打开文件,就是将指针指向要使用的文件,打开之后才能通过这个文件指针来读写文件的内容;而关闭文件,就是把文件指针和文件脱钩,以后不能再使用这个文件指针访问该文件。

通过调用函数fopen可以以不同的方式打开一个文件,并返回指向文件的FILE指针。fopen函数的原型声明为:

FILE * fopen(文件名,打开方式)

31 static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

答:全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。

从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。

static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用。

static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值。

static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

32 函数system()与exec()的区别

system()会调用shell,而exec()则不会调用shell。system是在单独的进程中执行命令,执行完毕还会回到程序中;而exec()则直接在进程中执行新的程序,新的程序会把原程序覆盖,除非调用出错,否则再也回不到exec()函数后面的代码。

system()会产生新的pid(生成新的shell),而exec()则不会。

33 缓冲输入与缓冲区

缓冲输入,将若干内容先存储到称为缓冲区(buffer)的临时存储区域,等待适当时机将缓冲区内的内容变得对程序可用,非缓冲输入是不使用缓冲区,直接让输入对程序可用。缓冲输入对于频繁读写有更高的效率,而非缓冲输入是即时交互程序所需要的。

34 位操作

在C语言中,可以单独操控变量中的位。读者可能好奇,竟然有人想这样做。有时必须单独操控位,而且非常有用。例如,通常向硬件设备发送一两个字节来控制这些设备,其中的每个位(bit)都有特定的含义。另外,与文件相关的操作信息经常被储存,通过使用特定位表明特定项。许多压缩和加密操作都是直接处理单独的位。高级语言一般不会处理这些级别的细节,C在提供高级语言使得的同时,还能在为汇编语言所保留的级别上工作,这使其成为编写设备驱动程序和嵌入式代码的首选语言。

在实际的程序设计中,有时需要存储少量的信息,这些信息并不需要占用一个完整的字节,只需占用几个或一个二进制位。例如,在存入一个标志时,只有0和1两种状态,用一个二进制即可。如果给其分配一个字节的空间,便浪费了存储空间。因此,C 引入了位域这一数据类型。

35 为什么0.1 0.2 != 0.3

浮点数虽然可以表示很大的数,是因为其分为小数位和指数位两部分,小数位其实也就是有限的六、七位。

实数在内存中战占4个字节,是按照指数形式存储的,实型数据分为小数部分和指数部分:

I 小数部分:用二进制表示。

I 指数部分:用2的幂次来表示;

二进制的0.111等于十进制的多少?

c语言必背入门代码解释(小白入门C语言20问20答2)(3)

十进制的0.1表示为二进制:0.0001 1001 1001 1001…(无限循环)

十进制的0.2表示为二进制:0.0011 0011 0011 0011…(无限循环)

双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 :

0.0100110011001100110011001100110011001100110011001100

因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 :

0.30000000000000004。

二进制表示小数的特殊性,以及位数的限制,所以十进制的小数表示成二进制时,误差始终会存在。

如以下实例:

#include<stdio.h> #include<stdlib.h> int main() { if(0.1 0.2 == 0.3) printf("0.1 0.2 = 0.3"); else printf("0.1 0.2 = %f",0.1 .3); system("pause"); } //output:0.1 0.2 = 0.400000

36 地址、标识符、指针

CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。

然而指针也是一种变量,他里面装的就是所指数据或者代码的地址。所以它可以指变量,也可以指函数。

37 函数调用

函数调用有三种形式:

I 把函数调用作为一条语句;

II 函数调用出现在一个表达式中;

III 函数调用作为一个函数的实际参数;

函数被调用时,系统为每个形参分配内存单元,也就是相当于一个局部变量的声明后的初始化。

函数可以通过函数指针被调用(也是以上三种调用形式)。函数指针还可以用作函数参数或函数返回值。

38 指针数组与数组指针

int *p1[10]; //指针数组

int (*p2)[10]; //数组指针

“[]”的优先级比“*”要高(即使优先级相同,而它们的结合性是从右至左)。p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含10 个指向int 类型数据的指针,即指针数组。至于p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针。

如何简单记忆呢?

没有括号,重心落在右边,符号*[],即指针数组,一个包括指针变量元素的数组。

如果有括号,重心落在括号,符号(*)[],即数组指针,一个指向数组的指针。

函数指针与指针函数也是如此理解:

int *f1(10); //指针函数

int (*f2)(10); //函数指针

39 数组做为参数时退化为指针的性能优势

数组作为函数参数时,在函数调用时,并不需要另行开辟内存空间,也就不需要另行对参数进行初始化或赋值,这是既节省了空间,也节省了时间。当然,指针传递可修改数组,如果想保护数组不被修改,只被访问,可以使用const作为数组参数前缀。

数组引用“退化”为指针的规则只适用于数组,而结构却是一级对象,当你提到结构的时候,你得到的是整个结构。

40 语句执行顺序的顺序执行与跳转执行

控制结构的四类语句有顺序、条件、循环、跳转(continue,break,goto,return),条件和循环的结构其实也就相当于顺序加跳转的结构。

按存储程序和程序控制的概念,控制器的程序计数器确定指令的读取位置,通常是顺序读取,但也可以跳转地址读取不同的指令。而高级语言的条件、循环语句的控制结构的实质就是跳转读取指令,类似于汇编语言的jmp和Basic语言的goto。

在C和C 语言中,也有goto语句,且能与条件、循环语句实现相同的效果,但后者显然更为优势。

goto也并不是豪无优势,它可以跳出多重循环。(break只能跳出单重循环)

-End-

,