嵌入式面试总结

C语言宏中#和##的用法

#字符串化操作符

将宏定义中的传入参数名转换成用一对双括号括起来的字符串。其只能用于有传入参数的宏定义中,且必须置于宏定义体中的参数名前。

c 复制代码
#define example(instr) printf("the input string is:%s\n", #instr)

example(instr);
c 复制代码
#define example(instr) #instr

string str = example(abc);

##符号连接操作符

c 复制代码
#define exampleNum(n) num##n

int num = 9;
exampleNum(num);
  • 当用##连接形参时,##前后的空格可有可无。

关键字volatile的含义,并举出三个不同的例子

volatile关键字用于告诉编译器一个变量的值可能随时被程序外部的因素修改,不要对该变量进行优化。

具体来说,编译器在生成代码时会注意到这个变量,在每次使用时都需要重新读取它的值,而不是使用缓存的值。

多线程编程

在多线程环境中,如果一个变量可能被多个线程修改和读取,可以将其声明为volatile,以便每个线程都能获得最新值。

cpp 复制代码
public class VolatileExample{
	private volatile boolean flag = false;
	public void setFlag(){
		flag = true;
	}

	public boolean getFlag(){
		return flag;
	}
} 

嵌入式系统

在嵌入式系统中,硬件寄存器的值可能被外部设备改变。这种情况下,将这些寄存器对应的变量声明为volatile。

c 复制代码
volatile int * port = (int *)0x1234;

信号处理

在处理信号时,处理信号程序可能会修改某些全局变量,为了确定这些变量在主程序中能正确读取,声明为volatile。

c 复制代码
#include <signal.h>
#include <stdio.h>

volatile sig_atomic_t signal_received = 0;

void signal_handler(int signal){
	signal_received = 1;
}

int main(){
	signal(SIGINT, signal_handler);
	while(!signal_received){
		
	}
	printf("Signal received!\n");
    return 0
}

关键字static的作用

在函数内部使用

在函数内部声明的static变量只会在函数第一次调用时初始化,并且在函数的后续调用中保持值不变。这种变量的声明周期贯穿整个运行过程,但其作用域仅限于函数内部。

c 复制代码
void counter(){
	static int count = 0;
	count++;
}

在类的成员变量中使用

声明为static的成员变量属于类而不是类的对象。所有对象共享同一个静态成员变量。

cpp 复制代码
class MyClass{
public:	
	static int count;

	void increment(){
		count++;
	}
};

int MyClass::count = 0; //静态成员变量需要在类外部定义

在类的静态成员函数中使用

静态成员函数属于类而不是类的对象,可以在不创建对象的情况下调用静态成员函数。
静态成员函数只能访问静态成员变量和其他静态成员函数。

cpp 复制代码
class MyClass{
public:
	static int val;
	static void addVal(){
		val++;
	}
}

在文件作用域中使用static

在文件的全局作用域中声明static变量或函数,可以将其作用域限制在声明它的文件内,从而实现文件私有。

为什么static变量只初始化一次?

静态变量的存储位置

静态变量通常存储在程序的静态存储区中。静态存储区是一块独立的内存区域,其生存期与整个程序的生命周期相同。意味着,静态变量一旦被声明并初始化,就会一直存在于内存中,直到程序结束。

静态存储区用于存储全局变量和静态变量。全局变量是指在函数体之外声明的变量。全局变量作用域是整个程序,静态变量作用域是整个函数。

extern"C"的作用

extern "C"的主要作用是为了能够正确实现C++代码调用其他C语言代码,加上extern "C"后,会指示编译器这部分代码按C语言的进行编译。

const的作用

定义变量为常量,在定义的时候必须初始化,且后续不能修改。

修饰函数的参数,表示在函数体内不能修改这个参数的值。

修饰函数的返回值

  1. 如果给用const修饰返回值的类型为指针,那么函数返回值的内容是不能修改的,而且这个返回值只能赋给const修饰的指针。
c 复制代码
const char* getStr();
const char* str = getStr();
  1. 如果const修饰普通的返回值,如返回int变量,这个返回值是一个临时变量,在函数调用结束后这个临时变量的生命周期就结束了,因此这些返回值修饰为const是没有意义的。

宏定义与const区别

new/delete与malloc/free的区别

  1. new 、delete是C++中的操作符,而malloc、free是标准库函数。
  2. new、delete是类型安全的,new知道要分配对象的类型并调用构造函数,malloc只知道字节大小,不调用构造函数。
  3. new调用构造函数,delete调用析构函数。malloc、free不调用析构函数。
  4. new返回指定类型的指针,malloc返回void*,需要强制类型转换。
  5. new可以初始化对象,malloc不会初始化分配的内存。
  6. new在分配失败时抛出异常,malloc在分配失败时返回NULL。
  7. new[]和delete[]用于分配和释放数组,malloc和free没有内置的数组分配和释放支持。

strlen("\0")与sizeof("\0")

c 复制代码
strlen("\0") = 0
sizeof("\0") = 2

strlen用来计算字符串的长度(在C/C++中,字符串是以"\0"作为结束符的),它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描直到碰到第一个字符串结束符\0为止。

返回计数器值sizeof是C语言的关键字,它以字节的形式给出了其操作数的存储大小。

sizeof和strlen区别

  1. sizeof是运算符,而strlen是函数,sizeof后面如果是类型,则必须加括弧,如果是变量名,则可以不加括弧。
  2. sizeof运算符的结果类型是size_t,它在头文件中typedef为unsigned int类型。

当数组作为参数传给函数时,传递的是指针,而不是数组,即传递的是数组的首地址。

不使用sizeof,如何求int占用的字节数?

c 复制代码
#define MYSIZEOF(value) (char *)(&value+1) - (char*)(&value)

C语言中struct和union的区别

struct

  • 内存分配:struct中每个成员都有自己的内存空间,所有成员的内存空间是连续分配的。
  • 大小:struct大小是所有成员大小之和,并可能包含填充字节(为了对齐)。
  • 访问:可以同时访问结构体中的所有成员。
  • 用途:用于表示具有多种属性的实体,比如一个人的姓名、年龄和地址。

union

  • 所有成员共享同一块内存空间,大小是其最大成员的大小。
  • 任意时刻只能使用联合体中的一个成员,修改一个成员会影响其他成员。
  • 用途:用于需要节省内存的场景,或者同一块内存需要存储不同类型数据的场景。

只出现一次的数字二

某个元素只出现一次,其余每个元素出现三次。

用线性时间复杂度和常数级空间来解决此问题。

依次确定每一个二进制位

由于数组中的元素都在int(32位整数)范围内,因此我们可以依次计算答案的每一个二进制位是0还是1。

考虑答案的第i个二进制位,对于非答案的元素,每个二进制位要么是3个0,要么是3个1,无论哪一种情况,它们的和都是3的倍数。

答案的第i个二进制位就是数组中所有元素的第i个二进制位之和除以3的余数。

Pow(x, n)

c 复制代码
class Solution {
public:
    double func(double x, int n){
        if(n == 0){
            return 1.0;
        }
        double y = func(x, n/2);
        return n%2 ? y*y*x : y*y;
    }
    double myPow(double x, int n) {
        return n>=0 ? func(x,n) : 1/func(x,n);
    }
};

快速排序

快速排序是基于分治策略的一个排序算法。

cpp 复制代码
int Partition(vector<int>& nums, int left, int right){
	int i = left, j = right + 1;
	int x = nums[left];
	while(true){
		while(nums[++i] < x && i < r);
		while(nums[--j] > x);
		if(i >= j){
			break;
		}
		swap(a[i], a[j]);
	}
	a[left] = a[j];
	a[j] = x;
	return j;
}

void quickSort(vector<int>& nums, int left, int right){
	if(left <right){
		int index = Partition(nums, left, index-1);
		quickSort(nums, left, index-1);
		quickSort(nums, index+1, right);
	}
}

数组中的第K个最大元素

cpp 复制代码
class Solution {
public:
    int quickSelect(vector<int>& nums, int left, int right, int k){
        if(left == right){
            return nums[k];
        }
        int i = left-1, j = right+1;
        int partion = nums[left];
        while(i < j){
            while(nums[++i] < partion);
            while(nums[--j] > partion);
            if(i < j){
                swap(nums[i], nums[j]);
            }
        }
        if(k <= j){
            return quickSelect(nums, left, j, k);
        }
        return quickSelect(nums, j+1, right, k);
    }
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        return quickSelect(nums, 0, n-1, n-k);
    }
};

找出字符串中第一个匹配项的下标

cpp 复制代码
vector<int> getNextArray(string needle){
	int n == needle.size();
	if(n == 1){
		return {-1};
	}else if(n == 2){
		return {-1, 0};
	}

	vector<int> res(n);
	res[0] = -1;
	res[1] = 0;
	int i = 2;
	int cnt = 0;
	while(i < n){
		if(needle[i-1] == needle[cnt]){
			res[i++] = ++cnt;
		}else if(cnt > 0){
			cnt = res[cnt];
		}else{
			res[i++] = 0;
		}
	}
}

int str(string haystack, string needle){
	int n = haystack.size(), m = needle.size();
	if(n < m){
		return -1;
	}

	vector<int> nextArray = getNextArray(needle);
	int i1 = 0, i2 = 0;
	while(i1 < n && i2 < m){
		if(haystack[i1] == needle[i2]){
			i1++;
			i2++;
		}else if(i2 > 0){
			i2 = nextArray[i2];
		}else{
			i1++;
		}
	}
	return i2 == m ? i1-i2 : -1;
}
相关推荐
南东山人2 小时前
一文说清:C和C++混合编程
c语言·c++
Ysjt | 深5 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__5 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
Microsoft Word5 小时前
c++基础语法
开发语言·c++·算法
一只小小汤圆5 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz6 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE6 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++
ö Constancy7 小时前
c++ 笔记
开发语言·c++
fengbizhe7 小时前
笔试-笔记2
c++·笔记