1.运行结果是啥?
fib函数每递归一次cnt就+1 fib就是把大于等于1的数拆成两个数之和,也就说只要fib的变量不是0或1,他就要拆一次,挨着数出来就行了
结果是67
2.这个代码的运行结果是?
x后置++,第一次先打印1 然后x变成了2进入判断语句进行判断,判断的时候用的是2
后置--x变成了1,然后又回去,导致打印的结果一直都是1 判断条件一直都是2
所以运行结果为陷入死循环
3.下面代码的运行结果为?
int i=1;
int j;
j=i++;
答:把i的值也就是1先赋给了j,此时j=1,然后后置++ i变成了2 因此运行结果为2 1
4.k最终结果是什么?
表达式也即k=k*(i+j)=3*30=90 其实就是+的优先级高于*=
5.下面代码运行结果为?
主函数里面打印的那个a应该是最上面那个全局变量a,也就是1,至于test里面那个a,他是个局部变量,只能在test函数里面调用,出了函数,这个a的生命周期就结束了
这个题告诉我们局部变量和全局变量的名字最好不要冲突
6.c程序的基本组成单位是函数
c语言只规定了语法,库函数是独立于c语言语法之外的,是利用c语言的语法写出来的
c语言本身并没有输入输出函数
在对一个c程序进行编译的过程中,是不能发现注释中的错误的,因为注释在预处理阶段就已经被删除了
7.求两个数的最小公倍数
或
或 辗转相除法
8.
gets() 函数的功能是从输入缓冲区中读取一个字符串存储到字符指针变量 str 所指向的内存空间。
三步翻转法1.字符串整体逆序2.每个单词逆序
9.
假如给了20块钱,可以买20瓶水,喝完之后剩下了20个瓶子,又可以换10瓶水,喝完之后又得到了10个空瓶子,又换了5瓶水,喝完之后得到了5个瓶子,可以换两瓶水,余下了一个空瓶子,喝完之后又多了俩空瓶子,这时候就有三个空瓶子,又可以换一瓶水,喝完之后剩了两个空瓶,又换了一瓶水,喝完之后剩下一个空瓶,至此结束
通过测试了多个数字之后由数学归纳法可知这个money和total之间是有规律的也即total=2*money-1,因此该代码也可以写成下面这样
这样更简洁
10.打印菱形 比如输入7会出现一个这样的菱形
答案:需要有一个意识:打印多少个东西,只需要让循环进行多少次就行
打印这个图形无非就是先打印空格再打印*,那现在问题就是打印哪一行应该打印多少个空格,打印多少个*,以这个有13行的菱形为例,它是以中间为分界线,上下各六行组成的,把这个菱形的打印分成上下两部分来打印,前七行是上半部分,后六行是下半部分。首先讨论上面部分,第一行打印了6个空格一个*,第二行打印了5个空格三个*,可知如果i是从零开始算的话,每行打印line-1-i个空格,打印了2*i+1个*,然后是下面部分重新来一个for循环,i=0,应该打印1个空格,11个*,i=1,应该打印2个空格,9个*,也就是打印i+1个空格,2*(line-1-i)-1个*,要想打印i+1个空格,只需要让j
11.C程序常见的错误分类包括编译错误,链接错误,运行时错误。其中运行时错误指的是语法没有问题,但是逻辑有问题导致的错误,比如栈溢出就是一种运行时错误
12.Debug被称为调试版本,Release被称为发布版本,测试人员测试的就是release版本,debug版本包含调试信息,不做优化,release版本不可调试,而且编译器往往会自动做一些优化,程序大小和运行速度上最优
13.答案:C
const应该放在*的右边,这样才能保证p指向的变量值可以修改
14. C
A 中第一个const修饰的是变量p,第二个const修饰的是指针p A错误
B 中第一个const修饰的是*p,第二个修饰的是p,B错误
C中第一个const和第二个const都在*的左边,修饰的都是*p,C正确
D中第一个是指针数组,表示一个数组里面有是个元素,每个元素是int*类型
第二个是数组指针,里面存放的是一个数组的地址,该数组里面有十个元素,每个元素的类型是int
15.编程实现
本题的基本思路就是从前面开始找偶数,同时从后面开始找奇数,如果前面找到了偶数,后面找到了奇数,就把这两个数交换一下
这里判断条件之所以写成这样,是因为如果给的数组是一个全为奇数或者全为偶数的数组的话left会一直++或者right会一直--,到最后的时候再++或者--就会造成越界访问
值得注意的是虽然这个代码出现了很多whiile循环,但是只需要用指针把这个数组里所有元素地址访问了一遍就行了,其实效率式非常高的
16.运行结果为?
char类型的a和char类型的b存到了char类型的c里面,首先要发生整型提升,此时a+b在内存中的二进制序列应该如图所示
这个二进制序列在往c里面存放的时候会发生截断,c只能存8位,因此存的是00101100
因此如果打印a+b,打印的是没有发生截断之前的那个二进制序列,这个数其实是300,如果是打印c,打印的就是发生截断之后的那个二进制徐磊,这个数就是44
17.
大端模式处理器,a如图存放,这里是为了方便理解直接把这个0x1234在内存中的存储写成了这样。(其实数据在内存中均是以二进制序列存储的,0x1234是一个十六进制序列而不是说是个十六进制的数一千二百三十四,我可以用这个十六进制序列1234的一每位去换二进制序列的四位,最后这个十六进制序列就换了16位二进制序列,因为a是一个int类型的,他占四个字节也就是32个比特位,因此我们要补零补够32位,这时候把这个二进制序列翻译成16进制序列就是00 00 12 34)
&a就找到了00,但是这里的&a是一个unsigned int*类型的,现在我们把它强制转换成unsigned char*类型的,这时候再对a的地址进行解引用,一次就只能访问一个字节,因此这里取出的是十六进制数00 答案选A
18.
大小端字节序指的是数据在电脑上存储的字节顺序而不是二进制顺序
大端字节序是高位放到低地址,低位放到了高地址
小端字节序是低位放到了低地址,高位放到了高地址
19.打印杨辉三角
如果我们把杨辉三角的空格去掉就变成了这样
这个东西完全可以放到一个二维数组里面,可以看到第一列和对角线上的元素均为1
而第i行第j列的元素等于第i-1行第j-1列元素和第i-1行第j列元素之和,如果想要打印成三角形的样子,只需要补上一些空格即可,就像前面做的那个打印菱形的题目一样
20.
只需分别假设A B C D 是凶手带进去看看对不对即可
21.
首先for循环罗列出a所有的名次可能性,当a是第一名的时候,b有可能是2345名其中一名,这里先写成12345名的其中一名,后面加一个限制条件让五个人名次不同即可,这样内嵌for循环就罗列出了所有的名次可能性,要想让五个人名次不同,只需要打印之前让a*b*c*d*e=120即可,名次可能性有很多种,但是满足每个人说对一半这个条件的可能性只有一种,由于在c语言中真为1,假为0,这个条件也可以翻译成两个命题加起来等于1。答案如图。
22.打印X形图案
打印X形图案无非就是打印*和空格,可以先打印一个矩形,然后在两条对角线上打印*,其余位置打印空格。主对角线上坐标特点是行和列相等,次对角线上特点是加起来等于n-1。
23.
首先要生成七个数,那就把scanf放到for循环里面,就能输入七次了,每次输入一个score之后,都把它加到sum上,这样等七个score都输入完成之后就能得到sum了,但是要求要去掉最大值和最小值,那我们就要把最大值和最小值找出来,至于最大值max,我们让他的初始值为0,每当碰到一个比他大的数,就把这个数赋给max,为什么不初始化成别的数?因为要是我初始化个80分,七个评分都比80要小,那最终max就成80了,而真实得分并不够八十。同理最小值min初始化成100,这是因为如果我们把它初始化成假如80,万一每一个评分都比八十分要高,那min最终就是80,但是真实成绩要比80高。最后打印的时候要求精确到两位小时因此写%.2lf即可
24.
输出某一年的某一个月有多少天,其实就是讨论二月有多少天,其他月份的天数都是固定死的。可以先把每个月有几天的信息放在一个名为days的数组里面,这里故意在前面多放了一个元素进去,是为了让月份与下标对应起来,即如果输入的月份是1月,那么就打印31,否则的话输入一月打印的就成28了,如果是闰年就让二月份的天数+1
25.
首先利用for循环录入数据,注意题目中已经说了是输入一组升序的数据,因此这里并不需要再写代码对录入的数据进行排序,基本思路是拿要插入的数m和最后的数比较,如果m比最后的数小,那就让最后一个数往后挪一位,这样就有n+1个位置了,这也是我们创建数组大小为51个元素的原因。其实这个过程就是如果arr[i]比m大,那就把arr[i]拷贝到arr[i+1]上去,然后i--再和m比较,直到m比arr[i]大,这时候就把m赋给arr[i+1]即可。
当m比arr中任何一个数都要大,则代码会直接执行25行的else,把m赋给arr[i+1],就相当于把m插入到最后面去,满足要求。
当m比arr中任何一个数都要小,如果没有32、33行的代码,因为arr[i]>m这个判断条件恒成立,代码会一直执行24行的语句,最终因为不满足for循环的判断条件而跳出for循环直接执行35行的for循环,打印这个数组内容,但是在这个过程中我们发现m并没有被插入到这个有序数列中,为了解决这个问题,需要加上32行的代码,因为如果m比arr中任何一个数都要小,最终i会变成-1,这时候把m放在数列的第一个元素即可。
26.
先看结构体A,第一个成员变量一定是从偏移量为零的地方开始分配内存空间,向后分配了四个字节的空间到了偏移量为3的位置,然后第二个变量是short类型的,大小是2个字节,然后默认对齐数是4,取二者当中较小者因此第二个成员变量对齐数是4,根据对其规则应该在偏移量为4的倍数的地方这里应该是从偏移量为4的地方开始存放,往下分配两个字节到了偏移量为5的地方,第三个成员变量是int类型的,对齐数为4,因此要从偏移量为4的倍数的地方也就是偏移量为8的地址开始存,往后分配4个字节到了偏移量为11的地方,最后一个char类型的变量对齐数是1,直接而存在偏移量为12的地方。目前看起来是从偏移量为零的位置一直存到了偏移量为12的位置一共存了13个字节,但是对其规则规定结构体类型的变量大小应该是所有变量最大对齐数的整数倍也就是4的整数倍,因此结构体类型struct A的大小是16个字节,同理结构体类型struct B的大小是12个字节
27.
#pragma pack(4)
#pragma pack(4)表示把默认对齐数设置成4,先看structtag Test1类型的结构体变量,第一个成员变量是short类型的,在创建的时候在从偏移量为0的位置开始,往后分配两个字节 到了偏移量为1的位置,第二个成员变量是char类型的,对齐数是1,往后分配一个字节到了偏移量为2的位置,第三个成员变量是long类型的,对齐数是4,从偏移量为4的倍数的位置也就是偏移量为4的位置开始往后分配四个字节,到了偏移量为7的位置,最后一个成员变量是long类型的,对齐数是4,从偏移量为4的倍数的位置开始存放也就是偏移量为8的位置往后分配4个字节到了偏移量为11的位置,自此,从偏移量为0的位置存到偏移量为11的位置,共12个字节,而12恰好是最大对齐数的倍数,因此stT1的大小是12个字节,同理stT2的大小是12个字节,stT3的大小是16个字节
28.当A=2,B=3时,pointer分配了多大的空间?
该题目考查位段,位段的声明跟结构体类似,但是后面要加一个冒号和一个数字,后面的数字用来限制位段成员最多占用多少个比特位的空间。
首先宏是用来整个替换的,不会先算好在替换过去,因此后面算的是sizeof(struct_Record_Struct)*2+3而不是*5。再来看unsigned char类型的位段成员就一个字节一个字节的开辟空间,因此第一个成员变量开辟了1个字节也就是八个比特位的空间,用了四个比特位还剩四个比特位,第二个成员变量需要两个比特位,因此他们就共用一个字节即可,然后第三次成员变量不是位段成员,他自己就用去了一个字节的空间,第四个成员变量需要用一个比特位的空间,但是仍然要为他开辟一个字节大小的空间,综上所述,操作系统一共开辟了3个字节大小的空间,因此最终结果是3*3+3=9,也即printer指向的空间分配了9个字节大小的空间。
29.
首先创建了一个struct tagPIM*类型的结构体指针pstpimData,然后把puc强制类型转换成struct tagPIM*类型并赋给pstpimData,现在pstpimData就指向了数组的首元素,但是站在pstpimData的角度看,pstpimData认为他指向的是一个结构体类型,然后利用memset函数把puc数组所有内容初始化为0,然后对结构体类型里面的成员变量进行初始化,首先把ucpiml初始化为2,因为ucpim1不是位段成员,因此这个2也就是00000010就放到了第一个字节的空间里面去,第二个成员变量是位段成员,他用的是第二个字节的内容,现在要把他初始化为3,3的二进制序列是00000011,但是ucData0只能放一个比特位,因此就把最后一位1放到了第二个字节最左边的空间里面去。然后要把第三个成员变量初始化为4,4的二进制序列是00000100,ucData1里面只能放2个比特位,因此初始化的时候就把最后两位放到仍然是第二个字节里面去,因为第二个字节有八个比特位,刚才ucData0用了1个,还剩七个,完全足够放下ucData1的2个比特位,这时候第二个字节还剩下5个比特位,再来初始化第四个成员变量,初始化为5,5的二进制序列为00000101,放到ucData2里面去,ucData2只能用3个比特位,因此就把101放到了仍然是第二个字节里面去,此时内存中的二进制序列如图,现在要以16进制并且是只打印两位将puc数组中打印出来,结果应该是02 29 00 00
30.运行结果为?
本题考查如何计算联合体的大小。操作系统在为联合体分配内存空间的时候,也是有对其规则的,规则有二 1.联合体类型的大小至少等于其成员变量中较大的那个2.联合体类型的大小是最大对齐数的整数倍。
VS编译器的默认对齐数是8,这里第一个成员变量的对齐数是2,占7个字节,第二个成员变量的对齐数是4,大小是4个字节,共9个字节,9并不是最大对齐数8的倍数,因此该联合体的大小是16个字节。
31.运行结果为?
本题考查联合体的内存分配和大小端字节序的知识点。联合体中的成员变量共用同一块内存空间,short类型的k是两个字节,char类型的数组i也是两个字节,因此只需开辟两个字节大小的空间即可。再创建联合体的同时创建了联合体类型的指针变量s,和联合体类型的a。现在把a的地址赋给了s,s就指向了联合体的这一块内存空间。现在把i数组的两个元素初始化成0x39和0x38,如图,然后以16进制的形式来打印k,k也是用的这一块内存空间,因为VS 编译器用的是小端存储的方式,因此打印结果应该是3839,因为只有这样39才是低位,符合小端存储低位存在低地址处的原则。
32.运行结果为?
本题考查枚举类型。枚举类型默认第一个是0,然后依次+1递增。这里X1=1,Y1=2,Z1=255,A1=256,B1=257,因此运行结果为1,257
33.
AB显然正确,C这个说法有歧义,malloc向内存申请零字节内存空间这种行为是标准未定义的,取决于编译器。D中malloc申请了空间不释放的话会造成内存泄漏
34.
思路:如果这组数中只有一个是单着的,那么我们直接用第一个数把所有数异或在一起就可以求出那个单着的数,这是异或操作符具有a^a=0,a^0=a两条性质,如果只有一个数是单着的,因为别的数都是成对存在的,异或在一起之后就全都是0,一堆0异或完了还是0,最终就是0^单着的那个数,就可以把这个数找出来了,但是题目中说有两个是单着的,比如单着的数是5和6,那么全部异或在一起得到的结果就是5^6,而我们想要的是单独挑出5和6,因此需要先分组,使得分完组之后每一组里面只有一个单着的数。至于分组的标准,因为这两个单着的数异或在一起的结果肯定不为零,因为如果结果为零表示这些数是相同的,但是前面说过了他们是单着的数,如果异或完之后的结果某一位的二进制数是1,说明异或的两个数中这个位置的二进制数不同,那么我们可以依靠这个特点把这两个数分到两个组中去,首先要找到异或的这个结果(这里是ret)第几位是1,那我们可以把ret的每一位都拿出来看看,看看哪一位是1,利用ret>>i&1就可以拿到ret的每一个二进制位(因为二进制位不是零就是一,按位与1之后如果本来是1,按位与之后还是1,是0按位与之后还是零,这样就拿到了ret的每一个二进制位),此时的代表的就是ret的二进制位右移了多少i位后才找到1,也即两个单着的数二进制位不同的位置,这里用新变量pos来记录。自此我们就把两个单着的数分到了不同的组去,然后把每一组的数都按位异或就可以得到每个单着的数了。这个函数的返回值只能是void类型的,我们可能想要让他返回这两个单着的数,但是返回值只能是一个,怎么返回这两个数呢?可以利用返回型参数,把x和y的地址传过去,并利用指针接收他们,同时在分完组之后把每一组异或在一起之后得到的结果分别赋给*px和*px,此时的x和y就被改成了这两个单着的数,打印x和y即可得到这两个单着的数。
35.
b表示二进制模式,A中没有指定用什么方式打开,B中只是说用写的方式打开,没说二进制,C中表示用二进制写的方式打开文件,D也没有指定打开方式,因此答案选C
36.
fopen函数的返回值需要判断是否为空指针NULL,才能知道文件是否打开成功,因此C错误,其余选项均正确。一般我们在用fopen函数的时候固定的模式都是FILE* pf=fopen(“test.dat”,“r”);if(pf==NULL){perror(“fopen”);};最后还要fclose(pf);pf=NULL;
37.
文件名不一定包含后缀名,答案选B
在大多数操作系统中,文件名通常包含后缀名,用于标识文件的类型或格式。后缀名是文件名中最后一个点(.)之后的字符序列。例如,"document.txt"中的后缀名是"txt","image.jpg"中的后缀名是"jpg"。
但是,C语言中打开文件时并不要求文件名必须包含后缀名。实际上,C语言中的文件操作函数并不关心文件名的具体格式或含义,它们只是根据提供的字符串来查找或创建文件。因此,你可以使用任意文件名作为参数来打开文件,而不一定需要包含后缀名。需要注意的是,包含后缀名的文件名可以帮助操作系统和其他应用程序更好地识别文件的类型,从而进行相应的处理。因此,在实际开发中,为了更好地描述文件的内容,建议给文件名添加适当的后缀。
38.
getchar是适用于标准输入流(键盘)的字符输入函数,B错误
39.这个代码在干嘛?
fgetc是C语言标准库中的一个函数,用于从打开的文件中逐个读取字符。
函数原型如下:
int fgetc(FILE *stream);
参数stream是一个指向FILE类型的指针,表示要从中读取字符的文件流。通常情况下,可以使用fopen函数打开文件并返回的文件指针作为参数传递给fgetc函数。
fgetc函数的返回值是读取到的字符(以unsigned char类型返回),或者在读取到文件结尾时返回特殊值EOF(End-Of-File)。EOF是一个负数常量,通常被定义为-1。
可以看到这个代码其实就是在统计文件中的字符数,答案选B
40.
sprintf是C语言中的一个标准库函数,用于将格式化的数据写入字符数组中。而不是写到输出流中,答案选D
函数原型如下:
int sprintf(char *str, const char *format, ...);
参数str是一个指向字符数组的指针,用于存储格式化后的字符串。这个字符数组需要足够大以容纳生成的字符串。
参数format是一个格式化字符串,用来指定输出的格式。
sprintf函数的返回值是生成的字符串的字符数(不包括终止空字符\0)。如果发生错误,则返回一个负值。
41.
预处理是C语言编译过程中的一个阶段,它在实际编译之前对源代码进行一些预处理操作。预处理过程包括以下三个主要步骤:
- 文件包含(File Inclusion):通过#include预处理指令,将其他文件的内容插入当前文件中。这样可以重用其他文件中定义的代码或声明。可以包含标准库头文件(如stdio.h)或用户自定义的头文件。
- 宏替换(Macro Substitution):通过宏定义和宏替换的方式,将源代码中定义的宏进行替换。宏是预处理器指令,可以用于简化代码,实现代码复用或定义常量。预处理器会根据宏定义将源代码中的宏名称替换为相应的标识符或表达式。
- 条件编译(Conditional Compilation):通过条件编译指令(如#if、#ifdef、#ifndef)来根据条件判断选择性地编译代码块。通过这些指令可以根据不同的条件编译不同的代码,实现跨平台兼容性、调试选项等功能。
这三个过程执行完后,预处理器会生成一个经过预处理的源代码文件,其中包含了文件包含、宏替换和条件编译所引发的改动。然后,这个经过预处理的源代码文件将被传递给编译器进行实际的编译过程。答案选A
在预处理之后,将生成经过预处理的源代码文件。然后,该文件将被编译器进一步处理为汇编语言代码。具体而言,在编译阶段,编译器将经过预处理的源代码翻译成汇编语言代码。汇编语言代码基于特定的硬件体系结构。它使用机器指令集来表示特定操作,例如数据传送、算术运算和控制流程等。汇编语言是与机器语言直接相关的,每条机器指令对应一个特定的操作码,用于执行特定操作。汇编语言代码更接近于底层硬件,而且十分依赖于具体的计算机体系结构。生成的汇编语言代码可以通过汇编器(assembler)转化为机器语言目标文件,它包含可以在特定平台上运行的机器指令。总结起来,预处理、编译和汇编是C语言编译过程中的三个主要阶段。预处理阶段在编译之前,通过预处理器处理源代码。然后,编译器将预处理后的代码转换为汇编语言代码,该代码可以由汇编器转换为机器语言目标文件。
42.
答案:C
链接阶段是C语言编译过程中的最后一个阶段,用于将多个编译后的目标文件(object files)和库文件(library files)合并在一起,生成最终可执行文件。
在编译过程中,每个源代码文件经过预处理、编译之后会生成对应的目标文件。这些目标文件包含了编译器所生成的机器代码和其他必要的信息,但它们仍然是相对独立的,并不能直接运行。
在链接阶段,链接器(linker)会将所有目标文件以及所需的库文件合并在一起,解决符号引用(symbol references)和符号重定位(symbol relocation)的问题。符号引用是指目标文件中引用了但未定义的符号(例如函数或全局变量),而符号重定位是指需要调整目标文件中的一些地址或偏移量的指令。
链接器完成以下几个主要任务:
- 符号解析(Symbol Resolution):将目标文件中的符号引用与其他目标文件中的符号定义进行匹配,建立符号引用和符号定义之间的关联。
- 符号重定位(Symbol Relocation):根据符号引用和符号定义之间的关系,调整目标文件中的地址或偏移量,确保它们指向正确的位置。
- 符号合并(Symbol Merging):合并多个目标文件中相同符号的定义,避免冲突。
- 库文件链接(Library Linking):将所需的库文件中的代码和数据合并到可执行文件中,以便程序能够调用库中提供的函数和资源。
最终,链接器生成一个完整的可执行文件,该文件包含了所有的目标文件和库文件,可以被操作系统加载和执行。
总结起来,链接阶段是将编译后的目标文件和库文件合并成最终可执行文件的过程。链接器解决了符号引用和重定位问题,将各个编译单元连接在一起,生成可独立运行的程序。
43.哪个不是指针变量?
虽然看起来两条语句都是int*a,b,但是INT_PTR只是一个替换,而int_ptr是一种新的类型,因此再创建a和b的时候相当于int*a;int b,只有一个*,被a用掉了,b就没有*了,只能是int类型
44.
feof函数用于判断前一个读取操作是否已经到达文件末尾,而不是用于判断文件是否读取结束。这是一个常见的误解。
具体来说,feof函数在以下情况下返回非零值(真):
- 在之前的读取操作中,文件流已经到达了文件末尾。也就是说,前一个读取操作(例如fgets或fscanf)从文件中读取的是文件的最后一部分,并没有继续读取下一个有效数据。
- 在之前的读取操作中,出现了读取错误,如文件被删除或被其他程序修改等情况。
但是,feof函数不能可靠地判断文件的读取是否已经结束。这是因为文件指针的位置是根据之前的读取操作来确定的,并不能提前知道文件是否已经结束。
为了确定文件读取是否已经结束,更可靠的方法是检查读取操作的返回值。例如,在使用fgets函数读取一行数据时,如果返回值为NULL,则表示已经读取到文件末尾或者出现了读取错误。
下面是一个示例,使用fgets函数读取文件并判断文件是否读取结束:
FILE *file = fopen("file.txt", "r"); if (file != NULL) { char line[100]; while (fgets(line, sizeof(line), file) != NULL) { printf("%s", line); } if (feof(file)) { printf("已到达文件末尾\n"); } else { printf("读取文件时发生了错误\n"); } fclose(file); } else { printf("无法打开文件\n"); }
在上述示例中,当fgets函数返回NULL时,程序会判断文件是否已经结束,并输出相应的消息。
总结起来,feof函数用于判断前一个读取操作是否已经到达文件末尾,而不能可靠地判断文件的读取是否已经结束。判断文件是否读取结束应该通过检查读取操作的返回值。
45.
选D
在C语言中,没有名为#end的预处理指令或标记。通常,C语言中的预处理指令以#作为前缀,并在指令和其后面的内容之间使用空格进行分隔。
常见的一些预处理指令包括:
- #include:用于包含头文件。
- #define:用于定义宏。
- #ifdef、#ifndef、#else、#endif:用于条件编译。
- #if、#elif、#else、#endif:用于条件表达式。
这些预处理指令通常用于在编译之前进行一些文本替换,或根据条件编译不同的代码。
46.
选D
C语言中有一些预定义的符号,也称为预定义宏(Predefined Macros),它们提供了一些有用的编译时信息。预定义符号以__开头和结尾,通常用于条件编译、平台识别和调试等方面。
以下是一些常见的预定义符号:
- __LINE__:表示当前源代码的行号。
- __FILE__:表示当前源文件的文件名。
- __DATE__:表示预处理的日期,以字符串形式表示,格式为"月 日 年",例如:"Jun 30 2023"。
- __TIME__:表示预处理的时间,以字符串形式表示,格式为"小时:分钟:秒",例如:"03:23:02"。
- __func__:表示当前函数的名称,用于C99及更高版本。
这些预定义符号可以在代码中使用,通常用于调试输出或条件编译等场景。例如,可以使用__LINE__和__FILE__来输出错误消息并指示出错位置。
#include <stdio.h> int main() { printf("错误发生在文件 %s 的第 %d 行\n", __FILE__, __LINE__); return 0; }
输出结果可能类似于:
错误发生在文件 example.c 的第 5 行
需要注意的是,预定义符号是由编译器提供的,在不同的编译器或平台上可能会有所不同。因此,在使用预定义符号时应谨慎考虑其可移植性。
除了上述列举的常见预定义符号外,不同的编译器可能还定义了其他额外的预定义符号,用于特定的编译器功能或平台相关信息。例如,一些编译器提供了__cplusplus预定义符号,用于指示当前是否处于C++编译环境下。
47.
选A
48.
先把N都替换成4,Y(n)就是((4+2)*n),z就是2*(4+Y(5+1)),Y(5+1)=((4+2)*5+1)=31,因此z就是2*(4+31)=70.
49.运行结果为?
宏是直接原封不动的替换,而并不会提前算好再替换。所以C=2+2*3+3=11
50.
宏由于不用进行调用和返回值,执行速度要比函数更快。因此答案选C
51.
选B
条件编译指令是用于根据条件选择性地编译代码的预处理指令。它允许在编译时根据不同的条件选择性地包含或排除特定的代码块。条件编译指令在C语言中非常常见,可以根据不同的情况选择编译或忽略不同的代码段。
常见的条件编译指令包括以下几个:
- #if、#elif、#else、#endif:这组指令用于基于条件表达式选择性地编译代码块。#if指令检查给定的条件表达式是否为真,如果为真,则编译#if和#endif之间的代码块;如果为假,则忽略该代码块。#elif用于额外的条件检查,#else用于处理条件不满足的情况。#endif用于结束代码块。
#if CONDITION_1 // 如果 CONDITION_1 成立,则编译这里的代码 #elif CONDITION_2 // 如果 CONDITION_2 成立,则编译这里的代码 #else // 如果前面的条件都不成立,则编译这里的代码 #endif
- #ifdef、#ifndef、#else、#endif:这组指令用于检查宏是否已定义。#ifdef检查宏是否已定义,如果已定义,则编译#ifdef和#endif之间的代码块;#ifndef则相反,只有宏未定义时才编译。#else和#endif与之前的示例相同,用于处理条件不满足的情况。
#ifdef MACRO_NAME // 如果 MACRO_NAME 宏已定义,则编译这里的代码 #elif !defined(MACRO_NAME) // 如果 MACRO_NAME 宏未定义,则编译这里的代码 #else // 其他情况下编译这里的代码 #endif
以上是两个常见的条件编译指令,它们可以通过编译时的条件表达式或宏的定义与否来选择性地编译代码。需要注意的是,在使用这些条件编译指令时,确保在合适的位置使用对应的指令闭合,以避免语法错误。
52.
尖括号包含头文件的时候会直接去指定的库目录中寻找,双引号包含头文件的时候会先在当前源文件所在目录中找,然后去库目录中找,头文件中只适合放类型的声明,包含其他头文件,函数的声明而不要把变量定义放到头文件中,因为如果后面多次引用头文件的话会造成变量的重定义。答案选D
53.
并打印交换奇数位和偶数位之后的数。
思路:这个过程可以形象的想象一个数的二进制序列中本来有32个比特位,偶数位在2 4 6 8这样的位置上,奇数位在1 3 5 7这样的位置上,现在我把所有的偶数位和奇数位分别单独拎出来,然后把偶数位的这个序列右移一位,这样偶数位就放在了1 3 5 7这样的位置上,然后把奇数位的这个序列左移一位,这样奇数位就放到了2 4 6 8这样的位置上,自此就完成了奇偶位的交换。
先把一个数二进制序列中的所有偶数位拿出来,奇数位全部置成零,得到一个32个比特位的二进制序列,再把奇数位全部拿出来,偶数位全部置成0,得到另一个32个比特位的二进制序列,如果将第一个序列右移一位,第二个序列左移一位,然后加在一起,就能得到奇偶位交换之后的数的二进制序列。比如一个数的二进制序列为00001111(剩下的位不写了全是零,凑够32位),那么第一个序列就是000001010,第二个序列就是00000101,然后把第一个序列右移一位,第一个序列就变成了00000101,第二个序列左移一位就变成了00001010,再把这两个序列加起来其实就是奇数和偶数位交换之后的数的二进制序列。要得到第一个序列,只需要让原来的数按位与上一个0xaaaaaaaaa(这个东西的二进制序列是10101010101010101010101010101010),要拿到第二个序列,只需要让原来的数按位与上一个0x55555555(这个东西的二进制序列是01010101010101010101010101010101)。写成常规的形式就是int ret=((num&0xaaaaaaaa)>>1+(num&0x55555555)>1+(N&0x55555555)
54.offsetof宏的实现
文章来源:https://www.toymoban.com/news/detail-813890.html
将整数零强制类型转换成结构体指针类型之后,0现在是某个结构体类型的地址,能访问这个结构体的成员变量,第一个成员变量的地址就是0,第二个成员变量的地址,取决于什么时候开始创建的第二个变量,也即取决于第一个成员变量的大小以及对齐规则。因为这个结构体变量开辟的内存是连续的,又因为每一个地址对应一个字节大小的空间,也就是说第一个成员变量的地址和偏移量都是0,假如第二个成员变量在偏移量为4的地方被分配空间,他的地址也是4,假如第三个变量在偏移量为8的地方被分配空间,那他的地址也是8,这时候成员变量的偏移量等于他的地址,因此将此时的地址转换成int类型或者size_t类型返回即为偏移量。文章来源地址https://www.toymoban.com/news/detail-813890.html
到了这里,关于C语言测试题(附有详细解析)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!