文章目录
问题一:使用宏定义完成两个数据的交换
一开始,由于小编过于紧张+想多了😅😅😅,然后给技术官的结果就是这样子的:
#define swap(a,b) \
int c = a; \
a = b; \
b = c; \
咋一看是不是没啥毛病🤣🤣🤣。但是呢,习惯上后面那三句话是需要括号的,就像这样子:
#define swap(a,b) \
{ \
int c = a; \
a = b; \
b = c; \
}
个人点评
上面的交换还借助了临时变量,一般情况在函数中看起来很正常,但在宏定义中看起来是不是奇奇怪怪的呢?🤔🤔🤔于是乎,在面试结束后,小编又使用另外的方法实现了交换功能。(其实在面试过程中,小编也想到这种方式,但是由于很久没有使用过了,因此不敢确定不敢写😂😂😂)
使用异或实现数据交换:
#define swap(a,b) \
{ \
a = a^b; \
b = a^b; \
a = a^b; \
}
假如使用该宏定义实现整型数据5、6的交换,那么其过程如下:
使用加法减法实现数据交换:
&esmp;使用加减法实现数据交换类似上面的使用三次异或实现数据交换,其实现过程中含有一次加法、两次减法,具体宏定义实现如下:
#define swap2(a,b) \
{ \
a = a+b; \
b = a-b; \
a = a-b; \
}
实现过程图解如下:
问题二:制作一个函数接口判断函数参数输入是否符合要求,如果符合要求就返回部分输入,如果不符合就返回no result
详细问题描述
使用C语言实现一个接口,接口的输入数据是函数的参数,函数需要实现如果这个输入符合xxxxx.bin的格式,那么就返回xxxx(也就是.bin前面的数据),否则返回"no result".
解决方案:
在将述正式解决方案之前,小编需要先讲述一个向main函数中传参的方法,该方法主要运用于终端运行编译后可执行文件。
话不多说,先给大家看函数吧!
#include <stdio.h>
int main(int arc,char*arg[])
{
for(int i=0;i<arc;++i)
printf("%s ",arg[i]);
printf("\n");
return 0;
}
该函数是一个典型的主函数传参函数,而且函数实现的功能是打印主函数的所有参数,如果我们使用 .\ target.exe “hello word!”
(假设编译后生成的文件为target.exe),那么其结果就会打印 绝对路径+.\ target.exe 以及 “hello word!”。如下图:
这里也就是说从执行命令开始算第一个输入参数(包括执行命令),那么大家可能会想,如果我们需要获取某一个参数怎么办?🤔🤔🤔
当然也是有方法的啦!主函数的参数int arc
表示参数的个数,char*arg[]
是一个数组,里面包含了所有参数。也就是说,如果我们想要获取第i个参数,那么我们可以使用 arg[i]
来获取。
在讲述正式题解之前,小编还想给大家介绍一个函数——字符串复制函数void *memcpy(void *str1, const void *str2, size_t n);
(今天面试的时候一直想用,但是忘记咋用的了😂😂😂)
void *memcpy(void *str1, const void *str2, size_t len)
,是一个字符串复制函数,参数1:str1表示结果字符串,参数2:str2表示源字符串,参数2:len表示复制长度。虽然这个简单,但是请大家在函数调用完成后别忘记最好还是手动加上一个字符串结束符’\0’,当然了,如果是整个字符串完全复制就大可不必了。使用示例如下:
#include <stdio.h>
#icnlude <string.h>
int main(void)
{
char src[50] = "123456789",dest[10];
int len = strlen(src);
// 从起始位置开始复制 (len-4)个字符
memcpy(dest, src, len-4);
dest[len-4] = '\0';
printf("dest = %s\n", dest);
// 从2号位开始复制 (len-4)个字符
memcpy(dest, src+2*sizeof(char), len-4); // 注意这里的*sizeof(char)可省略
dest[len-4] = '\0';
printf("dest = %s\n", dest);
return 0;
}
其结果如下:
好了好了,现在咱来说说正式题解吧!🤣🤣🤣
先上代码:
#include <stdio.h>
#include <string.h>
char *check(char str[])
{
int len = strlen(str);
if(len < 4) return (char*)"no result";
char*res = (char*)malloc(sizeof(char)*(len-4));
// 判断数据后缀是否符合规则
if(str[len-1]=='n' && str[len-2]=='i' && str[len-3]=='b' && str[len-4]=='.')
{
// 赋值数组str中前len-4个字符到res
memcpy(res, str, len-4);
// 给res数组添加一个结束符
res[len-4] = '\0';
return res;
}
else
return (char*)"no result";
}
// 主函数
int main(int arc,char*arg[])
{
printf("%s\n%s\n",check(arg[1]),check(arg[2]));
return 0;
}
其运行结果如下:
这个题解感觉上是不是很整洁、简约,🤣🤣🤣但是呢,小编在现场写的代码超级烂,因为当时我给懵了,思维跟不上了 😅😅😅 。写的代码如下:
#include <stdio.h>
#include <string.h>
int main(char str[])
{
int count = 0,i = 0,j = 0,len = strlen(str);
char table[] = "nib.",*res = (char*)malloc(sizeof(char)*(len-4));
for(i=len-4,j=0;i<len,j<4;++i,++j)
if(table[j] != str[i])
break;
if(i == len-4)
{
for(i=0;i<len-4;++i)
printf("%s",str[i]);
printf("\n");
}
else
printf("no result\n");
}
大家看这个是不是很无语,面试官直接跟我说“我想要的是一个接口,而不是这个,应该有返回值的”。😢😢😢
问题三:什么是结构体对齐
在讲述字节对齐前,大家需要知道变量的字节数及其取值范围:
类型 | 字节数 | 取值范围 |
---|---|---|
int(基础类型) | 4字节 | -232 - 231-1 |
unsigned int(无符号整型数据) | 4字节 | 0 - 232 -1 |
short(短整型) | 2字节 | -215 - 215-1 |
unsigned short(无符号短整型) | 2字节 | 0 - 216-1 |
long(长整型) | 4字节 | -231 - 231-1 |
unsigend long(无符号长整型) | 4字节 | 0 - 232-1 |
long long(双长整型) | 8字节 | -264 - 263-1 |
unsigned long long(无符号双长整型) | 8字节 | 0 - 264-1 |
(singed) char(有符号字符型) | 1字节 | -27 - 27-1 |
unsigned char(无符号字符型) | 1字节 | 0 - 28-1 |
float (单精度浮点型) | 4字节 | 0、-3.4*10-38 - 3.4*1038 |
double(双精度浮点型) | 8字节 | 0、2.3*10-308 - 1.7*10308 |
long double (Visual C++中的长双精度浮点型) | 8字节 | 0、2.3*10-308 - 1.7*10308 |
long double(Turbo C 中的长双精度浮点型) | 16字节 | 0、3.4*10-4932 - 1.1*104932 |
字节对齐:
- 字节对齐的要求: 结构体成员变量对齐、 结构体总体对齐。
- 结构体对齐的规则:
- 一、结构体变量的首地址是最长成员长度的整数倍。
- 二、每个成员相对结构体首地址的偏移量,一定是该成员长度的整数倍。
- 三、结构体的总长度是最长成员长度的整数倍。
- 四、如果结构体内有成员长度大于处理器的位数,那么就助处理器的位数作为对齐单位。
示例:
先给大家看看小编定义的两个结构体:
struct data1
{
char a;
int b;
short c;
};
struct data2
{
char a;
short c;
int b;
};
大家咋一看是不是认为这两个结构体没有任何区别呢?🤔🤔🤔不瞒大家,一开始,小编也是认为这两个结构体在存储上是没有任何区别的,但是在认真学习后,就明白了这两个结构体的区别,宏观上看就是sizeof(struct data1)
与sizeof(struct data2)
是不一样的,在实际的存储中他们两者的存储类似下图(下图中的黄色部分是填充):
也就是说结构体struct data1
的第二个成员变量int b
需要遵循上述规则的第二条(变量存储的起始地址需要时变量大小的整数倍),而导致数据在存储时发生了偏移。
但是结构体struct data2
的第二个成员变量short c
其大小刚好是2个字节,也就是说在填充一个字节数据后刚好能够存储。
那么再使用printf函数打印成员变量的首地址看看其存储结果:
struct data1 data1;
struct data2 data2;
printf("sizeof(data1):%d sizeof(data2):%d\n",sizeof(struct data1),sizeof(struct data2));
printf("data1_a:%lld data1_b:%lld data1_c:%lld\n",&data1.a,&data1.b,&data1.c);
printf("data2_a:%lld data2_c:%lld data2_b:%lld\n",&data2.a,&data2.c,&data2.b);
&esmp;因为这个是一个首地址,将其放入我们上述分析的图片中,每个变量的首地址刚好对上。😁😁😁
结构体对齐,好处:能够加快CPU数据取出的效率;坏处:很显然会浪费一定的空间。
问题四:大端小端的数据存储
讲解前,大家需要知道一个概念:读数据永远是从低地址开始的!!!
大端小端的概念
- 大端存储:大端模式,将数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中;
- 小端存储:小端模式,将数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
如果将一个32位的整数0x12345678
存放到一个整型变量(int)中,这个整型变量采用大端或者小端模式在内存中的存储由下表所示:
地址偏移量 | 大端模式 | 小端模式 |
---|---|---|
0x00 | 12 | 78 |
0x01 | 34 | 56 |
0x02 | 56 | 34 |
0x03 | 78 | 12 |
那么,我们如何判断某个系统是采用小端模式存储还是大端模式存储呢?
一个简单的方法:我们可以先定义一个数据0x0001
存储在一个变量中,那么这个变量最终在系统中存储时,如果系统采用大端模式存储,则最低位就会是0;如果系统采用小端模式存储,那么低位就会是1。因此,我们就可以通过判断一个数据的最低位来判系统的存储方式。
&esmp;其测试程序的核心代码如下:
int a = 0x0001;
int b = a&1;
if(b == 1)
printf("系统采用小端模式存储\n");
else
printf("系统采用大端模式存储\n");
这里小编在vscode、dev C++、ubuntu中运行了,其结果都一样:
问题五:STM32F103烧入时ROM的起始地址
STM32F103ZET6的ROM与RAM的起始地址及其大小如下:
- ROM:起始地址为0x0800 0000,大小为0x0008 0000;
- RAM:起始地址为0x2000 0000,大小为0x0001 0000。
其他问题
- 以后的职业规划;
- 自己理想的生活与工作;
- 家里人对自己找工作有啥想法或建议;
- 为什么要打比赛;
- 为什么要加入实验室;
- 在你简历的项目中,你最想分享的项目是哪个;
- C语言学的咋样以及你最想与我(hr)探讨的是哪个部分;(这里小编选的是结构体😅😅😅)
- C语言能够实现面向对象开发嘛;
- 移植FreeRTOS时碰到的问题以及解决方案;
- 你为什么会想到这个项目以及一些项目的细节,如项目中使用WiFi的作用;
好了,小编本次的面试分享就到这里了,如果大家对上述问题有啥更好的建议与解决方案,欢迎大家留言或私信哟!🤤🤤🤤
最后,小编再分享一个小编自己创建的QQ交流群 338366010,在里面会有嵌入式相关知识的分享,欢迎各位加入哟!😉😉😉