嵌入式c语言基础面试问题(二)

目录

1.C语言数据类型有哪些?

2.各种数据类型的字节长度及取值范围?字节长度?

3.sizeof,strlen 的区别?

4.补码的作用?

5.十进制,二进制,16进制的转换?

二进制---->十进制

十进制---->二进制

十进制转十六进制

十六进制转十进制

二进制与十六进制直接转换

6.C语言内存空间布局?

7.局部变量和全局变量的区别?作用域

8.static 关键字的作用?(语法作用,应用场景)

9.extern 关键字的作用?

10.register 关键字作用及使用注意事项?

11.const 关键字的作用?const 修饰指针的意义?

12.typedef 关键字的作用?(应用场景:函数指针和函数指针数组)

13.什么是函数指针,什么是指针函数,什么是函数指针数组,什么是数组指针,什么是指针数组?

 14.break、continue 区别

15.do{}while(0)的作用

16.switch case 使用注意事项

17.goto 语句使用注意事项及应用场景?

18.++i  VS   i++

1.C语言数据类型有哪些?

c语言数据类型分为基本数据类型和复合数据类型,基本数据类型分为数值类型,字符类型,浮点型,void空类型,地址类型(指针)

复合数据类型分为结构体,共用体,枚举

以及bool类型

2.各种数据类型的字节长度及取值范围?字节长度?

不同的编译器在类型所占字节长度可能不太一样

数据类型

字节长度取值范围
short2-32768~32767
int4-2^31~2^31-1

long

4-2^31~2^31-1
long long8

-2^63~2^63-1

char1-128~127
bool1true or false
float4 ±3.4e±38 (7位有效数字)
double8±1.7e±308 (15位有效数字)
long double16-3.4e+4932~1.1E+4932(18位有效数字)
unsigned short 2
0 ~ 65535
unsigned long4
0 ~ 4294967295
unsigned int4
0 ~ 4294967295
unsigned long long8
0 ~ 2^64 - 1

3.sizeof,strlen 的区别?

sizeof 是运算符,strlen 是函数

sizeof 可以用类型做参数,但是strlen只能用char*做参数,结尾必须是\0

sizeof 的功能是获得数据类型所占的字节长度,strlen 是获得字符串不包含'\0'的长 度

4.补码的作用?

补码作用:计算机的运算都是通过加法运算的,没有减法,补码就是为了统一加减。

正数的补码是他本身,负数的补码转换成原码进行计算(-128补码-0)

5.十进制,二进制,16进制的转换?

二进制---->十进制

二进制是基数为2的数制系统,使用两个数字0和1。转换二进制到十进制,可以将每个位的值乘以2的幂次方,幂的指数从0开始,从右到左递增,然后将这些值相加。

例如,二进制数1011转换为十进制:

  • 从右到左,位的值分别是1, 0, 1, 1
  • 计算每个位的值:(1 * 2^0 + 0 *2^1 + 1* 2^2 + 1* 2^3 = 1 + 0 + 4 + 8 = 13)
  • 因此,二进制1011等于十进制的13
十进制---->二进制

将十进制数转换为二进制,可以使用连续除以2的方法,记录每一步的余数,然后将余数逆序排列。

例如,将十进制数13转换为二进制:

  1. 13除以2,商为6余1
  2. 6除以2,商为3余0
  3. 3除以2,商为1余1
  4. 1除以2,商为0余1

将余数逆序排列得到1101,所以十进制的13等于二进制的1101

十进制转十六进制

十六进制是基数为16的数制系统,使用数字0到9和字母A到F(代表值10到15)。将十进制转换为十六进制,也可以使用连续除以16的方法,然后将得到的余数(如果是10到15,则使用相应的字母)逆序排列。

例如,将十进制数200转换为十六进制:

  1. 200除以16,商为12余8
  2. 12除以16,商为0余12(用十六进制的C表示)

因此,200十进制等于C8十六进制。

十六进制转十进制

十六进制转十进制类似于二进制转十进制的方法,但是使用16的幂次方。例如,十六进制C8转为十进制:

  • C代表12,因此C8 = (12 * 16^1) + (8 * 16^0) = 192 + 8 = 200
二进制与十六进制直接转换

二进制与十六进制之间的转换相对简单,因为16是2的整数次幂((16 = 2^4))。因此,每四位二进制数可以直接转换为一个十六进制数,反之亦然。

  • 将二进制转换为十六进制,可以将二进制数每四位一组(从右开始),转换为相应的十六进制数。
  • 将十六进制转换为二进制,则是将每一个十六进制数转换为相对应的四位二进制数。

例如,二进制11011101转为十六进制,分为两组1101(十进制13,十六进制D)和1101(同样是D),所以是DD。反过来,十六进制1A转为二进制是0001(1)和1010(A),组合起来是00011010

6.C语言内存空间布局?

假设进程的虚拟空间有 4 个 G,那么内存空间中 1G 属于内核空间,3G 属于用户空 间,3G 的用户空间中分为栈空间、堆空间、数据区和代码段。

栈空间保存的是局部变量、函数形参和函数,秉持着先进后出原则(先分配空间 的最后释放),并且由系统决定数据的分配和释放,未初始化的变量在栈上是垃圾值。

堆空间保存的是用户使用 malloc、calloc、ralloc 这样的分配函数分配空间的区域,秉持着先进先出的原则(先分配空间的先释放),由用户来决定数据的分配和释放,未初始化的变量为 0。

数据区又分为:bss 段、or 段、静态数据区;bss 段保存未初始化的全局变量;or 段保存的是只读数据;静态数据区保存的是静态变量。

代码段 保存的是 可执行文件代码 ,在运行期间是不可以进行修改的。

7.局部变量和全局变量的区别?作用域

第一,在存储空间上,局部变量存储在栈空间,而全局变量存储在数据区。

第二,在作用域上,局部变量只对它所在函数或所在代码块中可见,生命周期在该函数或 者块结束后释放;全局变量全局可见,访问时加 extern 关键字。

第三,使用时注意,未初始化的局部变量是一个垃圾值,而未初始化的全局变量是 0局部变量和全局变量重名时,局部变量会屏蔽全局变量。

8.static 关键字的作用?(语法作用,应用场景)

static 既能修饰局部变量,又能修饰全局变量,还能修饰函数。

修饰局部变量变成静态局部变量,延长局部变量的生命周期,使这个变量在执行函数 之后不会被释放,程序结束后才释放。

修饰全局变量变静态全局变量,该全局变量只能在 本文件中访问,不能再其他文件访问。

修饰函数变静态函数,该函数只能在本文件访问, 不能再其他文件访问。

他的意义在于修饰局部变量可以解决全局变量线程不安全问题,用来替代全局变量; 修饰全局变量和函数可以解决多人合作开发时命名冲突问题。

9.extern 关键字的作用?

extern 起到外部声明的作用,声明全局变量在其他文件中或其他位置已经被定义,当前函数或者文件可以访问该全局变量。

10.register 关键字作用及使用注意事项?

register 修饰变量,系统会尽可能(寄存器个数有限)将变量的值保存在 cpu 内部的寄存器中,成为寄存器变量,从而省去 cpu 从内存抓取数据的时间,提高了程序的运行效率。一般用于对频繁访问的变量进行修饰,比如循环变量。

使用时注意它只能够修饰局部变量,不能修饰函数和全局变量;不能修饰 cpu 不支持 的数据类型;也不能通过取地址符获取变量的地址。

11.const 关键字的作用?const 修饰指针的意义?

const 修饰变量为只读变量(约等于常量),不能通过 const 修饰的变量名来修改变量值,但是该变量对应的内存空间是可变的。
const 修饰的函数形参可以保护实参变量的值,使形参在函数调用过程中不允许被修改。
(1)const int *p = #//不能通过指针变量 p 修改 num 空间的值,但 p 对应内存空间是可的
*p = 6;//错误
(*p)++;//错误
p++;//可以
(2)int *const p = #//不能通过指针变量 p 修改 p 对应内存空间的值,但 p 指向的内存空间可变
(3)const int *const p = #//不能通过 p 修改指向和对应的内存空间//const 修饰后为只读变 量,只能通过改变内存空间来修改内存值
const int num = 5;
int *p = # *p = 12;
printf("p = %d\n",p);    //p = 一连串总是会变的数
printf("*p = %d\n",*p);  //*p = 12;
printf("num = %d\n",num);//num = 12
printf("p = %p\n",p);    //p = 0x7fff026a5e5c
printf("*p = %p\n",*p);  //*p = 0xc
printf("num = %p\n",num);//num = 0xc

12.typedef 关键字的作用?(应用场景:函数指针和函数指针数组)

typedef 可以给数据类型进行一个重命名,提高代码的可读性,一般大公司可以用来隐藏代码功能。
重名名数据类型 可以为特定平台上的数据类型定义一个通用的名称, 可以解决符号属性带来的移植性问题,提高代码的移植性,一步修改已定义的类型,提高编码效率。
还能用于复杂的变量类型,比如函数指针和函数指针数组, 重命名的复杂变量类型,简化类型命名,可以提高可读性和编码效率。

13.什么是函数指针,什么是指针函数,什么是函数指针数组,什么是数组指针,什么是指针数组?

函数指针是一个指针变量,它指向函数的入口地址。这意味着,通过这个指针,我们可以调用函数,就像直接使用函数名一样。函数指针对于实现回调函数、事件驱动程序和函数表特别有用。

它允许程序在运行时决定调用哪个函数。

void myFunction(int a) {
    printf("Value: %d\n", a);
}

int main() {
    void (*funcPtr)(int) = myFunction;
    funcPtr(5); // 通过函数指针调用函数
    return 0;
}

指针函数是一种函数,它的返回类型是指针。这意味着,这个函数返回的是一个地址,该地址指向一个特定类型的数据。指针函数在动态内存分配时特别有用。

int* returnPointer(int size) {
    int* arr = (int*)malloc(size * sizeof(int));
    return arr; // 返回一个指向整型数组的指针
}

函数指针数组是包含多个函数指针的数组。这样的数组让你能够存储指向不同函数的指针,并根据需要调用数组中的任一函数。这对于实现函数的动态调用非常有用。

void func1() { printf("Function 1\n"); }
void func2() { printf("Function 2\n"); }

int main() {
    void (*funcArray[2])() = {func1, func2};
    funcArray[0](); // 调用func1
    funcArray[1](); // 调用func2
    return 0;
}

数组指针是指向一个数组的指针。这种类型的指针可以用来指向一个整型数组、字符数组等。数组指针很有用,特别是在动态二维数组和函数参数传递中。

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int (*ptr)[5] = &arr; // 指向含有5个整数的数组的指针
    printf("%d\n", (*ptr)[2]); // 访问数组的第三个元素
    return 0;
}

指针数组是一个数组,其元素都是指针。这种数组可以存储多个地址值,这些地址可以指向int、char或其他任何类型的变量或数组。

int main() {
    int val1 = 1, val2 = 2, val3 = 3;
    int* arr[3] = {&val1, &val2, &val3}; // 一个整型指针数组
    printf("%d\n", *arr[0]); // 访问第一个元素所指向的值
    return 0;
}

 14.breakcontinue 区别

跳出当前循环:break

结束本次循环:continue

15.do{}while(0)的作用

在C或C++中,do{}while(0)结构在看起来似乎无意义的情况下,实际上有一些非常实用的用途,尤其是在宏定义中。这种结构使得宏定义在包含多条语句时更加安全和可用。

为什么需要do{}while(0)?

假设你有一个宏定义,需要执行多条语句:

#define MY_MACRO { \
    statement1; \
    statement2; \
    statement3; \
}

如果直接这样使用,可能会在使用宏的地方遇到问题,尤其是在控制流语句中。例如:

if (condition)
    MY_MACRO;
else
    // other code

这会导致编译错误或逻辑错误,因为宏扩展后的代码破坏了原有的控制流结构。

使用do{}while(0)可以避免这类问题,确保宏总是像一个单独的语句一样扩展,即使它包含多条语句:

#define MY_MACRO do { \
    statement1; \
    statement2; \
    statement3; \
} while(0)

这样,即使在如下情况下使用宏,也不会有问题:

if (condition)
    MY_MACRO;
else
    // other code
```

因为do{}while(0)构造被视为一个单独的语句块,所以即使包含多个语句,它们也会正确地作为一个整体执行。而while(0)确保了整个循环只会执行一次,不会引入任何循环效果。

其他优点

**防止错误**:当宏定义用作函数调用时,使用分号(`;`)结束可能会导致意外的行为。do{}while(0)确保即使在宏后误加分号也不会出现问题。
**提高兼容性**:这种模式在不同的编译器和环境中表现一致,增加了代码的可移植性。

16.switch case 使用注意事项

**switch 括号里只能是字符形或者整形

**每个 case 分支里,都不能忘记 break

**不要忘记default break 语句

17.goto 语句使用注意事项及应用场景?

跳转语句: goto 使用时要注意不能跳过相关变量的定义,他的跳转速度极快,一般使用在
异常处理的时候,比如我开机出现了异常需要立马关机这类情况。除此之外一般不建议使
goto 语句,因为他的可读性差,还可能会破环程序的结构。

18.++i  VS   i++

在运算时,可以将 i++ 看成 数值 ,将 ++i 看成 变量名
int i = 2;
int num = (++i) + (++i) + (i++) + (i++);
//i+i+(i++)+(i++);//i=4(+1+1)---->4+4+(i++)+(i++)
//8+4+(i++)//i=5
//12+5=17//i=6

int num = (++i) + (i++) + (i++) + (++i);
//i+3+(i++)+(++i)//i=4;---+1=3 +1=4----->4+3+(i++)+(++i)
//7+4+(++i)//i=5
//11+6=17//i=6

int num = (i++) + (i++) + (++i) + (i++);
//2(i=3)+3(i=4)+5+5(i=6)=15
printf("%d,%d,%d,%d,%d\n",i++,++i,i++,++i,i++);//67472

总结:(6,i,4,i,2)-----67472
char *ptr = "helloworld" printf("%c,%c,%c,%c,%c\n",*(ptr++),*(++ptr),*(++ptr),*(ptr++),*(ptr++));//w,w,o,l,d
//w,w,o,l,d

  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值