1. 变量和常量
查缺补漏:
(1)typedef关键字
-
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等)。
-
在编程中使用typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明。
2. 程序编码
查缺补漏:
-
创建动态内存后先判断是否创建成功,最后一定要释放;
-
指针使用完后要空置;
-
注意函数中的局部变量,该函数运行结束就消失了。
题目 | 下列不属于异常(exception)分类的是() |
选项1 | 错误(faults) |
选项2 | 中断(interrupts) |
选项3 | 陷入(traps) |
选项4 | 中止(aborts) |
解析 | CPU异常是指指令执行的时候产生的错误。异常通常被分为这三种不同的类型:faults,traps和aborts。 |
答案 | 2 |
3. 赋值表达式
查缺补漏:
题目 | 定义变量: int a = 5 + 4 << 2; 则a的值是多少 |
选项1 | 21 |
选项2 | 36 |
选项3 | 13 |
选项4 | 18 |
解析 | “+”的优先级高于“<<” |
答案 | 2 |
4. 赋值语句
查缺补漏:
从一种无符号类型的转换为一种有符号类型时,或从一种有符号类型的转换为一种无符号类型时,可能会发生类型范围错误,包括数据丢失(截断)和符号位丢失(符号错误)。
从一种有符号整数类型转换为另一种精度更小的有符号类型时,或从一种无符号整数类型转换为另一种精度更小的无符号类型时,可能会发生数据损失(截断)。
5. 关系表达式
6. 函数
switch case break default 用法
7. 结构体
查缺补漏
题目 | 有以下结构体说明和变量的定义,指针p指向变量a,指针q指向变量b。则能把结点b连接到结点a之后的语句是( ) struct node { char data; struct node *next; } a,b,*p=&a,*q=&b; |
选项1 | a->next=q |
选项2 | p.next=&b |
选项3 | p->next=&b |
选项4 | (*p)->next=q |
解析 | 结构体指针要用->,结构体变量要用点号。 |
答案 | 3 |
结构体指针要用->,结构体变量要用点号。
结构体数组
结构体内存对齐
C语言问题集——结构体及其内存对齐_愿知的博客-优快云博客_c语言结构体内存对齐
8. 联合体
联合体变量不能作为函数参数。
在一个联合体中,某一时刻,只能存储某一成员的值。后一成员的值会覆盖前一成员的值。
大小端内存存储:
联合体(共用体 union)的内存共享
由于联合体中的所有成员是共享一段内存的,因此每个成员的存放首地址相对于于联合体变量的基地址的偏移量为0,即所有成员的首地址都是一样的。为了使得所有成员能够共享一段内存,因此该空间必须足够容纳这些成员中最宽的成员。对于这句“对齐方式要适合其中所有的成员”是指其必须符合所有成员的自身对齐方式。
大小必须满足两个条件:1)大小足够容纳最宽的成员;2)大小能被其包含的所有基本数据类型的大小所整除。
题目3 | 定义如下联合体类型和变量 union { unsigned char c[2]; unsigned int i; }m; m.i= ~0xFF; 则在little-endian字节序的机器上m.c[0], m.c[1]分别为 |
选项1 | 0, 0xFF |
选项2 | 0xFF,0 |
选项3 | 0,0 |
选项4 | 0xFF,0xFF |
解析 | 考察联合基本概念以及大小端模式 |
答案 | 1 |
9. 逻辑表达式
注意优先级问题。
如果多个表达式用||连接,则一个真表达式将使整个连接都为真。这个真表达式之后的表达式将不会被执行了。
如果多个表达式用&&连接,则一个假表达式将使整个连接都为假。这个假表达式之后的表达式将不会被执行了。
10. 内存分配
静态分配,内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如 :全局变量, static 变量;
动态分配,如果在栈上创建, 函数内局部变量的存储单元都可以在栈上创建 ,函数执行结束时这些存储单元自动被释放 ;
动态分配,如果在堆上创建,程序在运行的时候使用 malloc 申请的内存,程序员自己负责使用 free 释放内存 ;(malloc-free;new-delete)
资源(内存)申请后,必须判断申请是否成功
资源(内存)释放后,必须清除所有对该资源的引用
资源应成对提供申请接口、释放接口; 不建议使用realloc 申请内存;
题目 | 以下动态内存分配函数中,明确会对内存进行初始化的是 |
选项1 | aligned_alloc() |
选项2 | calloc() |
选项3 | malloc() |
选项4 | realloc() |
解析 | 只有calloc()会初始化为0,aligned_alloc()和malloc()都不会执行初始化,reallo()从原始指针复制内容,可能不初始化所有内存 |
答案 | 2 |
11. 数据结构
队列:先进先出
循环队列中,因为队头和队尾指针可循环原因,无绝对的大小关系。 循环队列的长度由队头队尾指针的相对位置决定。 循环队列,当队尾翻转之后,其索引小于队头。
带链的队列,存储位置可以不连续,不能直接由队头队尾指针位置计算。
堆栈(栈):先进后出 ps:内存分配,在堆上(new,malloc),在栈上(局部变量)
链表:单向链表,有序链表,循环链表
单循环链表从表中任一结点出发都能扫描到整个链表
线性表若采用链式存储表示时所有结点之间的存储单元地址可连续可不连续。
二维数组是其数组元素为线性表的线性表
题目 | 设单链表中结点的结构是(data,link), 已知指针p所指结点不是尾结点, 若在*p之后插入结点*s,则应执行的操作是 |
选项1 | s->link = p; p->link =s; |
选项2 | p->link = s; s->link = p; |
选项3 | s->link = p->link; p=s; |
选项4 | s->link = p->link; p->link =s ; |
解析 | 单向链表在p节点后插入节点s,则s的下级指向原p的下级,P的下级改为指向s |
答案 | 4 |
12 数组
稀疏矩阵的压缩存储的目的是减少矩阵的存储空间。
题目 | 已知数组ARR的定义是int ARR[4][8];假设按行排列,现在需要把这个数组作为实参传递给一个函数进行处理。下列说明中可以作为对应的形参变量说明的有 |
选项1 | int s[4][ ] |
选项2 | int *s[8] |
选项3 | int (*s)[8] |
选项4 | int s[ ][8] |
解析 | 因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多 少列。 |
答案 | 3,4 |
13. 算术表达式
题目 | 在32位系统中(假设int长度为4字节),下面程序的打印结果是() void func() { int a = 14; int b = sizeof(a++); printf("%d,%d\n",a,b); } |
选项1 | 15,4 |
选项2 | 14,4 |
选项3 | 15,14 |
选项4 | 15,15 |
解析 | sizeof()不会对表达式a++进行求值,所以a保持原有值14. |
答案 | 2 |
选项1 | int s[4][ ] |
选项2 | int *s[8] |
选项3 | int (*s)[8] |
选项4 | int s[ ][8] |
解析 | 因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多 少列。 |
答案 | 3,4 |
14. 位运算
15. 文件操作
fwrite是由标准C库提供的库函数,通过标准C库自身提供的缓存,目的是减少write系统调用的次数。
16. 选择结构
if(逻辑判断)
do
{
}while(逻辑判断);
题目 | 以下程序段 void func1_8() { int a=5, b, c=0; if ((!c) && (9>a>1)) b=1; else b=0; printf("%d", b); } 输出结果为:1 |
选项1 | |
选项2 | |
选项3 | |
选项4 | |
解析 | C语言中没有连续表达式。 9>a>1,会先计算9>a 结果为1,再进行1>1判断结果为0 |
答案 | 0 |
17. 循环控制
continue语句只结束本次循环,而不终止整个循环的执行。continue会致使循环跳过循环体中余下的语句,转而判断循环条件是否仍然成立,然后选择是否再次进入循环体
break语句则是结束整个循环过程,不再判断执行循环的条件是否成立。
continue会致使循环跳过循环体中余下的语句,转而判断循环条件是否仍然成立,然后选择是否再次进入循环体
题目 | 在下列选项中,没有构成死循环的程序段是() |
选项1 | int i =100; for(;;) { i = i % 10+1; if (i > 10) break; } |
选项2 | #define MAX (256) unsigned char idx = 0; for (idx = 0; idx < MAX; idx++) { // do something } |
选项3 | int k=0; do { ++k; } while (k <= 10); |
选项4 | int s=12; while(s); --s;int s=12; while(s); --s; |
解析 | C语言中的语句以分号结束,如果一条语句后面单独写一个分号,则该分号会被当做不产生任何效果的空语句。 A 因为i经过取模 + 1后,其值不可能大于10,所以永远不可能执行break; B unsigned char 类型的idx永远不可能大于等于256 C 每执行一次do循环k值加1,所以很快就会结束循环 D while语句后面没有任何表达式,直接跟一个分号,表示循环体任何事情都不做,但只要条件为真,循环会一直执行下去,即死循环。 |
答案 | 3 |
18 预处理
宏定义
C语言编译系统包括预处理,编译和链接等部分。
用#define来定义一个符号常量
用宏定义一条或多条语句
用宏来定义函数
#undef 是用来撤销宏定义的
ifndef防止头文件被重复包含和编译
宏运算
头文件
题目1 | 关于头文件包含,以下说法正确的是 |
选项1 | 复杂的头文件嵌套包含容易导致文件之间循环引用,引起死循环,造成编译错误,一旦出现非常难清理 |
选项2 | 头文件中禁止包含本文件不需要、但包含本头文件的.c文件需要的头文件 |
选项3 | 头文件的功能之一是提供对外接口原型 |
选项4 | 为简化头文件依赖,可以将其他子系统提供的外部头文件包含在当前子系统的外部头文件中 |
解析 | D选向违背规则PRE82-C 和PRE83-C |
答案 | 1,2,3 |
19 指针
指针常量与常量指针区别:
double * const cptr:const修饰cptr,cptr为指针,则表示指针不能变,则指向的地址不能变。
double const *cptr: const修饰*cptr,那么则表示cptr地址的内容不能改变。 const double *cptr与double const *cptr都表示cptr地址的内容不能改变。
题目 | 下面的代码是否正确() void GetMemory(char *p, int num) { p = (char *)calloc(sizeof(char), num); } int main() { char *str = NULL; GetMemory(str,10); if ( NULL != str) { strcpy(str,"hello"); free(str); } return 0; } |
选项1 | |
选项2 | |
选项3 | |
选项4 | |
解析 | malloc的内存的地址并没有赋值给str,无法使用,所以想这样获取一块内存是不行的。 |
答案 | 0 |
20 字符型数据
子串定义:串中任意个连续的字符组成的子序列
signed char的范围是[-128, 127]
unsigned char的范围是[0 255]
题目 | 以下代码的打印结果是? 说明:假设在linux gcc环境 int main() { signed char a = 63; signed char b = 65; signed char c = a + b; printf("%d,%d\n", c, a + b); return 0; } |
选项1 | 128,128 |
选项2 | 128,-128 |
选项3 | -128,128 |
选项4 | -128,-128 |
解析 | c发生溢出,signed char的范围是[-128, 127] |
答案 | 3 |
题目 | 如下代码执行的结果是: void MyAlloc(unsigned char **p, int size) { *p = (char*)malloc(size); } int main(void) { unsigned char *p = NULL; MyAlloc(&p, 10); strcpy (p, “hello”); printf(“%s\n”, p); return 0; } |
选项1 | hello |
选项2 | 无打印 |
选项3 | 可能崩溃 |
选项4 | 编译失败 |
解析 | 分配动态内存后必须判断是否成功后再使用 |
答案 | 3 |
题目 | 如下程序 int main() { char str[] = "abc"; str += 2; printf("%d\n",str[0]); return 0; } 程序执行结果是: |
选项1 | 97 |
选项2 | 98 |
选项3 | 99 |
选项4 | 程序出错 |
解析 | 数组名代表数组的地址,不能赋值 |
答案 | 4 |