Skip to content

Latest commit

 

History

History
503 lines (385 loc) · 19.6 KB

蓝奥声笔试题.md

File metadata and controls

503 lines (385 loc) · 19.6 KB

01

 假设 a 为常数 V1,b 为常数 V2,给出下面函数的执行结果
#include <stdio.h>
 
int  main(){
    int a = v1,b = v2;
    a = a^b;
    b = a^b;
    a = a^b;
    printf("a = %d, b = %d\n",a,b);
    return 0;
    
}


// 输出时 V2  V1
/*

异或 运算  
两个相同的数异或为0  0与任何数异或为任何数本身

*/

02

数组 G[1024][768]数据类型为 Uint8(无符号单字节整型),已存放1024x768 像素的图片灰度。
计算:
    1)所有像素平均灰度值 Gav; 
    2)大于 Gav 的像素总数量 N。
要求:
    1)写出完整的可运行代码;
    2)尽可能缩短代码运行时间。
/*第二题*/

#include <stdio.h>
//如果一张图片为白色,灰度值拉满
//黑色就是灰度值为零
#define WIDTH 1024 // 定义图像  宽度
#define HEIGHT 768 // 定义图像  高度
typedef unsigned char Uint8;// 定义无符号8位整型

// 像素灰度图像的平均灰度值和大于平均灰度值的像素总数量
//double* average表示指向double类型变量的指针,用于存储灰度图像的平均值。
//int* count表示指向int类型变量的指针,用于存储灰度图像的像素点数量。
void calculateGrayscaleMetrics(Uint8 image[HEIGHT][WIDTH], double* average, int* count) {
    int total = 0;
    *count = 0;//被设置为0的变量是由指针变量count指向的。这行代码通常用于将变量初始化为已知值,这里是0。
    // 像素灰度总和
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            total += image[i][j];
        }
    }
    // 平均灰度值
    //使用 *average 可以访问和修改该指针所指向的 double 类型变量的值。
    //不需要使用 (double *),是因为 average 已经是一个指向 double 类型变量的指针。
    //在这种情况下,我们只需使用 *average 来访问指针所指向的变量,而无需使用 (double *) 进行类型转换。 
    *average = (double)total / (WIDTH * HEIGHT);//将一幅图所有像素值加总除以总像素个数,得到平均灰度值。
    // 大于平均灰度值的像素数量
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            //image[i][j] 表示二维数组 image 中的特定像素值,而 *average 表示指针 average 所指向的平均灰度值。
            if (image[i][j] > *average) {//如果像素值大于平均灰度值,则将 *count 增加 1。
                //(*count)++ 表示递增指针 count 所指向的变量。它是一个指针解引用操作,用于访问和修改指针所指向的内存中的值。
                //因为 count 是一个指向整数类型变量的指针,所以使用 (*count) 可以访问和修改该指针所指向的整数类型变量的值。
                (*count)++;//通过使用 ++ 运算符,我们对变量的值进行递增操作。
            }
        }
    }
}
//Grayscal  灰度;
int main() {
    Uint8 image[HEIGHT][WIDTH];  // 像素灰度图像数组
    double average;              // 平均灰度值
    int count;                   // 大于平均灰度值的像素数量
    // 在此处填充像素灰度图像数据存储到image数组中
    calculateGrayscaleMetrics(image, &average, &count);

    printf("平均灰度值:%.2f\n", average);

    printf("大于平均灰度值的像素数量:%d\n", count);
    return 0;
}
/*
1    定义了图像的宽度和高度,分别为 1024 和 768。同时,
使用 typedef 定义了 Uint8 类型,表示无符号 8 位整数(范围为 0 到 255)。
命名空间污染:#define 定义的标识符没有作用域限制,它们是全局的。
这可能导致命名冲突和意外的替换。如果在代码的其他地方已经定义了同样的标识符,可能会产生意外的结果。

2    定义了函数 calculateGrayscaleMetrics,该函数接受一个二维数组 image,
表示灰度图像,以及两个指针参数 average 和 count,分别用于存储计算结果。

3    在 calculateGrayscaleMetrics 函数中,首先初始化变量 total 为 0,
用于累加像素值的总和,初始化变量 *count 为 0,用于统计高于平均值的像素数量。

使用嵌套循环遍历图像的每个像素,累加像素值到变量 total 中。

计算平均灰度值,将 total 除以图像的像素总数(即 WIDTH * HEIGHT),并将结果存储在变量 *average 中。

再次使用嵌套循环遍历图像的每个像素,如果像素值大于平均灰度值 *average,则将 *count 增加 1。

返回到 main 函数,声明了一个名为 image 的二维数组,用于存储灰度图像的像素值。

定义了变量 average 和 count,用于接收计算结果。

调用函数 calculateGrayscaleMetrics,传递图像数组 image 以及 &average 和 &count 的地址作为参数,
计算平均灰度值和高于平均值的像素数量。

最后,使用 printf 函数打印出计算结果,分别输出平均灰度值和高于平均值的像素数量。

整体思路是通过遍历图像的每个像素,累加像素值并计算平均值,然后再次遍历图像,
统计高于平均值的像素数量。这样可以得到图像的灰度统计信息。

*/
	

03

设异步串行发送数据的波特率为 19200,8bit 无校验方式。
接收端用某一 GPIO 下降沿中断时响应输入。
请使用软件模拟在发生上述下降沿中断时接收单字节或多字节串行数据,并存入自定义指针指向的缓冲区。
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "stm32f10x.h"
 
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 100
// 256 字节 或者 1k 
//volatile关键字来修饰rxBuffer数组,表示该数组是易变的,可能会被中断服务函数修改。
volatile uint8_t rxBuffer[BUFFER_SIZE]; // 接收缓冲区
volatile uint8_t rxIndex = 0;           // 接收缓冲区索引
volatile uint8_t *rxPtr = NULL;

//写一个指针,指向这个数组  把串口接收到的数据存到指针指向的数组里面,指针每次加一,指向下一个数组元素

//在响应时,可能接受多个数据 ,中断响应使能这个串口,

//下降沿触发,使能串口1
void EXTI2_IRQnHandler(void) // 外部中断 2 中断服务函数
{
    //先清除中断标志位
    EXTI_ClearITPendingBit(EXTI_Line2);
    USART_Cmd(USART1, ENABLE); // 使能串口1
}

//在串口中断服务函数里面,把接收到的数据存到指针指向的数组里面,指针每次加一,指向下一个数组元素
//只是把指针指向接受数组的起始位置(把数组的下表设置为零)  在串口的中断函数里面自增
void UART1_IRQHandler(void) // 串口1中断服务函数
{
    //先清除中断标志位
    USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        *rxPtr = USART_ReceiveData(USART1); // 将接收到的数据存到指针指向的数组里面
        rxPtr++; // 指针每次加一,指向下一个数组元素
        if (rxPtr >= rxBuffer + BUFFER_SIZE) // 判断指针是否越界
            rxPtr = rxBuffer; // 如果越界,指针指向数组的首地址
    }
}

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; // GPIO配置结构体

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // TX  RX
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;       // 速度50MHz
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);                  // 初始化GPIOA
}

void USART1_Init(void)
{
    USART_InitTypeDef USART_InitStructure; // 串口配置结构体
    NVIC_InitTypeDef NVIC_InitStructure;   // 中断配置结构体

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1时钟

    USART_DeInit(USART1); // 复位串口1

    USART_InitStructure.USART_BaudRate = 19200;                                     // 波特率19200
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     // 8位数据位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                          // 停止位1
    USART_InitStructure.USART_Parity = USART_Parity_No;                             // 无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 // 收发模式
    USART_Init(USART1, &USART_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
	NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
	//开启中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启中断

}
void EXTIX_Init(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // 开启 AFIO 时钟

    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2); 
    EXTI_InitStructure.EXTI_Line = EXTI_Line2;// 中断线 2
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;// 中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;// 使能中断线

    EXTI_Init(&EXTI_InitStructure);                              // 初始化中断线参数
    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;             // 使能外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; // 抢占优先级 2
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;        // 子优先级 2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;              // 使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);// 初始化 NVIC 参数
}


 int main(void)
 {	
	delay_init();	    //延时函数初始化	  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
    GPIO_Configuration();
    USART1_Init();
    EXTIX_Init();
	rxPtr = (uint8_t *)malloc(sizeof(uint8_t)*BUFFER_SIZE);
	rxPtr = rxBuffer; // 声明并初始化指针变量,指向数组的首地址
	while(1)
	{

	}
 }


 /**

// void UART1_IRQHandler(void) // 串口1中断服务函数
// {
//     if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
//     {
//         rxBuffer[rxIndex] = USART_ReceiveData(USART1);
//         rxIndex++;
//         if (rxIndex >= BUFFER_SIZE)
//             rxIndex = 0;
//     }
// }

**/

04

设*p0指向一个由若干字符串代码构成的先进先出的链表队列,所述队列初始为空,每个单元变量为一个字符串代码 X。请尝试以下数据处理,使得当新增一个新的字符串代码 X1 时:
1)查找判断 X1 是否已经存在于队列;
2)若不存在,将 X1 押人队列末位:若队列单元数量超过指定长度 N 则队列首个单元溢出;
3)已存在(Xi=X1),先删除队列中存在的代码 Xi,再将 X1 押人队列末尾;
4)尝试设计一个哈希索引表,进行快速查找 (代替顺序查找)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 链表节点结构
typedef struct Node {
    char code[20];//用于存储字符串的字符数组
    struct Node* next;
} Node;

// 创建节点
Node* createNode(const char* code) {
    //将 malloc 返回的内存地址强制转换为节点类型的指针。
    Node* newNode = (Node*)malloc(sizeof(Node));//返回分配内存的首地址
    //使用 strcpy 函数将参数 code 的字符串内容复制到新节点的 code 字符数组中。
    //这里假设节点结构体中有一个名为 code 的字符数组用于存储字符串。
    strcpy(newNode->code, code);
    newNode->next = NULL;//将新节点的 next 指针设置为 NULL,表示该节点没有指向其他节点。
    return newNode;//返回指向新节点的指针 newNode,以便在其他地方使用该节点。
}

// 在链表末尾插入节点
void insertEnd(Node** head, const char* code) {
    //调用了前面提到的 createNode 函数来创建一个新的节点,
    //并将其赋值给 newNode 变量。这个新节点将包含给定的代码字符串。
    Node* newNode = createNode(code);

    if (*head == NULL) {// 如果链表为空,将新节点 newNode 设置为链表的头节点。
        *head = newNode;
    } else {
        //创建一个临时指针变量 temp,并将其指向链表的头节点。
        //这个临时指针用于遍历链表,找到链表的末尾节点。
        Node* temp = *head;
        while (temp->next != NULL) {
            // 使用一个循环来遍历链表,条件是当前节点的 next 指针不为空。
            //这意味着只要当前节点不是链表的最后一个节点,就继续循环。
            temp = temp->next;
        } 
        //将新节点 newNode 连接到最后一个节点的 next 指针上,将新节点插入到链表的末尾。
        temp->next = newNode;
    }
}

// 查找节点
//接受一个链表的头节点指针 head 和一个字符串 code
int searchNode(Node* head, const char* code) {
//创建一个临时指针变量 temp,并将其初始化为链表的头节点。
//这个临时指针用于遍历链表,从头节点开始。
    Node* temp = head;
    while (temp != NULL) 
    //只要当前节点不是链表的最后一个节点,就继续循环。
    {//将当前节点的 code 字符串与给定的 code 字符串进行比较。
    //如果两个字符串相等(即返回值为0),表示找到了匹配的节点。
        if (strcmp(temp->code, code) == 0) {
            //如果找到了匹配的节点,函数返回1,表示节点存在于链表中。
            return 1; // 找到节点
        }//在每次循环结束时,将 temp 指针移动到链表的下一个节点,继续搜索下一个节点。
        temp = temp->next;
    }
    // 如果循环结束后仍未找到匹配的节点,
    //表示节点不存在于链表中,函数返回0,表示节点不存在。
    return 0; // 未找到节点
}

// 删除节点
void deleteNode(Node** head, const char* code) {
    Node* temp = *head;
    Node* prev = NULL;
    // 处理头节点
    if (temp != NULL && strcmp(temp->code, code) == 0) {
        *head = temp->next;
        free(temp);
        return;
    }
    // 遍历链表查找要删除的节点
    while (temp != NULL && strcmp(temp->code, code) != 0) {
        prev = temp;
        temp = temp->next;
    }
    // 删除节点
    if (temp != NULL) {
        prev->next = temp->next;
        free(temp);
    }
}

// 将新节点插入链表末尾,超过指定长度则溢出
void insertOrUpdateNode(Node** head, const char* code, int maxLength) {
    // 查找节点是否已存在
    if (searchNode(*head, code)) {
        // 若存在则先删除再插入
        deleteNode(head, code);
    } else if (maxLength > 0) {
        // 若不存在且指定长度大于0,则判断队列长度是否超过指定长度
        Node* temp = *head;
        Node* prev = NULL;
        int count = 0;
        while (temp != NULL) {
            prev = temp;
            temp = temp->next;
            count++;
        }
        if (count >= maxLength) {
            // 队列长度超过指定长度,删除队列首个节点
            Node* temp = *head;
            *head = (*head)->next;
            free(temp);
        }
    }
    // 将新节点插入链表末尾
    insertEnd(head, code);
}

// 打印链表
void printList(Node* head) {
    Node* temp = head;
    while (temp != NULL) {
        printf("%s ", temp->code);
        temp = temp->next;
    }
    printf("\n");
}

int main() {
    Node* head = NULL; // 链表头节点
    int maxLength = 5; // 指定队列长度

    insertOrUpdateNode(&head, "X1", maxLength);
    insertOrUpdateNode(&head, "X2", maxLength);
    insertOrUpdateNode(&head, "X3", maxLength);
    insertOrUpdateNode(&head, "X4", maxLength);
    insertOrUpdateNode(&head, "X5", maxLength);
    insertOrUpdateNode(&head, "X6", maxLength);
    insertOrUpdateNode(&head, "X2", maxLength);
    insertOrUpdateNode(&head, "X7", maxLength);

    printf("list : ");
    printList(head);

    return 0;
}




/*

设*p0指向一个由若干字符串代码构成的先进先出的链表队列,所述队列初始为空,每个单元变量为一个字符串代码 X。
请尝试以下数据处理,使得当新增一个新的字符串代码 X1 时:
1)查找判断 X1 是否已经存在于队列;
2)若不存在,将 X1 押人队列末位:若队列单元数量超过指定长度 N 则队列首个单元溢出;
3)已存在(Xi=X1),先删除队列中存在的代码 Xi,再将 X1 押人队列末尾:
4)尝试设计一个哈希索引表,进行快速查找 (代替顺序查找)。

这段代码实现了一个链表队列的数据结构,并提供了相应的操作函数来处理字符串代码的插入、查找和删除。

1. 定义了链表节点结构 `Node`,其中包含一个字符串类型的 `code` 字段和一个指向下一个节点的指针 `next`。

2. `createNode` 函数用于创建一个新的节点,将传入的字符串代码复制到节点的 `code` 字段中,
并将 `next` 指针初始化为 `NULL`。然后返回新创建的节点。

3. `insertEnd` 函数用于在链表的末尾插入一个新节点。它首先调用 `createNode` 函数创建一个新节点,然后判断链表是否为空。
如果链表为空,则将新节点设置为头节点。如果链表不为空,则通过遍历找到链表的最后一个节点,并将最后一个节点的 `next` 指针指向新节点。

4. `searchNode` 函数用于在链表中查找指定的代码。它遍历链表,逐个比较节点的 `code` 字段
与给定的代码字符串,如果找到了匹配的节点,则返回1表示找到。
如果遍历完整个链表仍未找到匹配的节点,则返回0表示未找到。

5. `deleteNode` 函数用于删除链表中的指定节点。它首先处理头节点的情况,如果头节点即为要删除的节点,
则将头节点指向下一个节点,并释放要删除的节点的内存空间。
然后遍历链表,找到要删除的节点的前一个节点和要删除的节点本身,将前一个节点的 `next` 
指针指向要删除节点的下一个节点,并释放要删除节点的内存空间。

6. `insertOrUpdateNode` 函数用于将新节点插入队列中,同时根据指定的长度控制队列的溢出。
它首先调用 `searchNode` 函数查找要插入的代码是否已经存在于队列中,
如果存在,则调用 `deleteNode` 函数将已存在的代码节点从队列中删除。然后根据指定的长度判断队列是否已满。
如果队列已满,则调用 `deleteNode` 函数删除队列的首个节点。
最后,将新节点插入队列的末尾。

7. `printList` 函数用于打印链表中所有节点的代码字段。它遍历链表,逐个输出节点的代码字符串。

8. 在 `main` 函数中,首先定义了链表队列的头节点指针 `head` 和指定的队列长度 `maxLength`。
然后通过调用 `insertOrUpdateNode` 函数来插入或更新代码节点。按照要求插入了一系列代码节点,并设置了指定长度为5。
最后,调用 `printList` 函数打印链表中所有节点的代码字段。

这段代码的思路是利用链表实现一个先进先出的队列结构,通过遍历链表来查找和删除指定的代码节点,并根据指定的长度控制队列的溢出。


*/

image-20230709100237457