嵌入式面试、C/C++面试(二)

1.请对这行代码进行解释

char *const *(*next) ();
答:next是一个函数指针,指向一个没有参数的函数,该函数返回值是一个指针,
该指针指向一个类型为char的常量指针

2.请对这行代码进行解释

char *(*c[10])(int**p)
1> char *pt[10]------>[]优先级高于*,所以pt是一个数组,数组由10char *指针组成,即指针数组
2> char (*pt)[10]---->()优先级高于[],所以pt指针指向了由10char 元素构成的数组,
   即pt是数组指针。
3> c 数组里面中每一个元素都是函数指针,其所指向的函数返回值是 char * 类型,并且函数带一个指    向指针的指针参数

3.请问以下代码是否有问题,如有问题请指出,如果没有问题,运行结束什么?

char *s = "AAA"; 
printf("%s\n",s);//正常输出AAA
char s[0] = 'B';//尝试修改字符串常量"AAA",而"AAA"存储在内存的只读区域
printf("%s\n",s);//段错误
s = "BBB";
printf("%s\n",s);//正常输出"BBB",指针可以指向其他

4.嵌入式系统经常要求程序员去访问某特定的内存位置,要求读取地址的里面的内容或者想该地址写入新值,特别是在嵌入式处理器开发中操作寄存器是这种用法经常出现。例如:在某工程中,要求设置一绝对内存地址为 0x40020800 的位置,将该地址里面的内容设置为整形值 0x3456 。编写代码完成这一项任务。

1> <方法一> 
int *pt;
pt = (unsigned long *)0x40020800;
*pt = 0x3456;

2> <方法二>
 #define ADDR (*(volatile unsigned long *)0x40020800)
 ADDR = 0x3456;

5.typedef在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理做类似的事。例如:

   #define dsP struct s*
   typedef struct s*  tsP;
   以上两种情况的意图都是要定义dsP和tsP作为一个指向s指针。哪一个方法更好?
   1>
        dsP p1; -----> struct s *p1;
   	 tsP p2; -----> struct s *p2;
   2>   dsP p3,p4; -----> struct s *p3, p4;--->p1是结构体指针,而p2是结构体对象
   	 tsP p3,p4; -----> struct s *p3,*p4;--->p1、p2都是结构体指针,这种方式较好。
   

6.下面代码是否有问题,若有问题,那么运行的结果是什么,并说明其中的原因,那该如何修改。

#include <stdio.h>
char *get_str(void);
int main(void)
{
    char *p = get_str();
    printf("%s\n",p);
    return 0;
}
char *get_str(void)
{
    char str[] = {"abcd"};
    return str;
}
答:段错误,因为字符在栈空间存储,函数结束系统收回栈空间,p指向null
方法一:
      static char str[] = {"abcd"};//更换存储区域,static存储在静态存储区,不会被销毁
方法二:
      char *str = {"abcd"}; //"abcd"字符串是存储在内存只读静态区,返回str指针,指针内存储字符串常量的地址

7.下面所示的代码运行的结果是什么?

#include <stdio.h>
int main(void)
{
    unsigned int a = 6;
    int b = -20;
    (a+b > 6) ? puts(">6") : puts("<=6");
    return 0;
}
//运行结束是 puts(">6"),无符号整形与有符号整形运算自动类型转化,转化为两个无符号整形相加
//自动类型转换:无符号整数+有符号整数---->将有符号自动转化为无符号数据
 6 ---> 0000 0000 0000 0000 0000 0000 0000 0110
-20---> 1000 0000 0000 0000 0000 0000 0001 0100  
反码---> 1111 1111 1111 1111 1111 1111 1110 1011  符号位不变,其他位取反
补码---> 1111 1111 1111 1111 1111 1111 1110 1100  补码 = 反码 + 1
     0000 0000 0000 0000 0000 0000 0000 0110
     									   +
	 1111 1111 1111 1111 1111 1111 1110 1100
     -------------------------------------------
     1111 1111 1111 1111 1111 1111 1111 0010 

8.什么是大端模式,什么是小端模式,编写代码测试你计算机是大端模式还是小端模式?

大端模式:数据的高位放在内存低地址中,数据低位放在内存高地址中

小端模式:数据的高位放在内存高地址中,数据低位放在内存低地址中

union test{
    unsigned char arr;
    unsigned int n;
};
union test num;
num.n = 0x12345678;
if(num.arr == 0x12)
{
    printf("大端\n")
}
else if(num.arr == 0x78)
{
    printf("小端\n")
}

9.想取出当前计算机系统下无符号整数int类型的0值和最大值,评价下面的代码片段:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF; //默认16位,不适合32位64位计算机机
//修改为:
unsigned int compzero = ~0;//合适各种位数的计算机,方便移植。

10.考虑下面的代码,其作用在数组中查找一个特定的元素:

i = 0;
while(i < arrsizeof && arr[i]!=x)
    i++;
假如无意中我们用位操作符代替&代替了上面的逻辑运算符&&,如下:
i= 0;
while(i < arrsizeof & arr[i]!=x)
    i++;
则当前的语句是否还能实现上面的元素查找功能?
虽然运行结果一样,但是如果&左右为非 01值就不可以了
侥幸1:
    x && y     1 && 0 ----> 0
    x & y      1 &  0 ----> 0
    5 && 10    0101 && 1010 ----> 1
    5 &  10    0101 &  1010 ----> 0000
侥幸2:x && y ----->x为假,编译器就不进行y的判断了
    而 x & y ----->x为假,也要进行运算符及计算,程序还没结束,数值越界,可能会出问题
    

11.下面这个程序的输出结果是什么?

#include <stdio.h>
int main(void)
{
    i = 43;
    printf("%d\n",printf("%d\n",printf("%d",i)));
	return 0;
} 
//printf函数返回值返回打印字符的个数
printf("%d",i)----> 输出43,返回值为 2
printf("%d",2)----> 输出2,返回值为 1
printf("%d",1)----> 输出1,返回值为 1
所以输出结果为 4321

12.举例说明,通过 # 运算符,利用宏参数创建字符串

#include <stdio.h>
#define SQUARE(x) (printf(""#x"square is %d\n",(x)*(x)))
//使用"#x"普通文本 x 被替换成字符串,宏参数是什么就输出什么
int main(void)
{
    SQUAER(4);
    SQUAER(2+4);
    return 0;
} 

13.请举例说明,“ ## ”运算符的作用

#include <stdio.h>
#define X_NAME(n)  x##n
int main(void)
{
    //int x1 = 10;
    int X_NAME(1) = 10; //x1 -->int x1 = 10;
    printf("%d\n",x1);
    return 0;
} 
##这个运算符的作用:预处理的粘合剂,用它把两个语言符号结合并一个语言符号

14.阅读下面代码,看这段代码是否有问题,若果有问题请指出问题。

#include <stdio.h>
#include <string.h>
struct std{
    unsigned int id;
    char *name; //4个字节,指针存放地址,放不下HSW Jackson,并且指针没有初始化,野指针随机地址可能能访问也可能不能访问
    unsigned int age;
}per;
int main(void)
{
    per.id = 001;
    strcpy(per.name,"HSW Jackson");
    per.age = 20;
    printf("%s\n",per.name);
    return 0;
}
//编译没有语法问题,运行时出现段错误 
修改办法一: 用 per.name = "HSW Jackson";代替strcpy(per.name,"HSW Jackson");让指针指向常量字符串的地址
修改办法二:#define SIZE 80 char name[SIZE];

15.阅读下面代码,看这段代码是否有问题,如果有问题请指出。

char *p1 = "ABCABC";
char *p2 = (char *)mallic(strlen(p1));
strcpy(p2,p1);
1----> strlen 不包含'\0',-->strlen(p1) --> 6
2----> strcpy 拷贝包含'\0'空字符,内存越界
修改:
char *p2 = (char *)mallic(strlen(p1)+1);或者
char *p2 = (char *)mallic(sizeof(p1));

  1. C 和 C++ 中,free()和delete是如何操作指向动态开辟内存的指针。
char *p = (char *)malloc(100);
free(p);
p = NULL;

char *p = new char[100];
delete []p;
p = NULL;
//free和delete之后只是释放开辟的内存空间,指针还是存在的,空间释放后强烈不建议操作指针,因为指针此时是野指针,指向未知地址,可能会出现意向不到的问题,所以在释放之后让它指向NULL。

17.在Linux内核中代码中有如下定义:

#define offsetof (TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
尝试解释这句语句的含义。
1----> (TYPE *)0,0强制类型转化为TYPE类型的指针,p = (TYPE *)02----> ((TYPE *)0)->MEMBER,p->MEMBER-->访问MEMBER的成员变量
3----> &((TYPE *)0)->MEMBER,取出MEMBER成员变量的地址
4----> (size_t)&((TYPE *)0)->MEMBER,将MEMBER成员变量地址转化为 size_t 类型数据,size_t  < = = > int
//总结:实际上是求MUMEBER成员在TYPER中的偏移量

18.查看如下代码,写出运行结果

#include <stdio.h>
#define offsetof(TYPE,MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
typedef student s{
    union{
        int a;
        char str[10];
    };
    struct s* next;
}S;
int main(void)
{
    printf("%d\n",offsetof(S,next));
    return 0;
}
32----> 12 结构体默认4字节对齐
64----> 16 结构体默认8字节对齐
//修改字节对齐:#pragma pack(2)

19.const关键字的作用

1> const 定义一个常量,这个常量在定义的时候必须进行初始化,否则以后没有机会
2> 实际上 const 定义的变量并不是一个真正的常量(指针、数据两种方式验证这个观点)
3> const 和指针的用法
    const int *pt; <==> int const *pt;//两者等价,都代表pt指针可以指向任意对象,但是不  	  能通过pt指针来修改指向的对象
    int * const pt; //指针不能指向其他位置,但是可以通过指针pt修改指向的值
4> const 修饰形参:预防操作失误
/*================================================================================
沿着 * 号划一条线:如果const位于* 的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于* 的右侧,const就是修饰指针本身,即指针本身是常量。
================================================================================*/

20.下面这段代码 共创建几个进程

#include <stdio.h>
#include <sys/typrs.h>
#include <unistd.h>
int main(void)
{
   	fork() || fork();
    return 0;
}
//一共创建三个进程,fork() 首先创建出父子进程,父进程 pid > 0不进行 || 逻辑运算,父进程不会执行第二个fork(),子进程 pid= 0 执行这条语句,运算符 || 左面不能确定整个表达式真假,所以执行第二个fork()所以一共创建3个进程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值