【C语言敲重点(四)】嵌入式“八股文“(1)

1. 程序的内存分配

  • ①栈(stack):由编译器进行管理,自动分配和释放,存放的是函数调用过
    程中的各种参数,局部变量,返回值以及函数返回地址。

  • ②堆(heap):用于程序动态申请分配和释放空间( malloc和free),程序员
    申请的空间在使用结束后应该释放,则程序自动收回。
    ✔ 如何记忆?自己申请的东西不要了就丢垃圾堆,垃圾堆需要自己收拾;客栈的东西出钱了由老板收拾,不需要自己动手。(嘿嘿嘿,生动形象吧!!!)

  • ③数据段:主要存放全局变量和静态变量。分成了以下几部分

  • 已初始化数据段.data:已初始化的全局变量和静态变量

  • 未初始化数据段.bss:未初始化的全局变量和静态变量

  • 只读数据段.rodata:只读常量
    ✔ 这里可能有误,百度的各种说明中,个人感觉很多分类得不是很清晰,比较容易混淆,这里给出的是整理之后比较清晰的版本,如有错误,敬请读者批评指正。详细见下图。

  • ⑤代码段: 存放程序的二进制代码。
    在这里插入图片描述

【2024面试真题·广州某100-499公司】
在这里插入图片描述

2. 堆栈溢出的原因?

(递归层次深、动态申请内存未释放、数组越界、指针非法访问)
答:①递归函数调用层次太深,栈中需要保存每次递归调用的局部变量,当递归调用层次太深,会导致栈溢出;
②动态申请的变量保存在堆中,需要在调用结束后手动释放,否则也会导致堆溢出;
③数组下标越界;
④指针访问非法的内存地址。

3. 关键字const的理解?

答:作用:限定或者说限制变量和常量为只读,常量必须在定义的时候同时被初始化
使用和理解如下:

#include <stdio.h>
int main(){
    
    
    int val = 100;
    int valNew = 200;

    int *p1 = &val;
    int const *p2 = &val;   //p2的值只读(*p2),不能修改
    int *const p3 = &val;   //p3只读,不能修改
    int const *const p4 = &val;   //p4和*p4只读,不能修改
    printf("p1=%d,p2=%d,p3=%d\n",*p1,*p2,*p3);

    /** p1的值和指向可以被修改 **/
     p1 = &valNew;      /* OK */
    *p1 = 300;          /* OK */
    /** p2的值只读不能修改,指针指向可以修改 **/
     p2 = &valNew;      /* OK */
    *p2 = 300;          /* ERROR */
    /** p3只读不能修改,p3的值(*p3)可以修改 **/
     p3 = &valNew;      /* ERROR */
    *p3 = 300;          /* OK */
    /** p4的的值和指向均只读不能修改 **/
     p4 = &valNew;      /* ERROR */
    *p4 = 300;          /* ERROR */
    printf("p1=%d,p2=%d,p3=%d\n",*p1,*p2,*p3);
    return 0;
}

4. 关键字static的作用?

答:①static修饰全局变量:只初始化一次,且防止该变量在其他文件中被引用,此时该全局变量的作用域是当前文件
static修饰局部变量:普通的局部变量在函数执行完毕后会被销毁,而使用 static 修饰的局部变量内存空间会一直存在直到程序结束。这意味着每次调用函数时,该变量的值是不会被覆盖或重新初始化,可以实现变量值的持久化。
static修饰函数:表示该函数作用域在当前声明文件中。

5.关键字extern的作用?

答:声明一个定义在外部文件的变量(引入)。如果extern声明全局变量,编译器会自动到本工程其他.c源文件引入该变量; extern也可以引入外部定义的函数,相当于包含对应的头文件。(引入外部变量、外部函数)

6. sizeof和strlen的区别?

  • sizeof是C语言中的关键字,用于计算数据类型或变量所占内存的字节数。
  • strlen是C语言中的函数,用于计算字符串的长度,即有效字符的个数。
  • 计算字符串的大小时,sizeof包含字符串的结束符‘\0’,strlen不包含’\0’。
  • sizeof 运算符在编译时期完成,返回的是一个常量表达式,它的结果是在编译时确定的;strlen函数在运行时期根据传入的字符串动态计算其长度,返回的是字符串的有效字符个数。

7. strlen(“\0”) 和sizeof(“\0”)的“陷阱”

写出下面程序的输出结果:

a = 0,b = 2,a1段错误,b1 = 4
在这里插入图片描述

8. 什么是预编译,什么时候需要预编译

答:①编译前处理以#开头的指令,例如:#include编译器会拷贝已包含的头文件代码;#define宏定义的替换;以及#ifndef进行的条件处理。预编译指令可以放在程序中的任何位置。
②对于总是经常使用但不经常改动的大型代码,程序由多个模块组成,所有模块都使用相同的编译选项,这种情况下可以将包含文件预编译成一个预编译头。

9. C语言中的预编译运算符#和##的区别?

  • #(字符串化运算符):
  • 用于将宏参数转换为字符串常量。
  • 示例:假设有一个宏定义 #define STR(x) #x,当使用该宏时,STR(test) 将被展开为 “test” 字符串常量。
  • ##(连接运算符):
  • 用于将两个标识连接在一起形成一个新的标识。
  • 示例:假设有一个宏定义 #define CONCAT(x, y) x##y,当使用该宏时,CONCAT(a, b) 将替换为标识符ab。
  • 如以下代码:
#include <stdio.h>

#define TEST1(S)        (#S)
#define TEST2(S1,S2)    (S1 ## S2)

int main(){
    
    
    char *ab = "HelloWorld";
    printf("%s\n",TEST1(1));
    printf("%s\n", TEST2(a,b));
    return 0;
}

在这里插入图片描述

10. 如何避免头文件被重复包含?

  • 使用#ifndef、#define和#endif

11. 局部变量和全部变量能否重名?

答:可以,局部变量会屏蔽全局变量。当局部变量和全局变量重名,在函数内引用该变量时,会用到同名的局部变量,而不会用到全局变量。(在C++中可使用作用域解析运算符 ::界定区分)

12. 指针在内存中占多少个字节?

答:指针占多少个字节与地址总线的位数有关,32位的地址总线指针所占大小位4个字节;64位的地址总线指针占8个字节

13. 请写一个标准宏MIN,实现传入两个参数返回较小的一个。

  • #define MIN(A,B) ((A) <= (B) ? (A) : (B))

14. 给出下列变量的定义

定义一个整型数 int a;
定义一个指向整型数的指针 int *a;
定义一个指向指针的指针,它指向的指针指向一个整型数 int **a;
定义一个有10个整型数的数组 int a[10];
定义一个有10个指针的数组,每个指针指向一个整型数 int *p[10];
定义一个指向有10个整型数的数组的指针 int (*p)[10];
定义一个指向指针的指针,被指向的指针指向一个有10个整型数的数组 int *(*p)[10];() / int (**a)[10];(对)
定义一个指向数组的指针,数组中有10个整型指针 int *((*p1)[10]);(我的答案) / int *(*p2)[10];(参考答案)
定义一个指向函数的指针,该函数只有一个整型参数且返回一个整型数 int (*p)(int);
定义一个有10个指针的数组,每个数组指向一个函数,该函数只有一个整型参数且返回一个整型数 int (*p[10])(int);
定义一个函数指针,指向的函数有两个整型参数且返回一个函数指针,返回的函数指针指向有一个整型参数且返回整型数的函数 int(* (*p)(int,int))(int)

15. 结构体字节对齐

  • 结构体变量的首地址是最长成员变量的整数倍
  • 每个成员变量的地址空间必须对齐
    在这里插入图片描述

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_54429787/article/details/131533881