C语言经典笔试题目分析(持续更新)

发布于:2025-05-16 ⋅ 阅读:(21) ⋅ 点赞:(0)

1. 描述下面代码中两个static 各自的含义

static void func (void)
{
	static unsigned int i;
}

  1. static void func(void) 中的 static
    作用对象:函数 func。

含义:
限制函数的作用域(链接属性),使其仅在当前源文件(.c 文件)内可见,其他源文件无法调用此函数。
未加 static 的函数默认为 extern(外部链接),其他文件可以通过声明调用;而 static 函数是“内部链接”的,仅用于文件内封装。

  1. static unsigned int i; 中的 static
    作用对象:局部变量 i。

含义:
延长变量的生命周期,使其从“自动存储期”变为“静态存储期”。
未加 static 的局部变量会在函数调用结束后销毁,而 static 变量会一直存在,直到程序结束。
特性:

变量 i 的作用域仍限制在函数 func 内部(只能在函数内访问)。

变量 i 的值会保留,每次调用 func 时,i 的值是上一次调用结束后的值。

变量 i 在程序启动时初始化为 0(若未显式初始化)。

总结

static 位置 作用对象 核心意义 影响范围
static void func(void) 函数 限制函数的作用域(内部链接) 文件级可见性
static unsigned int i; 局部变量 延长变量的生命周期(静态存储期) 变量的存储与持久化

2. 写出执行下面代码后变量a的值:

unsigned int a,b=3;
void move(unsigned int *p,unsigned int val)
{
	P = &val;
}

void main (void)
{
	a = b++;
	move(&a,b);
}

  1. 全局变量a和b的声明。b初始化为3,a未初始化,但因为是全局变量,默认初始化为0吗?或者可能在C语言中全局变量默认初始化为0?我记得是的,但这里a被显式赋值了,所以可能不需要考虑初始值。

  2. 主函数main开始执行。首先执行a = b++。这里的b++是后置递增,即先赋值b的当前值给a,然后b自增1。所以执行完这一步后,a应该是3,而b变成4。

  3. 接着调用move函数,传入&a(a的地址)和b的值(此时b是4)。在move函数内部,参数p接收的是a的地址,val接收的是4。然后在函数内部,执行p = &val。这里需要注意的是,p是一个指针变量,传递的是指针的副本。也就是说,在函数内部,p被改为指向val的地址,但这个改变不会影响到函数外部的指针,因为C语言是值传递,函数内部修改的是指针的副本,而不是原来的指针。

因此,move函数执行后,并没有改变a的值,因为函数内部只是修改了局部指针p的指向,而没有通过指针去修改a的值。所以a仍然保持原来的值,也就是3。

不过,这里可能存在一个误解:在函数move中,p被赋值为val的地址。但是val是一个局部变量,当函数move结束后,val的地址就不再有效了。即使通过p修改了指向的内容,但由于val已经不存在了,这可能导致未定义行为。不过在这个代码中,并没有通过*p来修改val的值,只是改变了p的指向。因此,实际上move函数并没有对a进行任何修改,所以a的值仍然保持为3。

3. 在32位的单片机系统中,下面的结构体长度是多少?

typedef struct {
short a;
char b;
char c;
int d;
}struct1;

typedef struct {
char a;
short b;
unsigned char c;
int d;
}struct2;

分析如下:

在 32 位单片机系统 中,结构体的长度由成员变量的内存对齐规则决定。以下是具体分析:

typedef struct {
    short a;    // 2 字节(对齐到 2 的倍数地址)
    char b;     // 1 字节(对齐到 1 的倍数地址)
    char c;     // 1 字节(对齐到 1 的倍数地址)
    int d;      // 4 字节(对齐到 4 的倍数地址)
} struct1;

内存布局分析:
short a:占 2 字节(地址 0-1)。

char b:占 1 字节(地址 2)。

char c:占 1 字节(地址 3)。

int d:需对齐到 4 的倍数地址。当前地址为 4(跳过 0-3),占 4-7 字节。

总长度:8 字节(无需额外填充,已满足最大对齐值 4 的倍数)。

typedef struct {
    char a;          // 1 字节(对齐到 1 的倍数地址)
    short b;         // 2 字节(对齐到 2 的倍数地址)
    unsigned char c; // 1 字节(对齐到 1 的倍数地址)
    int d;           // 4 字节(对齐到 4 的倍数地址)
} struct2;

内存布局分析:
char a:占 1 字节(地址 0)。

short b:需对齐到 2 的倍数地址。当前地址为 1,填充 1 字节到地址 2,占 2-3 字节。

unsigned char c:占 1 字节(地址 4)。

int d:需对齐到 4 的倍数地址。当前地址为 5,填充 3 字节到地址 8,占 8-11 字节。

总长度:12 字节(满足最大对齐值 4 的倍数)。

最终结果

结构体 总长度(字节) 关键对齐分析
struct1 8 成员顺序紧凑,填充较少。
struct2 12 因 short 和 int 对齐导致多次填充。

4.请使用 typedef定义一个数据类型 func_t为指向 void 型函数的函数指针,再使用此数据类型定义一个指向 void 型函数的函数指针,并通过此指针来调用函数 test。

typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值为int的函数指针类型

typedef_______
void test(void);
void main()
{
}

代码实现:

#include <stdio.h>

// 使用 typedef 定义函数指针类型 func_t
typedef void (*func_t)(void);

// 定义目标函数 test
void test(void) {
    printf("Test function called.\n");
}

int main() {
    // 声明 func_t 类型的函数指针并指向 test
    func_t func_ptr = test;  // 等价于 func_ptr = &test

    // 通过函数指针调用 test
    func_ptr();              // 等价于 (*func_ptr)();

    return 0;
}

5.请编写宏定义实现以下功能:
1)将无符号整数a的第1位置1,同时保证其它位的值不改变;
2)将无符号整数b的第5位清0,同时保证其它位的值不改变;
3)计算出任意结构体类型的常数组(如struct tt tab[])的元素个数

#include <stdio.h>

// 宏定义
#define SET_BIT1(a)    ((a) |= (1U << 1))      // 第 1 位置 1
#define CLEAR_BIT5(b)  ((b) &= ~(1U << 5))     // 第 5 位清 0
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) // 计算数组元素个数

int main() {
    // 测试置位和清零
    unsigned int a = 0;
    SET_BIT1(a);
    printf("a = 0x%08X\n", a); // 输出 0x00000002

    unsigned int b = 0xFFFFFFFF;
    CLEAR_BIT5(b);
    printf("b = 0x%08X\n", b); // 输出 0xFFFFFFDF

    // 测试数组元素个数计算
    struct tt { int x; char y; };
    struct tt tab[] = { {1, 'a'}, {2, 'b'}, {3, 'c'} };
    printf("Array size: %zu\n", ARRAY_SIZE(tab)); // 输出 3

    return 0;
}

1U << 1:生成掩码 0b00000010(假设为 8 位)。

注意事项

  1. 位操作宏:

    • 1U 确保掩码为无符号整数,避免符号扩展问题。

    • 括号包裹参数 (a) 和 (b),防止宏展开时的运算符优先级错误。

  2. 数组大小宏:

    • 仅适用于静态数组,若传入指针(如函数参数退化的指针),宏会失效。

    • arr 必须为数组名,不能是指针或动态分配的内存。

6.请按照说明实现下面的函数:

/*功能:把十六进制数转换为字符,如0xA8转换为字母A和数字8
*参数:hex是待转换的十六进制数:char1和char2是转换后的字符的存储指针
*返回值:返回0表示转换成功,返回-1表示参数错误或转换失败*/
int hex_to_chars(unsigned char hex,char *charl, char *char2)
{
}

实现:

#include <stdio.h>

int hex_to_char(unsigned char hex, char *char1, char *char2) {
    // 检查指针有效性
    if (char1 == NULL || char2 == NULL) {
        return -1;
    }

    // 检查hex是否为有效单字节十六进制数(0x00~0xFF)
    if (hex > 0xFF) {
        return -1;
    }

    // 处理高四位
    unsigned char high = (hex >> 4) & 0x0F;
    // 处理低四位
    unsigned char low = hex & 0x0F;

    // 转换为字符
    *char1 = (high < 10) ? ('0' + high) : ('A' + high - 10);
    *char2 = (low < 10)  ? ('0' + low)  : ('A' + low - 10);

    return 0;
}

/* 使用示例 */
int main() {
    char c1, c2;
    unsigned char test_num = 0xA8;

    if (hex_to_char(test_num, &c1, &c2) == 0) {
        printf("0x%02X -> '%c' '%c'\n", test_num, c1, c2); // 输出:0xA8 -> 'A' '8'
    }

    // 错误测试:传入空指针
    if (hex_to_char(0x12, NULL, &c2) == -1) {
        printf("Invalid parameters!\n");
    }

    return 0;
}

代码说明:

  1. 参数校验

    • 首先检查char1和char2是否为NULL,避免空指针解引用。

    • 检查hex是否超过单字节范围(0x00~0xFF),若超过则返回错误。

  2. 位操作提取高低四位

    • high = (hex >> 4) & 0x0F:右移4位获取高四位并屏蔽其他位。

    • low = hex & 0x0F:直接屏蔽高四位获取低四位。

  3. 数值转字符

    • 0-9:直接转换为字符’0’-‘9’(‘0’ + value)。

    • 10-15:转换为大写字母’A’-‘F’(‘A’ + value - 10)。

  4. 返回值

    • 成功返回0,失败返回-1(参数错误或数值越界)。

网站公告

今日签到

点亮在社区的每一天
去签到