❤️作者主页:嵌入式_源
❤️作者简介:嵌入式软件工程师,记录学习
❤️点赞 👍 收藏 ⭐再看,养成习惯
1.实现一个函数,可以左旋字符串中的k个字符
例如:
ABCD左旋一个字符可以得到BCDA
ABCD左旋两个字符可以得到CDAB
void left_move(char* arr,int num)// 暴力求解
{
assert(arr);
int i=0;
int j=0;
char temp=0;
int len=strlen(arr);
for(i=0;i<num;i++)
{
//存储第一个字符
temp=*arr;
//挪位置
for(j=0;j<len-1;j++) //例如arr[9],从arr[0]~arr[8]
{
*(arr+j)=*(arr+j+1);
}
*(arr+len-1)=temp;
}
}
//三步 翻转法
//bacdef 对要开始翻转的字符左侧翻转
//bafedc 对要开始翻转的字符右侧翻转
//cdefab 对整体翻转
void reverse(char *left,char *right)
{
assert(left!=NULL);assert(right!=NULL);
char temp=0;
while(left<right)
{
temp=*left;
*left=*right;
*right=temp;
left++;right--;
}
}
void left_move(char* arr,int k)//把字符串左旋问题转化成字符串翻转
{
assert(arr);
int len=strlen(arr);
reverse(arr,arr+k-1);
reverse(arr+k,arr+len-1);
reverse(arr,arr+len-1);
}
int main()
{
char arr[]="abcdef";
left_move(arr,2);
printf("%s\n",arr);
return 0;
}
2.输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。 序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1:
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2: 输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
3.实现一种算法,删除单向链表中间的某个节点(除了第一个和最后一个节点,不一定是中间节点),假定你只能访问该节点。
示例: 输入:单向链表 a->b->c->d->e->f中的节点c 结果:不返回任何数据,但该链表变为a->b->d->e->f
struct Listnode
{
int val;
struct Listnode* next;
};
void deleteNode(struct Listnode* Node)
{
if (Node == NULL || Node->next == NULL) {
return;
}
/*我们定义一个指针 nextNode,指向要删除节点 node的下一个节点 */
struct Listnode* nextNode = Node->next;
/*将要删除节点 node 的值更新为下一个节点 nextNode 的值。
这样,当前节点的值就被“覆盖”为下一个节点的值*/
Node->val = nextNode->val;
/* 将要删除节点 node 的指针指向下下一个节点,跳过了下一个节点 nextNode。
这样,实际上是将当前节点直接链接到了下下个节点,相当于删除了中间的节点*/
Node->next = nextNode->next;
/*释放了原本下一个节点 nextNode 的内存空间。因为我们已经成功将当前节点的值和指针进行了更新,
所以可以安全地释放掉原下一个节点的内存,防止内存泄漏*/
free(nextNode);
}
struct Listnode* createNode(int val)
{
struct Listnode* newNode = (struct Listnode*)malloc(sizeof(struct Listnode));
if (newNode != NULL)
{
newNode->val = val;
newNode->next = NULL;
}
return newNode;
}
int main()
{
struct Listnode* a= createNode(1);
struct Listnode* b = createNode(2);
struct Listnode* c = createNode(3);
struct Listnode* d = createNode(4);
struct Listnode* e = createNode(5);
struct Listnode* f = createNode(6);
a->next = b;
b->next = c;
c->next = d;
d->next = e;
e->next = f;
f->next = NULL;
deleteNode(c);
struct Listnode* current = a;
while (current != NULL)
{
printf("%d ", current->val);
current = current->next;
}
free(a); free(b); free(d); free(e); free(f);
return 0;
}
4.用预处理指令交换两个参数的值
#define SWAP(x, y) do { typeof(x) temp = x; x = y; y = temp; } while (0)
typeof
是一个在许多编程语言中常见的操作符,用于获取给定变量或值的类型信息
5. 简述代码编译后生成的map文件里面的内容?
-
Section Cross References:各文件模块中函数的交叉引用
-
Removing Unused input sections from the image:移除未调用模块
-
Image Symbol Table:映射符号表
-
Memory Map of the image:内存(映射)分布
-
Image component sizes:存储组成大小
6.在数据通信过程中,设置某普通串口的波特率为115200,则此串口每秒能传输多少KB数据。写出推导过程。
串口的波特率指的是每秒钟传输的比特数。如果串口的波特率为115200,那么这个串口每秒能够传输115200个比特。
要计算传输的数据量,需要考虑到每个字节包含8个比特(1字节=8比特)。因此,可以使用以下公式来计算每秒传输的数据量(以KB为单位):
传输数据量(KB)= (波特率 / 8) / 1024
将波特率115200代入公式计算:
传输数据量(KB)= (115200 / 8) / 1024 ≈ 14.0625 KB/s
7.如下代码的输出是什么?
x=y= 10;
z= ++x ||++y;
printf("x=%d, y=%d, z=%d", x, y, z);
在 C 语言中,逻辑或运算符 || 的计算规则是:如果第一个操作数为真(非零),则结果为真(1),不会继续计算第二个操作数;如果第一个操作数为假(0),则继续计算第二个操作数,最终结果取决于第二个操作数。
现在来分析代码的执行过程:
- 首先将 x 和 y 的值都设置为 10。
- 执行表达式 z = ++x || ++y;。
- 由于 ++x 的结果为 11(非零,真),所以不会继续计算 ++y,整个表达式的结果为真(1),赋值给 z。
8.如下代码会有什么问题?为什么?
typedef enum
{
eData0 = 0,
eData1,
eData2,
} eTestData_t;
#if eData1
void doSomething(void)
...
#endif
- 在定义枚举类型时,最后一个枚举值
eData2
后多余的逗号会导致语法错误。 - 在条件编译指令
#if eData1
中,eData1
是一个枚举值,并不是一个预处理宏,因此无法直接在条件编译中使用枚举值。
9.写出你熟悉的一个嵌入式芯片的型号、性能指标及资源分布情况。
STM32F103C8T6
- 处理器:ARM Cortex-M3 内核,最高频率为 72 MHz。
- 存储器:
- Flash 存储器:64 KB
- RAM:20 KB
- 通信接口:包括 2个SPI、2个I2C、3个USART、CAN 等常见接口。
- 模拟/数字转换器:具有12位的ADC,多个通道。
- 定时器:高级定时器TIM1,通用定时器TIM2,TIM3,TIM4。
- GPIO:通用输入输出引脚,用于连接外部设备和传感器。
10.写一段程序,输入正整数n (n<100),输出n的阶乘
#include <stdio.h>
// 计算阶乘的函数
int factorial(int n) {
if (n == 0 || n == 1) {
return 1;
} else {
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
}
int main() {
int n;
printf("请输入一个正整数n(n<100):");
scanf("%d", &n);
if (n < 0 || n >= 100) {
printf("输入的数不符合要求\n");
} else {
printf("%d的阶乘是:%d\n", n, factorial(n));
}
return 0;
}
11.请使用单片机设计下面的产品,给出初步的设计方案,包括大概的原理图、软件流 程图,主芯片可以随意选择。 一款温度表,用于检测室温,12V 电源供电,二位数码管显示温度,热敏电阻测温。温度显示范围:0~99 摄氏度,显示精度:+-1摄氏度。 (注:需要写出通过AD 采样转化温度值的过程)
初步设计方案
一、硬件设计
1. 主芯片选择
选用STM32F103C8T6作为主芯片,该芯片功能强大、性能稳定,内置ADC转换器,适用于温度检测与显示。
2. 原理图设计
- 电源电路:将12V电源通过降压模块(如LM2596)转换为STM32F103C8T6所需的3.3V电源。
- 热敏电阻测温电路:热敏电阻与固定电阻串联,形成一个分压电路,将温度变化转换为电压变化。
- ADC采样电路:利用STM32F103C8T6内置的ADC转换器对热敏电阻两端的电压进行采样。
- 数码管显示电路:采用两位共阳或共阴数码管显示温度值,需要驱动电路(如74HC595)来控制数码管的显示。
3. 元件选择
- 热敏电阻:选用合适的NTC或PTC热敏电阻,其阻值随温度变化明显。
- 数码管:选用两位共阳或共阴数码管,并配上适当的驱动电阻。
- 固定电阻:与热敏电阻串联,形成分压电路。
二、软件设计
1. 软件流程图
[开始] | V 系统初始化(配置GPIO、ADC等) | V 启动ADC转换,读取热敏电阻电压值 | V 根据电压-温度曲线转换温度值 | V 检查温度值是否在0~99摄氏度范围内 / \ 是 否 | | V X(错误处理) 将温度值显示在数码管上 | V 延时一段时间(如1秒) | V 返回启动ADC转换步骤(循环检测)
2. 关键代码实现
- 系统初始化:初始化GPIO为输出模式以驱动数码管,配置ADC参数(如分辨率、采样时间等)。
- ADC采样:启动ADC转换,读取热敏电阻两端的电压值。
- 温度转换:根据预先通过实验或数据手册获取的电压-温度曲线,将ADC采样得到的电压值转换为温度值。
- 数码管显示:将温度值转换为数码管可显示的编码,并通过GPIO控制数码管显示。
三、AD采样转化温度值过程
- 配置ADC:设置ADC的工作模式、采样速率、分辨率等参数。
- 启动ADC转换:启动ADC转换,等待转换完成。
- 读取ADC值:从ADC寄存器中读取转换得到的电压值(通常是12位数字量)。
- 电压计算:根据ADC的参考电压(如3.3V)和ADC值,计算出热敏电阻两端的实际电压。
- 温度转换:根据热敏电阻的电压-温度曲线(可能是线性的,也可能是非线性的),将电压值转换为对应的温度值。
四、注意事项
- 校准:由于热敏电阻的阻值可能随环境变化或老化而发生变化,因此需要对电压-温度曲线进行校准,以确保测量精度。
- 误差处理:当ADC转换失败或计算得到的温度值超出范围时,应进行相应的错误处理。
- 电源稳定性:确保12V电源的稳定性,以避免因电源波动引起的测量误差
12.去除字符串里面的空格
void removeSpaces(char* str) {
int i, j = 0;
for (i = 0; str[i]; i++) {//当当前迭代到的字符为字符串结束符 '\0' 时,循环停止
if (str[i] != ' ') {
str[j++] = str[i];
}
}
str[j] = '\0';
}
int main() {
char str[100];
printf("请输入一个带有空格的字符串:");
fgets(str, sizeof(str), stdin);
// 清除fgets()函数在字符串末尾添加的换行符
if (str[strlen(str) - 1] == '\n') {
str[strlen(str) - 1] = '\0';
}
printf("原始字符串:%s\n", str);
removeSpaces(str);
printf("去除空格后的字符串:%s\n", str);
return 0;
}