C语言常见笔试编程题【备战春招秋招】
近些年针对应届生的C语言笔试的常见编程题,不含算法题
#该笔试题只记录常见且典型的编程题,不记录算法题#
一、编写一个程序,判断当前是大端字节序还是小端字节序
大端字节序:高位字节存储在低地址,低位字节存储在高地址。
小端字节序:低位字节存储在低地址,高位字节存储在高地址。
方法一:使用指针判断
unsigned int num = 0x12345678;
char *p = (char *)#
if (*p == 0x78) {
//小端字节序
}
else {
//大端字节序
}
方法二:使用联合体判断(部分笔试题要求不使用指针)
union {
int num;
char c;
}endian;
endian.num = 0x12345678;
if (endian.c == 0x78) {
//小端
}
else {
//大端
}
二、编写strcpy函数(字符串拷贝函数)的实现
函数原型: char *strcpy(char *strDest, const char *strSrc);
函数功能:将strSrc中的字符串拷贝到strDest
char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest != NULL) && (strSrc != NULL)); //判断传参的正确性
char *address = strDest; //保存目标字符串首地址用于函数返回
while((*strDest++ = *strSrc++) != '\0'); // 拷贝字符串
return address; //返回目标串首地址,目的是实现链式表达式
}
注意:部分实现中不会考虑地址strDest与strSrc地址重叠问题,此处也未考虑此问题。解决这个问题可以参考第四题的memcpy
三、编写atoi函数(字符串转整型函数)的实现
函数原型:int atoi(char *str);
int atoi(char *str)
{
assert(str != NULL); //判断输入合法
int result = 0; //计算结果
int sign = 1; //正负数标记,正数为1,负数为-1
int i = 0; //遍历下标
if (str[0] == '-') {
sign = -1;
i = 1;
}
for (; str[i] != '\0'; ++i) {
assert(str[i] >= '0' && str[i] <= '9'); //中间存在非数字字符则报错
assert(result < INT_MAX / 10); //INT_MAX为int最大值,此处判断越界
result = result * 10 + (str[i] - '0');
}
return result * sign;
}
进阶版A:把一个字符串转化为一个逆序的整型数值。
例如字符串:“123456”,转化成数字:654321。注意考虑边界情况,不考虑负数。
int reverse_atoi(char *str)
{
assert(str != NULL); //判断输入合法
int result = 0; //计算结果
int i = strlen(str) - 1; //遍历下标,从后往前遍历
for (; i>= 0; --i) {
assert(str[i] >= '0' && str[i] <= '9'); //中间存在非数字字符则报错
assert(result > INT_MAX / 10); //INT_MAX为int最大值,此处判断越界
result = result * 10 + (str[i] - '0');
}
return result;
}
和atoi版本区别在于此处字符串从后往前遍历即可。
进阶版B:把一个16进制的字符串转换为整型数值。
例如字符串:“12ab”,转化为数字:4779。16进制数只用小写字母表示
int hex_atoi(char *str)
{
assert(str != NULL); //判断输入合法
int result = 0; //计算结果
for (int i = 0; str[i] != '\0'; ++i) {
assert(result > INT_MAX / 16); //INT_MAX为int最大值,此处判断越界
if (str[i] >= 'a' && str[i] <= 'f') {
result = result * 16 + (str[i] - 'a' + 10);
}
else if (str[i] >= '0' && str[i] <= '9') {
result = result * 16 + (str[i] - '0');
}
else {
assert(0); //输入不合法导致退出
return 0;
}
}
return result;
}
注:以上代码不喜欢断言也可以用 if 判断报错后返回错误码
四、编写memcpy函数的底层实现
void *memcpy(void *dest, const void *src, size_t n)
{
if (dest == NULL || src == NULL || dest == src) {
return dest;
}
char *d = (char *)dest;
const char *s = (const char *)src;
// 判断内存重叠情况,如果重叠且重叠方向是原串结尾与目标开头重叠,则从后往前复制
if (d > s && d < s + n) {
d += n;
s += n;
while (n--) {
*--d = *--s;
}
}
else {
while (n--) {
*d++ = *s++;
}
}
return dest;
}
五、编写strstr函数(查找字符串子串函数)
函数原型:char *strstr(const char *str1, const char *str2);
函数功能:判断str2是否是str1的字串,如果是,函数返回str2在str1中首次出现的地址,否则返回NULL
char* strstr(const char* str1, const char* str2)
{
if (*str2 == '\0') {
return (char*)str1;
}
const char* p1 = str1;
while (*p1) {
const char* p1_start = p1;
const char* p2 = str2;
while (*p1 && *p2 && *p1 == *p2) {
p1++;
p2++;
}
if (*p2 == '\0') {
return (char*)p1_start;
}
p1 = p1_start + 1;
}
return NULL;
}
可以拆成两个功能去写,一个函数为strncmp判断两个字符串是否相等,然后再循环遍历str1,依次判断str2是否与之相等。
六、结构体与联合体大小计算
结构体对齐原则:
1、变量所处内存位置起始地址必须为自身类型大小的倍数。比如char类型,占1个字节,可以存放在任何地址上,short类型占2个字节,必须存放在2的倍数地址上,比如0,2,4,6的位置,int类型占4个字节,必须存放在4的倍数地址上
2、结构体总大小必须为其中元素的最大基础元素的倍数,比如结构体中只包含char,int,short,那么结构体总大小一定是4的倍数。
见以下示例
struct A
{
char str;
short s;
int num;
};
//sizeof(struct A) = 8
struct B
{
char str;
int num;
short s;
};
//sizeof(struct B) = 12
union C
{
int num;
char str;
};
//sizeof(union C) = 4
typedef union
{
long i;
int k[5];
char c;
}DATE;
struct data
{
int cat;
DATE cow;
double dog;
};
//sizeof(DATE) = 20
//sizeof(struct data) = 32
以下为struct A的内存样式(每格1字节,空格为补齐)
str | s | s | num | num | num | num |
以下为struct B的内存样式(每格1字节,空格为补齐)
str | num | num | num | num | s | s |
七、给绝对地址的内存赋值
此题常见于嵌入式相关岗位笔试或硬件单片机岗位笔试
设置一个绝对地址为0x6588的整形变量值为0xaabb
#define REG (*(volatile unsigned int *)0x6588)
REG &= ~(0xffffffff); //先清零
REG |= 0xaabb; //再赋值
结语
编写该文章目的主要为想从事相关工作的同学找到一份好的工作,以上题目在笔试中经常出现,如果有在外面试的朋友发现有更常见更经典的题目也可以私信告知,后续也会更新到博客当中。
如果有朋友想系统的学习嵌入式相关知识,从事相关的行业,可以私信我,有一些经典的电子档书籍资料和开源网课学习链接。
更多推荐
所有评论(0)