【嵌入式 C 语言期中测试】高频考点解析 + 编程题实战(含答案与思路)

34 阅读12分钟

【嵌入式 C 语言期中测试】高频考点解析 + 编程题实战(含答案与思路)

大家好,我是学嵌入式的小杨同学。嵌入式 C 语言是嵌入式开发的基础,期中测试往往能集中暴露知识点漏洞 —— 比如运算符特性、指针操作、关键字用法、字符串处理等高频考点。今天就结合这份期中测试题,逐题解析核心考点,手把手讲解编程题的实现思路,帮你吃透嵌入式 C 语言的核心知识点,轻松应对考试和实际开发。

一、选择题高频考点解析(1-7 题)

选择题主要考察基础语法细节和底层逻辑,错题往往集中在 “指针步长”“数据类型兼容”“逻辑运算符优先级” 等易混淆点,逐题拆解如下:

1. 运算符适用类型(第 1 题)

题目:运算对象必须是整型的运算符是(A)A. % B. / C. == D. <=考点:取模运算符%的特性解析

  • %(取模)用于计算余数,仅支持整型(int、long 等),若传入浮点型会编译报错;
  • /(除法)支持整型和浮点型(整型除法会舍弃小数);
  • ==(等于)、<=(小于等于)是关系运算符,支持任意算术类型(整型、浮点型)。

2. 字符常量与字符串常量(第 2 题)

题目:下列为字符常量的是(C)A.“a” B. ‘ab’ C. ‘\n’ D. b考点:字符常量的语法规则解析

  • 字符常量用单引号' '包裹,且仅能包含 1 个字符(普通字符或转义字符);
  • A 是字符串常量(双引号包裹),B 是非法字符常量(单引号内 2 个字符),D 是标识符(变量名);
  • C 是转义字符(换行符),属于合法字符常量。

3. 指针步长计算(第 3 题)

题目:64 位 CPU 中,long *p = (long *)0x1000; p++; 输出p的结果是(D)A. 0x1000 B. 0x1001 C. 0x1004 D. 0x1008考点:指针自增的步长规则解析

  • 指针自增 / 自减的步长 = 指向数据类型的大小;
  • 64 位系统中long类型占 8 字节,p++会让指针地址增加 8,因此0x1000 + 8 = 0x1008
  • 若为 32 位系统(long占 4 字节),结果为 0x1004,需注意 CPU 位数对数据类型大小的影响。

4. 有符号与无符号整数比较(第 4 题)

题目int a=-1; unsigned int b=-1; int c=a==b?1:0; 则 c 的值是(C)A. -1 B. 0 C. 1 D. 表达式不合法考点:混合类型比较的隐式转换解析

  • 有符号整数(int)与无符号整数(unsigned int)比较时,有符号整数会隐式转换为无符号整数;
  • a=-1(int)转换为无符号整数时,按补码规则存储为最大值(如 32 位系统为0xFFFFFFFF);
  • b=-1(unsigned int)存储值也是0xFFFFFFFF,因此a==b为真,c=1。

5. 逻辑运算符优先级(第 5 题)

题目x1=0,x2=0,x3=1; if(x1 && x2 | x3) 输出结果是(A)A. + + + + + B. * * * * * C. 无输出 D. 语法错误考点:逻辑运算符的优先级(&&高于|解析

  • 逻辑与&&优先级高于逻辑或|,表达式等价于(x1 && x2) | x3
  • x1 && x20 && 0 = 0,再与x3=1进行|运算,结果为0 | 1 = 1? 此处需注意:原解析有误!实际x1 && x2为 0,0 | x3=1,if 条件为真,应输出* * * * *? 重新核对:纠正:逻辑运算符中,&&优先级高于||,但题目中是|(按位或),按位或与逻辑与优先级相同,从左到右计算:x1 && x2 = 00 | x3 = 1,if 条件为真,输出* * * * *? 但题目给出答案为 A,可能题目中是||(逻辑或),需注意题目符号差异 —— 核心考点是 “运算符优先级和短路求值”。

6. 异或交换法(第 6 题)

题目:异或交换a=0x1111,b=0x3333,最终a的值是(C)A. 0x1111 B. 0x2222 C. 0x3333 D. 0x0考点:异或运算的特性(a^a=0a^0=a解析

  • 异或交换无需临时变量,步骤如下:

    1. a = a^ba=0x1111^0x3333=0x2222
    2. b = a^bb=0x2222^0x3333=0x1111(恢复原 a 的值);
    3. a = a^ba=0x2222^0x1111=0x3333(恢复原 b 的值);
  • 最终ab交换,a=0x3333

7. sizeof 运算符计算(第 7 题)

题目char str[]="Hello"; char *p=str; int n=10;sizeof(str)=6sizeof(p)=8sizeof(n)=4考点sizeof对数组、指针、基本类型的计算规则解析

  • str[]="Hello":数组包含 5 个有效字符 + 1 个字符串结束符'\0',总大小为 6;
  • char *p:64 位系统中指针占 8 字节(32 位占 4 字节),与指向的数据类型无关;
  • int n:32/64 位系统中int均占 4 字节,是固定大小。

二、简答题核心考点(8-10 题)

简答题考察对关键字、参数传递、指针定义的理解,答案需简洁准确,直击要点:

8. static、extern、const 关键字的作用

考点:关键字对作用域、生命周期、变量可修改性的影响标准答案

  • static

    1. 修饰局部变量:延长生命周期(从栈区移到静态存储区,程序结束后释放),不改变作用域;
    2. 修饰全局变量 / 函数:限制作用域为当前文件(私有化),禁止其他文件通过extern调用;
  • extern

    1. 声明外部全局变量 / 函数(仅声明,不定义),允许跨文件调用;
    2. 无定义功能,需确保被声明的变量 / 函数在其他文件中已定义;
  • const

    1. 修饰变量:变量值不可修改(只读变量),需初始化;
    2. 修饰指针:const int *p(指针指向的值不可改)、int *const p(指针本身不可改);
    3. 修饰函数参数 / 返回值:限制参数 / 返回值不可被修改,提升代码安全性。

9. 形参和实参的区别,是否可以重名?

考点:参数传递的本质和命名规则标准答案

  • 区别:

    1. 定义位置:形参在函数定义时声明(如void func(int x)中的x),实参在函数调用时传入(如func(5)中的5);
    2. 内存分配:形参在函数调用时分配内存,函数执行完毕后释放;实参内存由定义位置决定(全局变量在静态区,局部变量在栈区);
    3. 传递方式:实参通过 “值传递” 或 “地址传递” 给形参(形参是实参的副本,值传递不影响实参,地址传递可修改实参);
  • 能否重名:可以重名。形参的作用域是函数内部,与函数外部的实参(变量)属于不同作用域,互不影响。

10. 用变量 a 给出指定定义(8 种场景)

考点:指针、数组、函数指针的复杂定义语法标准答案:a) 一个整型数 → int a;b) 指向整型数的指针 → int *a;c) 指向指针的指针(指向整型数) → int **a;d) 10 个整型数的数组 → int a[10];e) 10 个指针的数组(指针指向整型数) → int *a[10];(数组优先级高于*,先解析为数组)f) 指向 “10 个整型数数组” 的指针 → int (*a)[10];(括号提升*优先级,先解析为指针)g) 指向函数的指针(函数有 1 个整型参数,返回整型) → int (*a)(int);h) 10 个指针的数组(指针指向 “有 1 个整型参数、返回整型的函数”) → int (*a[10])(int);

三、编程题实战解析(3 道核心题)

编程题考察 “指针操作”“字符串处理”“逻辑设计”,题目均要求避免使用标准库函数,需手动实现核心逻辑,是嵌入式开发的高频实战场景。

1. 手动实现字符串四大核心函数(指针形式)

题目:不使用<string.h>函数,用指针实现strcpy(复制)、strcat(拼接)、strcmp(比较)、strlen(长度)。核心思路:基于指针遍历字符串,依赖'\0'作为字符串结束标志。

完整实现代码

c

运行

#include<stdio.h>

// 1. 字符串复制:将s复制到d(d需足够大)
char* Strcpy(char *d, const char *s) {
    if (d == NULL || s == NULL) return NULL; // 判空,避免野指针
    char *p = d; // 保存d的原始地址(后续d会移动)
    while (*s != '\0') { // 遍历到s的结束符
        *d = *s; // 逐字符复制
        d++;
        s++;
    }
    *d = '\0'; // 手动添加d的结束符
    return p;
}

// 2. 字符串拼接:将s拼接到d末尾
char* Strcat(char *d, const char *s) {
    if (d == NULL || s == NULL) return NULL;
    char *p = d;
    while (*d != '\0') { // 遍历到d的末尾
        d++;
    }
    while (*s != '\0') { // 从d末尾开始复制s
        *d = *s;
        d++;
        s++;
    }
    *d = '\0'; // 手动添加结束符
    return p;
}

// 3. 字符串长度:返回有效字符个数(不含'\0')
int Strlen(char *d) {
    if (d == NULL) return 0;
    int len = 0;
    while (*d != '\0') { // 遍历到结束符
        len++;
        d++;
    }
    return len;
}

// 4. 字符串比较:按ASCII码比较,返回差值(d>s返回正,相等返回0,否则负)
int Strcmp(char *d, char *s) {
    if (d == NULL || s == NULL) return -1; // 异常返回-1
    while (*d != '\0' && *s != '\0') { // 遍历到任一字符串结束
        if (*d != *s) {
            return *d - *s; // 不相等则返回差值
        }
        d++;
        s++;
    }
    return *d - *s; // 处理长度不同的情况(如"abc"和"abcd")
}

// 测试函数
int main() {
    char a[1024] = {0};
    const char *s1 = "helloworld";
    const char *s2 = "12345";
    char *s3 = "helloworld12345";
    char *s4 = "helloworld67890";

    // 测试Strcpy
    Strcpy(a, s1);
    printf("1.Strcpy: 复制后a=%s\n", a);

    // 测试Strlen
    int len = Strlen(a);
    printf("2.Strlen: a的长度=%d\n", len);

    // 测试Strcat
    Strcat(a, s2);
    printf("3.Strcat: 拼接后a=%s\n", a);

    // 测试Strcmp
    int cmp_res1 = Strcmp(a, s3);
    int cmp_res2 = Strcmp(a, s4);
    printf("4.Strcmp: a与s3比较结果=%d\n", cmp_res1);
    printf("   Strcmp: a与s4比较结果=%d\n", cmp_res2);

    return 0;
}

关键考点
  • 指针遍历字符串的核心是*str != '\0'
  • StrcpyStrcat必须手动添加'\0',否则字符串不合法;
  • Strcmp需处理 “长度不同但前缀相同” 的场景(如 “abc” 和 “abcd”)。

2. 统计字符出现次数(不区分大小写)

题目:输入字母数字字符串和目标字符,输出目标字符的出现次数(不区分大小写)。核心思路:将字符串和目标字符统一转为大写 / 小写,再遍历统计。

完整实现代码

c

运行

#include<stdio.h>
#include<string.h>

// 统计字符出现次数(不区分大小写)
int count_char(const char *str, char c) {
    int count = 0;
    if (str == NULL) return 0;

    while (*str != '\0') {
        char current = *str;
        char target = c;

        // 统一转为小写(也可转为大写)
        if (current >= 'A' && current <= 'Z') {
            current += 32; // 'A'+'a'的ASCII差值为32
        }
        if (target >= 'A' && target <= 'Z') {
            target += 32;
        }

        // 匹配则计数
        if (current == target) {
            count++;
        }
        str++; // 指针后移
    }
    return count;
}

int main() {
    char a[1024];
    char target;

    // 输入字符串(支持空格?题目说“字母和数字组成”,无空格)
    printf("请输入一段字符:\n");
    fgets(a, sizeof(a), stdin);
    a[strcspn(a, "\n")] = '\0'; // 截断换行符

    // 输入目标字符
    printf("请输入要统计的单个字符:");
    scanf("%c", &target);

    // 统计并输出
    int count = count_char(a, target);
    printf("字符'%c'在字符串中出现了%d次(不区分大小写)\n", target, count);

    return 0;
}

关键考点
  • 大小写转换的核心是 ASCII 码差值(A=65,a=97,差值 32);
  • fgets读取字符串后需截断换行符(strcspn函数);
  • 指针遍历字符串时无需知道长度,依赖'\0'结束。

3. 统计字符串中的单词个数(空格分隔)

题目:输入字符串,统计单词个数(单词之间用空格隔开,最大长度 1024)。核心思路:用状态标记法(in_word),仅在 “空格→非空格” 时计数。

完整实现代码

c

运行

#include<stdio.h>
#include<string.h>

// 统计单词个数(空格分隔)
int count_words(const char *str) {
    if (str == NULL) return 0;

    int count = 0;
    int in_word = 0; // 0:在单词外(空格),1:在单词内

    while (*str != '\0') {
        if (*str == ' ') {
            in_word = 0; // 遇到空格,标记为单词外
        } else if (in_word == 0) {
            count++; // 从单词外→单词内,计数+1
            in_word = 1; // 标记为单词内
        }
        str++; // 指针后移
    }
    return count;
}

int main() {
    char a[1024];

    printf("请输入字符串:");
    fgets(a, sizeof(a), stdin);
    a[strcspn(a, "\n")] = '\0'; // 截断换行符

    int count = count_words(a);
    printf("单词个数:%d\n", count);

    return 0;
}

关键考点
  • 状态标记法避免 “多个连续空格” 误判为多个单词;
  • 核心逻辑:仅当 “非空格字符且当前在单词外” 时,才视为新单词的开始。

四、总结:嵌入式 C 语言学习重点

这份期中测试题覆盖了嵌入式 C 语言的核心考点,错题往往集中在:

  1. 指针操作(步长、复杂定义);
  2. 数据类型与运算符(取模、异或、混合类型比较);
  3. 字符串处理(依赖'\0'、指针遍历);
  4. 关键字作用(static、extern、const 的细节)。

学习建议:

  • 基础语法:重点掌握 “指针 + 数组 + 关键字”,多做底层逻辑题(如指针步长、类型转换);
  • 编程实战:多手动实现字符串函数、数据结构(链表、栈),避免过度依赖标准库;
  • 嵌入式适配:关注 32/64 位系统差异(指针大小、long 类型)、内存分配(栈 / 静态区)。

掌握这些知识点,不仅能应对期中测试,更能为嵌入式开发中的驱动编写、数据处理打下坚实基础。我是学嵌入式的小杨同学,关注我,后续会分享更多嵌入式实战技巧!