兆易创新
Linux中使用mkdir命令创建新的目录时,在其父目录不在时先创建父目录的选项:
- -m :–mode=模式,建立目录的时候同时设置目录的权限。
- -p:–parents若所建立的上层目录目前尚未建立,则会一并建立上层目录。
- -v:–verbose每次创建新目录都显示信息。
- -h:–help帮助信息
cond1 && cond2 || cond3
- cond1为假时,不判断cond2,直接判断cond3。
- cond1为真时,如果cond2为真,不判断cond3。
- cond1为真时,如果cond2为假,判断cond3。
fork()调用的一个奇妙之处在于它仅仅被调用一次,却能够返回两次。
- 在父进程中,返回的是创建新的子进程的ID号。
- 子进程中,返回的是0。
- 如果出现错误,fork返回一个负值。
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。子进程中,fork函数返回0,父进程中,fork返回新创建的子进程的进程ID。我们可以通过fork的返回值来判断当前进程是子进程还是父进程。
通常情况下,二维数组的每一行分别使用一个字符串进行初始化。
char c[3][8] = {{"apple"}, {"banana"}, {"orange"}};
等价于
char c[3][8] = {"apple", "banana", "orange"};
简述常量指针和指针常量的区别
const int *p; //常量指针,指针指向的值不可以修改
int* const p; //指针常量,指针的指向不可修改,必须初始化
如何避免头文件被重复包含
条件编译
#ifndef HEADERNAME_H
#define HEADERNAME_H//头文件内容#endif
#pragma once
指定当前文件在构建时只被包含一次,这样就可以减少构建时间,因为加入#pragma once后,编译器在打开读取第一个#include模块后,就不会再打开或读取随后出现的相同模块。
#define STR(x) #x
#会把参数转换为字符串。
结构体中成员变量在内存中存储的其实是偏移地址,也就是说结构体的首地址+成员变量的偏移地址 = 结构体成员变量的起始地址。
C中运算有规定,如果整型变量间进行数据运算,只要有一个变量是无符号的,结果就按无符号处理。
联发科
j的值不可以修改,但int* const p4 = &j;代表指针常量p4等于j的地址,通过*p4就可以引起j的值改变。
内存的数据带宽计算公式: 数据带宽=内存的数据传输频率x内存数据总线位/8.
用户进程通常只能访问用户空间的虚拟地址,不能访问内核空间的虚拟地址。
Linux中使用buddy system算法管理页外内存碎片,使用slub算法管理页内内存碎片。
slub算法是使用一个哈希表来管理页内内存碎片,哈希表的键是空闲内存块的大小,值是空闲内存块的地址。
以下哪些事件会导致进程的创建
- 系统初始化(查看进程Linux用ps命令,Windows用任务管理器,前台进程负责与用户交互,后台运行的进程与用户无关,运行在后台并且只在需要时唤醒的进程,称为守护进程,如电子邮件,web页面、新闻、打印)。
- 一个进程在运行过程中开启了子进程(fork系统调用)
- 用户的交互式请求,而创建了一个新进程(如用户鼠标双击一款软件,如QQ等)
- 一个批处理作业的初始化(只有在大型机的批处理系统中应用)
无论哪一种,新进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的。
计算机体系结构主要研究软件、硬件功能分配和对软件、硬件界面的确定。
现代计算机处理结构按照指令系统方式划分,可分为复杂指令集计算机和精简指令集计算机。
单指令流单数据流计算机的每个机器周期最多执行一条指令。
RISC技术对比CISC最大的区别就是对CPI的精简。
使用strlen()求某个字符串的长度时不包括结尾字符’\0’,使用sizeof()求内存空间时,包括。
strlen用来计算字符串的长度(在C/C++中,字符串是以’\0’作为结束符的),它从内存的某个位置(可以是字符串开头,中间位置)开始扫描直到碰到第一个字符串结束符\0为止,然后返回计数器值。
#include <stdio.h>
int func(int i){int cnt = 0;while(i){cnt++;i = i & (i-1);}
}
i & (i-1);将i的二进制表示中最右边的1都置为0。
2021转换成二进制有几个1就会走几次循环。2021一共有8个1,走8次循环。
(n > 0 && (n & n-1)) == 0判断n是不是2的次幂。
p.b,.的优先级高于,*p.b = *(p.b)
结构体指针,访问成员时就用->;结构体变量,访问成员时就用.
this指针的特点
- 每个当前对象都含有一个指向该对象的this指针。this指针只能在类的成员函数中使用,在全局函数,静态成员函数中不能使用。
- this指针是在成员函数的开始前构造,并在成员函数的结束后清除。
- this指针因编译器不同而有不同位置,可能是寄存器或是全局变量。
- 虚函数才会放到函数表中。
double转int回直接舍去小数部分。
内存泄漏,分配给其他变量的内存就会减小。
我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身,此时p就是一个野指针。
free()释放的是指针指向的内存,不是指针!
指针是一个变量,只有程序结束时才被销毁,释放了内存空间后,指向这块空间的指针还是存在,只不过现在指针指向的内容是未定义。
因此,释放内存后把指针指向NULL,防止指针在后面不小心被解引用了。
malloc()从哪里获得了空间?
malloc()从堆里面获得空间,也就是说函数返回的指针是指向堆里面的一块内存,操作系统中有一个记录空闲内存地址的链表,当操作系统收到程序的申请时,就会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该节点从空闲节点链表中删除,并将该结点的空间分配给程序。
计算一天是该年的第几天
比较容易想到的方式是查表,将一年中每个月份的天数放进数组中。
#include <stdio.h>int b1[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
int b2[12] = {31,29,31,30,31,30,31,31,30,31,30,31};int add(int months, int flag){int i,j = 0;if(flag){for(i=0; i<months; i++){j += b2[i]}}else{j += b1[i];}return j;
}int main()
{int years, months, days;//闰年标志位int flag = 0;scanf("%d,%d,%d", &years, &months, &days);if((years%4 == 0 && years%100 != 0) || (years%40 == 0)){flag = 1;}
}
冒泡排序在最好情况下,数组已经有序,只需要进行一次遍历就能确定数组有序,这种情况下,冒泡排序只需要进行n次比较。
最坏情况下,数组逆序,O(n²)
快速排序,最好情况下,每次选择的基准都能将数组平衡地分成两半,快速排序的递归树O(log n),每层需要进行n次比较。
最坏情况下,每次选择的基准总是数组中的最小或最大元素时,快速排序退化为链表,高度为n,每层需要进行n次比较。
将两个各有n各元素的有序表归并成一个有序表,最少的比较次数是n。
归并排序是将两个或两个以上的有序子表合并成一个新的有序表。
依次从小到大取每个序列中的元素比较,较小的一个放进新序列,取完一个有序序列中的所有元素后,再把剩下一个序列中的元素放进新序列的后面的即可。
最好情况下是一个有序序列中最小元素大于另一个有序序列中所有元素,这样只需要比较n次。
将递归算法转换为非递归算法需要栈。
同个进程的不同线程下不能被共享的是?
线程栈空间,每个线程都有自己的独立栈,用于存储局部变量,函数调用信息和返回地址等。
线程的栈是独立的,不能被其它线程直接访问和共享。如果一个线程试图访问另一个线程的栈,这将导致不可知的行为或崩溃。
软链接和硬链接
软链接,也称符号链接或快捷方式,是一个指向另一个文件或目录的文件。它包含的是目标文件的路径名。
软链接可以指向目录或文件。
软链接可以跨文件系统。
如果目标文件被删除,软链接变成悬挂的,指向一个不存在的文件。
软链接的大小是目标文件路径名的长度。
ln -s <target> <link_name>
删除软链接不会影响目标文件,只删除链接本身。
硬链接是直接指向文件数据的一个文件系统条目。它与原文件共享相同的inode节点,因此它们实际上是同一个文件的多个目录条目。
硬链接只能指向文件,不能指向目录。
硬链接不能跨文件系统。
删除任何一个硬链接不会影响文件数据,文件数据只有在所有硬链接都被删除后才被删除。
硬链接的大小是目标文件内容的大小,它们实际上是同一个文件。
ln <target> <link_name>
删除一个硬链接不会影响其他链接和文件本身。
对比
软链接指向路径名,可以指向文件或目录,指向路径可变。
硬链接直接指向文件数据,只能指向文件,指向数据不变。
软链接可以跨文件系统。
硬链接不可以跨文件系统。
软链接依赖目标文件,目标文件删除后软链接失效。
硬链接不依赖目标文件,任一链接删除后文件仍然存在。
软链接有自己的inode,大小为目标路径名长度。
硬链接共享自己的inode,大小为文件内容长度。
指针
指针数组:首先是一个数组,数组里都存储着指针。 int *arr[10];
数组指针:首先是一个指针,执向一个一维数组。int (*arr)[10];
函数指针:回调中经常使用函数指针。
int add(int a, int b){return a+b;
}
int (*func)(int, int) = add;
指针函数:普通函数,返回值是指针形式。int *func(int a);
指针和引用
指针
指针是一个变量,存储的是另一个变量的内存地址。
指针可以在其生命周期内指向不同的变量。
可以为空。
需要解引用来访问指针指向的变量。
需要显示初始化。
可以进行算数运算:指针可以进行加减运算来访问数组中的不同元素。
引用
引用是一个变量的别名,必须在创建时被初始化,并且在其生命周期内不能改变指向。
不能重新赋值:引用在其生命周期内始终引用同一个变量。
不能为NULL,必须绑定到一个有效的变量上。
无需解引用,因为编译器会自动处理引用的解引用操作。
必须在初始化赋值。
C++标准库map的底层实现为红黑树。
#include <stdio.h>
void killsame(char *o, char *n){int i = 0, j = 0, k = 0;int lable;while(o[i] != '\0'){label = 1;for(j=0; j<i; j++){if(o[i] == n[j]){label = 0;}}if(label){n[k++] = o[i];}i++;}n[k] = '\0';puts(n);
}
int main(){char old[126];char new[126];scanf("%s", old);killsame(old, new);return 0;
}
x86汇编和ARM汇编的区别
x86汇编和ARM汇编是两种不同的汇编语言,用于不同的处理器架构。
x86基于复杂指令集计算架构,常用于桌面电脑和服务器,支持丰富的指令集和多种寻址模式。
ARM汇编基于精简指令集计算架构,常用于移动设备、嵌入式系统和单板计算机等。指令集精简,寻址模式相对简单。
- x86指令集更加复杂,ARM更加精简
- x86寄存器相对较少而且功能比较专用,ARM寄存器较多且通用。
- x86指令长度可变,ARM指令长度固定。
- ARM提供灵活的条件执行。
自旋锁和信号量
自旋锁和信号量是两种常用的同步机制,用于控制多线程或多进程对共享资源的访问。
自旋锁是一种低级同步原语,当一个线程尝试获取自旋锁,而锁被其它线程持有时,它将保持循环并不断检查锁的状态,直到成功获取锁。
- 忙等待:在等待期间,线程会一直占用CPU资源,不进行上下文切换。
- 无阻塞:不会导致线程挂起
- 适用于临界区非常短的情况,因为忙等待会消耗CPU资源,如果临界区较长,自旋锁会导致CPU资源浪费。
- 实现简单:自旋锁依赖于原子操作。
信号量是一种更高级的同步机制,信号量的值表示可以访问资源的线程数量。
- 阻塞等待:当一个线程试图获取信号量而信号量的值为0时,该线程会被挂起,直到信号量的值大于0。
- 上下文切换:阻塞等待的线程会引起上下文切换。
- 适用于临界区较长或需要控制同时访问资源的线程数量的情况。
计数型信号量:允许多个线程同时访问共享资源。
二进制信号量:类似于互斥锁,只允许一个线程访问共享资源。
调用函数时,有哪些需要压栈
- 函数参数:传递给函数的参数需要压栈,这样调用的函数才能访问这些参数。在一些系统中,前几个参数可能通过寄存器传递,而超过的参数通过堆栈传递。
- 返回地址:调用函数后,程序继续执行的位置。在函数调用时,程序计数器PC的值会被压栈,以便在函数完成后能正确返回。
- 调用者保存的寄存器。
- 被调用者保存的寄存器。