本次进行分享的是嵌入式软件开发中常见的一些c语言知识相关的笔试题
内容来自B站博主:
c语言笔试题嵌入式软件开发视频
1.char *const *(*next)()请对这行代码进行解释
1)首先这题考的知识点有函数指针
2)const 和指针之间的关系(指向常量的指针,以及常量指针)
代码解释:
1、(*next) 中next是一个指针
2、(*next)() next是一个函数指针
3、 char *const 是一个指针 常量指针
综合理解:next是个函数指针,指向一个没有参数的函数,并且这个函数的返回值是一个指针,该指针指向一个类型为char的常量指针。
2、char *(*c[10])(int **p)
1)char pt[10] ;pt是一个数组 ,数组是由10个char指针所组成,Pt是指针数组
2)char (*pt)[10]; pt是指针,pt指针指向了由10个char元素所构成的数组,pt是数组指针
我们理解上面两个知识点在进行分析就简单了。
1、c[10] 是数组
2、*c[10] 是一个指针数组
3、char *()(int **p); c数组中每一个元素都是函数指针,其所指向的函数返回值是char *类型并且函数带一个指向指针的指针(二重指针)。
请问一下代码是否有问题,如果有问题请指出问题
char *s = "AAA";
printf("%s\n",s);
s[0] = 'B'; //不允许 *** 作错误使用
printf("%s\n",s);
从运行的结果我们可以看出,发生了段错误。这是因为我们初始化字符串相当于的一个常量,这个值时只读的 不允许进行 *** 作。
4、嵌入式系统经常具有要求程序员去访问某特定的内存位置要求读取地址的里面的内容或者向该地址写入新值,特别是在嵌入式处理器并发中 *** 作寄存器时这种用法会经常出现。例如在某工程中,耍求设置一绝对内存地址为0x40020800的位置,将该地址里面的内容设置为整型值0x3456。编写代码完成这一任务。
int *pt;
pt = (unsigned long *)0x40020800;
*pt = 0x3456;
//方式二
#define ADDR (*(volatile unsigned long *)t0x40020800)
ADDR = 0x3456;
typedef 在c语言中频繁用以声明一个已经存在的数据类型的同义字,也可以使用预处理器做类似的是,思考这样一个例子
#define dps struct s *
typedef struct * tps;
以上两种情况都是定义dps 和tps做为一个指向结构体的指针,拿中方式更好呢?
#define dps struct s*
typedef struct s * tps;
//1.情况一
dps p1; //struct s *p1;
tps p2; //struct s *p2;
//当我们只定义一个变量的不难发现这两种方式的效果是一样
//2.情况二
dps p1,p2; //struct s*p1,p2; //p1是结构体指针p2是结构体s的对象
tpd p3,p4; //struct s*p3,struct s*p4 ;//都是结构体指针
//所以第二种方式更好
下面代码是否有问题,如果有问题,那么编译运行后的结果是什么,并说明问题的原因是什么,该怎么修改
#include
char *get_str(void);
int main(void)
{
char *p = get_str();
printf("p = %s\n",p);
return 0;
}
char *get_str(void)
{
//char str[] = "abcd";
//char *str = "abcd";
return str;
}
从运行结果可以看出我们P指针是一个空指针,那么造成这个的原因是因为我们函数内部所定义的数组进行内存分配时是在栈上的当我们调用这个函数的时候就会分配内存,调用完成后就会回收该内存空间,所以当我们用P指针去接收返回值的时候为一个空指针。修改方法也很简单我们把字符数组改成指针的形式就可以了。因为指针定义的字符串为常量,所存放的空间并不在栈了,而是在数据段中,并不会随着函数的调用完成而去释放完内存,该地址的值发送改变。
什么是大端模式,什么是小端莫斯,编写代码测试你的计算机是大端模式还是小端模式?
关于大小端我们记住这样一个结论就好了,为什么会有大小端之分,大家可以去查看相关的资料介绍,这里就不做解释了。
大端模式:数据的低位存放在高地址中,数据的高位存放在低地址中
小端模式:数据的低位存放在低地址中,数据的高位存放在高地址中。
//代码测试
#include
int main(void)
{
//方式一
union Test
{
int a;
char b;
}c;
c.a = 1;
printf("%d\r\n",c.b);
union T
{
unsigned int n;
char arr[4];
}t;
t.n = 0x12345678;
int i = 0;
for(i = 0;i<4;i++)
{
printf("&arr[%d] = %p,arr[%d] = 0x%x\r\n",i,&t.arr[i],i,t.arr[i]);
}
//方式二
unsigned int a = 0x12345678;
char b = (char *)a;
printf("b = 0x%x\r\n",b);
return 0;
}
从上面的结果中我们可以看出我使用的计算机的小端模式的存储。
想取出当前计算机系统下无符号整数Int类型的0值和其最大值,评价下面的代码的片段。
unsigned int zero = 0;
unsigned int copmzero = 0xffff;
首先我们要明白的是32位系统的Int占4个字节,64位系统占8个字节,那么问题来了我们有时不知道自己的系统是多少位的那么直接0xffff就默认了是32位系统,这么做是不严谨的,最好的求无符号的最大值应该是对高0值取反unsigned copmzero = ~0;这样不管是多少位的系统都可以得出正确的答案。
考虑下面的代码,其作用是在数组中查找一个特定的元素,如果将&&写成了&下面两种方式是否都可以实现?
//方式一
i = 0;
while(i < arrsize && arr[i] != x)
//方式二
i = 0;
while(i < arrsize & arr[i] != x)
先写一个测试进行看一下结果
#include
#define ARRSIZE 10
int main(void)
{
//当值只有0 或者1的时候 && &的结果是一样的
//如果只是真值则下面两个结果不同
int i = 0;
int arr[] = {1,2,3,4,5,6,7,8,9,0};
int ret = 5;
while(i < ARRSIZE && arr[i] != ret)
i++;
printf("i = %d\r\n",i);
while(i < ARRSIZE & arr[i] != ret)
i++;
printf("i = %d\r\n",i);
return 0;
}
从结果分析来看,上面两种方式都实现了对特定元素下标的查找,但第二种的结果并不是确定的,当我们变量的结果只有1或0 的时候上面两种方式确实可以等价,但如果判断条件里面是确定的条件时第二种就不行了。
首先&&是逻辑上的与如果左边等式为0了右边的等式就不用判断了这个结果就是为0,&为位与及时左边为0同样还是要看右边的值在进行位与运算
求printf("%d\r\n",printf("%d",printf("%d",a)));的结果
//结果
4321
分析:首先输出的是最右边的printf的值此时a时43
printf(“%d”,a); -------------->43
printf(“%d”,printf(“%d”,a)); ------------>432 第一句输出后printf的返回值为输出的元素个数(2)使用在第一句的基础上结果为432
最后一个输出时上面一个Printf的返回值(2)也就是1个元素,因为只有最后一次Printf输出的时候才换行所以结果是4321
举例说明通过#运算符,利用宏参数创建字符串
#include
#define SQUARE(x) printf("x squre is:%d\n",((x) * (x)))
#define SQUARE2(x) printf(""#x" squre is:%d\n",((x) * (x)))
//demo4 使用宏参数创建字符串 #
int main(void)
{
SQUARE(4);
SQUARE2(4);
SQUARE2(2 + 4);
return 0;
}
举例说明"##"运算符的作用
#include
#define XNAME(n) x##n
// ##预处理的 粘合剂 将2个符号转成一个符号
int main(void)
{
//int x1 = 10;
int XNAME(1) = 10 ; //x1 ---> int x1 = 10;
printf("%d\n",x1);
return 0;
}
##进行字符之间的拼接。
阅读下面代码,看这段代码是否有问题,如果有请指出问题
#include
#include
#define SIZE 24 //数组大小根据需求进行修改
struct std
{
unsigned int id;
char *name;
//char name[SIZE];
unsigned int age;
}per;
int main(void)
{
per.id = 0001;
strcpy(per.name,"Micheal JackSon");
//per.name = "Micheal JackSon";
per.age = 20;
printf("%s\n",per.name);
return 0;
}
运行的结果是发送了段错误,究其原因有1》指针为野指针 2》内存空间不足上面定义的char *name本身本身只有4个字节 而我们strcpy赋值所占用的空间是大于这个的。
解决办法:
1》我们直接对name赋值per.name = “Micheal JackSon”;
2》结构体中定义为字符数组的形式,这也是最常用的方法,存放字符串的时候最好使用数组
阅读下面代码,看是否有问题,如果有请指出问题
char *p1 = “ABCABC”;
char *p2 = (char *)malloc(strlen(p1));
strcpy(p2,p1);
代码测试结果是没有问题的,但这段代码是不够严谨的p2在进行空间开辟的使用strlen的大小是6是没有算’\0’结尾字符的,而p1的实际大小是7,进行拷贝的时候可能就会出现问题,所以我们在进行空间开辟的时候要合适,上面代码我们可以改成char *p2 = (char *)malloc(strlen(p1) +1)
在Linux内核代码(版本2.6.22)中有如下定义:
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE*)0)->MEMBER)请尝试解释下上面这行语句的含义。
1)(TYPE*)0:将0强制类型转换为TPYE类型的指针,p = (TYPE*) 0
2) ((TYPE*)0)->MEMBER—–》p->MEMBER—–>访问MEMBER成员变量
3)&((TYPE*)0)->MEMBER :取出MEMBER成员变量的地址
4)(size_t)&((TYPE*)0)->MEMBER :size_t相当于是类型强制转换,将MEMBER成员变量的地址转换为size_t类型的数据,和Int 类型是等价的
总结:该宏的作用就是求出MEMBER成员变量在TYPE中的偏移量
const关键的作用
1》const定义一个常量,这个常量在定义时必须进行初始化,否则后面就没有机会
2》实际上const定义的变量并不是真正的常量(指针数组可以验证)
3》const和指针的用法
const int *pt;
int const *pt;这两种方式是等价的,都代表pt指针可以指向任意对象,但是不能通过pt指针来修改指向的对象
int *const pt;pt 指针不能指向其他位置,但是可以通过pt指针修改指向位置的值
4》const修改形参
#include
int main(void)
{
int i = 10,j = 20;
const int *pt;
pt = &i;
*pt = 20;
printf("i = %d\n",i);
return 0;
}
#include
int main(void)
{
int i = 10,j = 20;
const int *pt;
pt = &i;
pt = &j;
printf("pt = %d\n",*pt);
return 0;
}
可以修改指针的指针,但是不能修改指向的值
#include
int main(void)
{
int i = 10,j = 20;
int * const pt ;
pt = &i;
pt = &j;
printf("pt = %d\n",*pt);
return 0;
}
指针的指向不可修改,在定义时进行初始化
#include
int main(void)
{
int i = 10,j = 20;
int * const pt = &i ;
*pt = 30;
printf("i = %d\n",i);
return 0;
}
指向的值可以进行修改
#include
int add(const int *pt1,const int *pt2);
int main(void)
{
int a= 10,b = 20;
int ret = 0;
ret = add(&a,&b);
printf("ret = %d\n",ret);
return 0;
}
int add(const int *pt1,const int *pt2)
{
// *pt1 = 20;
// *pt2 = 30;
return (*pt1 + *pt2);
}
做形参时不可去修改值
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)