嵌入式面试2(c相关)

这篇具有很好参考价值的文章主要介绍了嵌入式面试2(c相关)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1.C语言中static、const、volatile关键字用法区别;

static的用法(定义和用途)

const的用法(定义和用途)

volatile (英文意思为易变的) 作用和用法:

2.C语言中,const 和 static 的区别,char * const 和 const * char的区别

3.变量声明和定义的区别:

4.C语言编译后的内存分布

5.C语言内存分区,未定义的全局变存放在哪个区

6.静态局部变量存储在静态区,那么静态区的创建和消失是在什么时候?

7.变量加入static以后在内存中存储位置的变化

8.在windows里面运行了多个进程,其中一个进程执行完了,它的静态区会如何处理

9.连续调用同一个函数两次,它的局部变量初始化结果是否会一致?

10.静态局部变量与局部变量的区别?为什么局部变量未定义时,每次初始化的结果是不确定的?是个真随机数还是个伪随机数? 

11.快排时间复杂度怎么计算的

12.用熟悉的编译器手写栈和冒泡排序


1.C语言中static、const、volatile关键字用法区别;

static的用法(定义和用途)


1)用static修饰局部变量:使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中。
2)用static修饰全局变量:使其只在本文件内部有效,而其他文件不可连接或引用该变量。
3)用static修饰函数:对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的(这一点在大工程中很重要很重要,避免很多麻烦,很常见)。这样的函数又叫作静态函数。使用 静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。

const的用法(定义和用途)


const主要用来修饰变量、函数形参和类成员函数:
1)用const修饰常量:定义时就初始化,以后不能更改。
2)用const修饰形参:func(const int a){};该形参在函数里不能改变
3)用const修饰类成员函数:该函数对成员变量只能进行只读操作,就是const类成员函数是不能修改成员变量的数值的。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

1.const int a;
2.int const a;
3.const int *a;
4.int * const a;
5.int const * a const;

前两个的作用是一样,a是一个常整型数。

常指针从右往左读,以*为分界

第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。
第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。
最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

volatile (英文意思为易变的) 作用和用法:


        一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量在内存中的值,而不是使用保存在寄存器里的备份(虽然读写寄存器比读写内存快)。
以下几种情况都会用到volatile:

并行设备的硬件寄存器(如:状态寄存器)
一个中断服务子程序中会访问到的非自动变量  
多线程应用中被几个任务共享的变量

2.C语言中,const 和 static 的区别,char * const 和 const * char的区别

const 关键字用于定义常量,即在程序运行期间不可修改的值。const 可以用于修饰变量、函数参数和函数返回值。使用 const 可以提高程序的可读性和安全性。

static 关键字用于定义静态变量或函数。静态变量在程序运行期间只会被初始化一次,而静态函数只能在当前文件中被调用。使用 static 可以控制变量和函数的作用域,避免命名冲突和不必要的访问。

char * const 和 const * char 的区别在于指针指向的内容是否可以被修改。char * const 表示指针本身是常量,即指针指向的地址不可修改,但指针指向的内容可以修改;而 const * char 表示指针指向的内容是常量,即指针指向的内容不可修改,但指针本身可以修改。

3.C语言字节对齐、结构体对齐

什么是字节对齐?

在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然边界(alignment)分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

为了使CPU能够对变量进行快速的访问,变量的起始地址应该具有某些特性,即所谓的”对齐”. 比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整除.

字节对齐有什么作用?

需要字节对齐的根本原因在于CPU访问数据的效率问题。假设变量的地址不是自然对齐,取它的值的话需要访问多次内存,而如果变量在自然对齐位置上,则只要一次就可以取出数据。

正确处理字节对齐
  
   对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:
  数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。
  联合 :按其包含的长度最大的数据类型对齐。
  结构体: 结构体中每个数据类型都要对齐。

结构体各成员的起始位置相对于结构体变量的起始位置的偏移量,应该为该结构体成员类型所占字节数与pack(n)的n取最小值的倍数
• 结构体变量所占字节数应该是结构体各成员所占字节数的最大值与pack(n)的n取最小值

举例说明

例1

struct test
{
char x1;
short x2;
float x3;
char x4;
};

由于编译器默认情况下会对这个struct作自然边界(有人说“自然对界”我觉得边界更顺口)对齐,结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然边界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大边界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。

例2

#pragma pack(1) //让编译器对这个结构作1字节对齐
struct test
{
char x1;
short x2;
float x3;
char x4;
};
#pragma pack() //取消1字节对齐,恢复为默认4字节对齐

这时候sizeof(struct test)的值为8。

变量声明和定义的区别:

变量声明只是变量的说明,在程序中不需要为其分配内存空间;而变量定义是为变量分配内存空间并初始化

4.C语言编译后的内存分布

C语言编译后的内存分布通常包括以下几个部分:

1. 代码段(Text Segment):也称为只读代码段,存放程序的机器指令。在程序运行时,代码段是只读的,不允许修改。

2. 数据段(Data Segment):存放已初始化的全局变量和静态变量。在程序加载时,数据段的大小是固定的。

3. BSS段(Block Started by Symbol):存放未初始化的全局变量和静态变量。在程序加载时,BSS段的大小是固定的,但是实际上并不占用磁盘空间,只是在程序运行时根据需要分配内存。

4. 堆(Heap):用于动态分配内存。在程序运行时,可以通过函数如malloc()和free()来动态地分配和释放堆内存。

5. 栈(Stack):用于存放函数的局部变量、函数参数和函数调用的上下文信息。栈是一种后进先出(LIFO)的数据结构,每次函数调用时,会在栈上分配一块内存,函数返回时会释放这块内存。

6. 环境变量区域:存放环境变量的值。

7. 命令行参数区域:存放命令行参数的值

5.C语言内存分区,未定义的全局变存放在哪个区

bss段:未初始化或初始化未0的全局变量和静态变量,具体体现为 一个占位符,不占用.exe文件空间,其内容由操作系统初始化/清零

6.静态局部变量存储在静态区,那么静态区的创建和消失是在什么时候?

静态区是在程序运行时由操作系统分配的一块内存空间,它的创建和消失与程序的生命周期有关。静态局部变量在程序运行时只会被初始化一次,它们的内存空间会一直存在直到程序结束。因此,静态区的创建和消失是在程序启动时创建,在程序结束时销毁。

7.变量加入static以后在内存中存储位置的变化

当一个变量被声明为static时,它的存储位置会从栈(stack)或堆(heap)转移到静态存储区(static storage area),也称为全局数据区(global data area)。

在栈或堆中声明的变量是在函数调用时动态分配的,当函数返回时,它们的存储空间会被释放。而在静态存储区中声明的变量则在程序运行期间一直存在,直到程序结束才会被释放。

因此,使用static声明的变量可以在多个函数之间共享,而不需要通过参数传递或全局变量来实现。此外,静态变量的初始值只会被赋值一次,即使函数被多次调用,它的值也会保持不变。

8.在windows里面运行了多个进程,其中一个进程执行完了,它的静态区会如何处理


当一个进程执行完毕后,操作系统会回收该进程的资源,包括其地址空间。在回收过程中,操作系统会释放该进程所占用的内存空间,包括静态区。释放静态区的具体处理方式取决于操作系统的实现。

一般情况下,操作系统会将静态区的内存空间标记为可用,以便其他进程可以重新使用。但是,静态区中的数据并不会立即被清除,而是等待下一个进程使用该内存空间时进行覆盖。因此,在下一个进程使用该静态区之前,原来进程的数据可能仍然存在于内存中。

9.连续调用同一个函数两次,它的局部变量初始化结果是否会一致?

一般情况下,连续调用同一个函数两次,它的局部变量初始化结果不会一致。这是因为每次函数调用时,局部变量都会重新初始化。如果函数中的局部变量没有被显式地初始化,那么它们的值将是未定义的,可能会导致不同的结果。但是,如果函数中的局部变量被显式地初始化为相同的值,那么它们的值将是一致的。

10.静态局部变量与局部变量的区别?为什么局部变量未定义时,每次初始化的结果是不确定的?是个真随机数还是个伪随机数? 

静态局部变量和局部变量的区别在于它们的生命周期和作用域不同。静态局部变量在函数内部定义,但是它们的生命周期是整个程序运行期间,而不是函数调用期间。而局部变量的生命周期只在函数调用期间存在。

当局部变量未定义时,每次初始化的结果是不确定的。这是因为未初始化的局部变量的值是未定义的,它们的值取决于它们在内存中的位置和之前存储在该位置的值。因此,每次初始化的结果可能是不同的。

这些值通常是伪随机数,而不是真随机数。伪随机数是通过算法生成的数字序列,看起来像是随机的,但实际上是可预测的。在C语言中,未初始化的局部变量通常会被分配为内存中的随机值,这些值通常是伪随机数。

11.快排时间复杂度怎么计算的

快速排序的时间复杂度可以通过递归树来计算。在最坏情况下,每次划分都只能将序列分成一个元素和n-1个元素,这样递归树的深度就是n,每层的时间复杂度都是O(n),因此最坏情况下的时间复杂度是O(n^2)。

在平均情况下,每次划分都能将序列分成大小大致相等的两个子序列,递归树的深度是logn,每层的时间复杂度都是O(n),因此平均情况下的时间复杂度是O(nlogn)。

最好情况下,每次划分都能将序列分成大小相等的两个子序列,递归树的深度是logn,每层的时间复杂度都是O(n),因此最好情况下的时间复杂度也是O(nlogn)。

因此,快速排序的时间复杂度在最坏情况下是O(n^2),在平均情况下和最好情况下都是O(nlogn)。文章来源地址https://www.toymoban.com/news/detail-714284.html

12.用熟悉的编译器手写栈和冒泡排序

#define MAX_SIZE 100

typedef struct {
    int data[MAX_SIZE];
    int top;
} Stack;

// 初始化栈
void initStack(Stack *stack) {
    stack->top = -1;
}

// 判断栈是否为空
int isEmpty(Stack *stack) {
    return stack->top == -1;
}

// 判断栈是否已满
int isFull(Stack *stack) {
    return stack->top == MAX_SIZE - 1;
}

// 入栈
void push(Stack *stack, int value) {
    if (isFull(stack)) {
        printf("Stack is full. Cannot push element.
");
        return;
    }
    stack->data[++stack->top] = value;
}

// 出栈
int pop(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty. Cannot pop element.
");
        return -1;
    }
    return stack->data[stack->top--];
}

// 获取栈顶元素
int peek(Stack *stack) {
    if (isEmpty(stack)) {
        printf("Stack is empty. Cannot peek element.
");
        return -1;
    }
    return stack->data[stack->top];
}

int main() {
    Stack stack;
    initStack(&stack);

    push(&stack, 1);
    push(&stack, 2);
    push(&stack, 3);

    printf("Top element: %d
", peek(&stack));

    printf("Popped element: %d
", pop(&stack));
    printf("Popped element: %d
", pop(&stack));

    printf("Top element: %d
", peek(&stack));

    return 0;
}
void bubble_sort(int arr[], int n) {
    int i, j, temp;
    for (i = 0; i < n - 1; i++) {
        for (j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    int arr[] = {5, 2, 8, 3, 1, 6};
    int n = sizeof(arr) / sizeof(arr[0]);
    int i;
    printf("Before sorting: ");
    for (i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("
");
    bubble_sort(arr, n);
    printf("After sorting: ");
    for (i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("
");
    return 0;
}

到了这里,关于嵌入式面试2(c相关)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式面试2

    ==== 9.写出float x 与“零值”比较的if语句。 if(x 0.000001 x -0.000001) 权重:高 备注:实际编程时要注意 ==== 12、已知一个数组table,用一个宏定义,求出数据的元素个数。 参考答案: #define NTBL (sizeof(table) / sizeof(table[0])) 权重:高 备注:实际工作经常使用 ==== 34、 对(-1.2345)取整是

    2024年02月13日
    浏览(35)
  • 嵌入式面试提问

      现总结下:首先是时钟源输入时钟信号到单片机,然后单片机对输入的时钟信号进行倍频和分频处理,再将处理后的时钟信号输出至系统,外设或外部接口。   先看这张图,最外面的线上的方格是时钟相关的外部接口,OSC接口用于连接外部石英晶振时钟电路,最下面的

    2024年01月24日
    浏览(37)
  • 嵌入式设备显示屏相关概念汇总

    LCD 接口:是一种常见的数字电路接口,支持多种显示器件,如字符型液晶显示器和点阵型液晶显示器等。 VGA 接口:是一种视频接口标准,用于连接显示器和计算机。该接口提供模拟 RGB 信号,支持最高分辨率为 1920x1080。 HDMI 接口:是一种数字音视频接口标准,用于连接高清

    2024年02月01日
    浏览(59)
  • 嵌入式面试常考问题

    1、malloc与new的区别 1、new是操作符,malloc是函数 2、new使用时先分配内存,再调用构造函数,释放时调用析构函数 3、new只能分配实例所占类型的整数倍,malloc可以随意分配。 4、new失败返回异常,malloc返回NULL 2、C语言内存分配的方式 1、静态区分配:编译时分配好,主要储存

    2024年02月17日
    浏览(41)
  • 嵌入式相关开源项目、库、资料------持续更新中

    学习初期最难找的就是找学习资料了,本贴精心汇总了一些嵌入式相关资源,包括但不限于编程语言、单片机、开源项目、物联网、操作系统、Linux、计算机等资源,并且在不断地更新中,致力于打造全网最全的嵌入式资料库。有好的嵌入式相关资源的朋友欢迎做贡献,利人

    2024年02月02日
    浏览(43)
  • 【面试集锦 - 嵌入式 - volatile变量】

    在编程中, volatile 是一个,用于声明一个变量为“易变”的。它告诉编译器,该变量的值可能在程序的控制流之外被修改,因此编译器不应对该变量进行某些优化。 volatile 的作用是: 禁止编译器对变量的读取和写入进行优化,以确保对变量的读取和写入操作是

    2024年02月15日
    浏览(39)
  • 经典30个嵌入式面试问题

    经典30个嵌入式面试问题 嵌入式系统的面试经典问题有很多,以下是其中的30个常见问题: 1.nbsp;什么是嵌入式系统? 2.nbsp;嵌入式系统和普通计算机系统有什么区别? 3.nbsp;嵌入式系统的主要应用领域有哪些? 4.nbsp;嵌入式系统的设计流程是什么?我这里有单片机、嵌入式、

    2024年02月09日
    浏览(45)
  • 【面试集锦 - 嵌入式软件 - C语言】

    指针函数(Pointer to a Function)和函数指针(Function Pointer)是在C和C++中经常使用的概念,它们虽然名称相似,但是在用法和作用上有一些区别。 指针函数 指针函数是指返回值为指针类型的函数。换句话说,它是一个函数,其返回类型是一个指针。指针函数可以用来实现动态分

    2024年02月07日
    浏览(50)
  • 【嵌入式Linux驱动】驱动开发调试相关的关系记录

    https://www.processon.com/mindmap/64537772b546c76a2f37bd2f

    2024年02月02日
    浏览(52)
  • 嵌入式面试刷题(day3)

    本篇文章我们继续讲解嵌入式面试刷题,给大家继续分享嵌入式中的面试笔试经验和技巧。 在C语言中,可以使用以下代码来比较两个float类型的数据是否相同: 上述代码中,通过计算两个浮点数之差的绝对值,并与给定的误差范围进行比较。如果差值小于等于指定的误差范

    2024年02月14日
    浏览(72)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包