Cracking Digital VLSI Verification Interview 第三章

欢迎关注个人公众号摸鱼范式,后台回复pdf获得全文的pdf文件

Programming Basics

Basic Programming Concepts

[68] 在任何一种编程语言中,静态(static)变量和自动(automatic)变量,局部(local)变量和全局(global)变量之间有什么区别?

区分这些名词需要两个概念,作用域(scope)和存储持续时间(storage duration),前者定义了在何处可以访问变量,后者定义了在何时可以访问变量。

  • 按照变量的作用域可以区分局部(local)和全局(global)变量。局部变量的作用范围有限,尽在声明它们的代码块中可见。而全局变量在声明后在程序的任何位置都可见。
  • 存储持续时间可以区分自动(automatic)变量和静态(static)变量。静态变量的生命周其一直持续到程序结束,因此可以始终访问。自动变量具有有限的生命周期,只能持续到程序离开定义的块或者作用域为止。

例如:在以下的systemverilog代码中,global_int被声明为类成员,并且在整个类中具有全局作用域,而当取消引用该类的对象时,其生命期结束。global_static变量被声明为静态变量并具有全局作用域整个类以及整个程序的生命周期,即使取消引用类的对象也存在。sum变量对于函数compute()是局部的,并且仅在函数内部可见,并且仅在compute执行时存在, count变量在函数compute()中是局部变量,仅在函数范围内可见,但由于它是静态的,因此即使在多次执行函数compute()之后,它也只有单个副本并保留该值.

class test_class;
    int global_int; //automatic by default
    static global_static; //global static variable
    
    void function compute()
        begin
            static int count; //local static variable
            local int sum; //local automatic variable
            sum = sum +1;
            count = count +sum;
        end
    endfunction
    
endclass

[69] 什么是内联(inline)函数?

内联函数时调用时会进行展开内联的函数,即编译器会将函数调用替换为相应函数代码。如果函数非常小并且在多个地方使用,使用内联函数会更有优势。这么做会提高运行速度,没有调用函数和从函数返回的开销。

例如:在C语言中,定义一个名为max的内联函数,在main内部的每次调用都会通过替换代码实现,而不是函数调用实现。

inline int max(int a, int b) {
    return a > b ? a : b;
} 

main () {
    int a1,a2,a3,b1,b2,b3;
    int c1,c2,c3;
    c1 = max(a1,b1);
    c2 = max(a2,b2);
    c3 = max(a3,b3);
}

[70] 什么是正则表达式?

正则表达式是特殊的字符序列,可以使用特殊的语法帮助用户匹配或查找其他字符串(或字符串集)。 它是用于字符串内模式匹配的最强大的概念之一,广泛用于Perl,Python,Tcl等语言。

[71] 堆和栈的区别是什么?

栈是内存的一块特殊区域,用于存储由函数创建的临时变量。每次函数声明一个新的自动变量时,它将被压入栈,并且每次函数退出时,会删除压入栈的所有变量。所有局部变量都使用栈进行存储,并且时自动管理的,也有大小限制。如果栈的空间不足,则会出现栈溢出错误。

堆则是需要管理的内存区域,程序员需要分配和释放内存,某些语言中是自动完成的。堆通常用于存储静态变量和对象。与栈相比,堆略慢,并且是通过指针应用的,并且可以在任何位置应用堆的变量。堆的大小也是可以更改的,当可用内存是不连续的块时,堆可能会出现碎片问题。

[72] a++和++a的区别是?

++a首先“a”自增,然后返回引用“a”的值。 因此,如果将“++ a”分配给变量,则将使用递增值“a”。

a++首先返回值“a”(当前值为“a”),然后“a”自增。因此,如果将“a ++”分配给变量,则将在分配中使用旧值“a”。

[73] 什么是内存泄漏?

当我们动态分配内存但以某种方式失去到达该内存的方式时,这称为内存泄漏。 在某些编程语言(如C ++)中,应释放(通过调用析构函数)完成的每个内存分配(例如,创建对象),否则,这些内存将泄漏且不再可用。 在某些其他语言(例如SystemVerilog,Java等)中,语言内部机制负责清理内存,并且内存泄漏的可能性较小。

[74] 编译器和解释器的区别是什么?

机器(例如计算机)理解代码是通过二进制的,机器可以理解的二进制代码称之为“机器码”。程序员通常使用高级编程语言(C,C++,Perl,Python)变写计算机程序或者代码。编译器和解释器就是将这些源代码转换为机器代码的程序。

编译器 解释器
扫描整个程序并将整个源代码转换为机器代码 一次扫描并转化一行源代码
需要大量的时间去分析源代码 只需要少量时间用于分析源代码
输出机器专用的二进制码 输出代码是某种中间代码,由另一个程序解释
执行速度更快(计算机硬件运行) 执行更慢(由另一个程序执行)
扫描整个程序后报告错误 一直运行,直到遇到第一个错误,并且停止程序

[75] 静态语言和动态语言的区别是什么?

静态语言:静态语言是一种在编译时固定类型的语言。 这意味着您需要在使用它们之前声明所有变量及其数据类型。 例如:Java,C和SystemVerilog是静态类型的语言。

动态语言:动态语言是一种在执行时确定类型的语言。这与静态类型的语言相反。 例如:VBScript和Python是动态类型的,因此在使用之前不需要声明所有变量及其数据类型。 他们会在首次为变量分配值时弄清楚变量的类型。

[76] 下面关于栈的观点哪个是错的?

  1. 栈只能push或者pop
  2. 可以使用栈实现FIFO
  3. 栈对于嵌套循环,子程序调用很有用
  4. 队长堆算术表达式计算有用

选项2是错的,栈是LIFO而非FIFO,先入后出。

[77] Perl中“use”和“require”的主要区别是?

  • use在编译时起作用,require在运行时起作用
  • use隐式调用了将要加载的模块,require则没有
  • use引入的名称不需要后缀名,而require需要
  • use引入模块的同时,也引入了模块的子模块。而require则不能引入,要再重新声明

[78] 静态内存分配和动态内存分配有什么区别?

静态内存分配 动态内存分配
内存在编译时分配 内存在运行时分配
内存被分配到栈上或者程序的其他部分 内存分配到堆上
不需要释放内存,静态变量的生命周期就是程序的生命周期 需要及时释放内存
固定大小,一旦分配以后内存大小就不能改变 能够改变大小
执行更加快 执行更加慢

[79] 什么是编译预处理命令?

在代码中,预处理器指令是以#开头的行。它们充当预处理程序的指令,预处理程序在代码编译开始之前检查代码。其结果就是替换了源代码中的某些代码。例如:预处理程序指令的常规语法为:#define标识符值每当预处理程序在源代码中遇到“标识符”时,它将用“值”替换,并在编译之前生成新的源代码。

[80] C++代码中"using namespace std"的功能是什么?

namespace是指标识符的各种可见范围。命名空间用关键字namespace 来定义。命名空间是C++的一种机制,用来把单个标识符下的大量有逻辑联系的程序实体(例如类、对象和函数)组合到一起。"std"是"standard"一词的缩写。 standard namespace (std namespace)是一种特殊类型的名称空间,其中保留了所有内置的C ++库(例如字符串,cin,cout,vector等)。 因此,"using namespace std"告诉C ++编译器使用标准C ++库。

[81] 以下两种初始化的方式有什么区别:“int a;” and “const int a;”?

const关键字告诉编译器,该变量或对象一旦进行初始化便不可更改。所以,int a 声明后,后续可以对变量a进行更改,而const int a,后续不可更改

[82] C语言中的关键词volatile是什么意思?

volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。volatile关键字主要在与内存映射的输入输出(硬件)接口时使用。 变量声明为volatile之后,编译器将无法执行任何优化,例如:删除内存分配,将变量缓存在寄存器中或更改分配的执行顺序。

[83] 解释指针的概念

指针是一个变量,其值是另一个变量的地址。星号*表示指针,int * p 告诉编译器变量“p”是一个指针,其值是存储整数变量的存储位置的地址。 同样,float * f; 告诉编译器变量“ f”是一个指针,其值是存储浮点变量的存储位置的地址。以下列代码为例

int a = 10;
int *b;
int c;
b = &a;
c = *b;
printf(“b=%d and c=%d
”,b,c);

其中a是一个变量,他的值是10,b是一个指针,通过语句 b = &a 将a的地址传给了指针b。而通过c = *b 将指针b内地址所指向的值,即a的值赋予c。

[84] 解释C语言中的“值传递”、“地址传递”和“引用传递”的区别

  • 值传递:在这种情况下,函数会用一块新的内存去存储变量,将参数的值复制进来,并且函数内部对参数的修改,不会影响到外部。下例中,在调用Exchg1(a,b)时最开始做的两个隐含动作是:int x=a;int y=b; 及 x=a;y=b; 原来函数在调用时是隐含地把参数a,b的值分别赋值给了x,y。之后在函数体内一直是对形参x,y进行操作。并没有对a,b进行任何操作。函数只是把a,b的值通过赋值传递将值传递给了x,y。函数里操作的只是x,y的值,并不是a,b的值。这就是所谓的值传递
void Exchg1(int x, int y)
{
     int tmp;
     tmp = x;
     x = y;
     y = tmp;
     printf("x = %d, y = %d
", x, y);
}
main()
{
     int a = 4,b = 6;
     Exchg1(a, b);
     printf("a = %d, b = %d
", a, b);
     return(0);
}
  • 地址传递:地址传递的参数为指针,函数内部实际上是通过指针实现的,通过指针的方式寻址,这种修改会对外部的值产生影响。下例中:在调用Exchg2(&a,&b)时最开始做的两个隐含动作是:int *px=&a;int *py=&b;.及 px=&a;py=&b; 原来函数在调用时是隐含地把参数a,b的地址分别传递给了指针px,py。之后在函数体内一直是对指针px,py进行操作。也就是对a,b的地址进行的操作。
void Exchg2(int *px, int *py)
{
      int tmp = *px;
      *px = *py;
      *py = tmp;
      printf("*px = %d, *py = %d.
",*px, *py);
}
main()
{
      int a = 4,b = 6;
      Exchg2(&a, &b);
      printf("a = %d, b = %d.
", a,b);
      return(0);
}
  • 引用传递:这种情况下会将参数的地址复制进来,函数内对参数的修改会反映到外部。通常通过这种方式减小对内存的消耗,例如数组的传递,使用引用穿的可以减小内存消耗。下例中:与值传递相比,代码上只有只有一处不同,即函数定义处:void Exchg3(int &x, int &y) Exchg3函数的定义处Exchg3(int&x, int &y)。调用时我们可以像值传递(如: Exchg1(a, b); )一样调用函数(如: Exchg3(a,b);)。但是x、y前都有一个取地址符号“&”。有了这个,调用Exchg3时函数会将a、b 分别代替了x、y了,我们称:x、y分别引用了a、b变量。这样函数里操作的其实就是实参a、b本身了,因此函数的值可在函数里被修改
void Exchg3(int &x, int &y)
{
     int tmp = x;
     x = y;
     y = tmp;
     printf("x= %d,y = %d
", x, y);
}
main()
{
     int a = 4,b =6;
     Exchg3(a, b);
     printf("a= %d, b = %d
", a, b);
     return(0);
}

[85] NULL指针的值和大小是多少?

NULL指针可以定义为:int * a = NULL; NULL指针的值为0。指针是一个变量,其值是另一个变量的地址。 由于指针的值是地址,所以指针的大小会因机器而异。 如果是32=4*8位计算机,则指针大小为4个字节,如果计算机大小为64=8*8位,则指针大小为8个字节。

[86] 什么是链表?一共有几种类型的链表?

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

一共有三种不同类型的链表:

  1. 单向链表
  2. 双向链表
  3. 循环链表

[87] 以下算法的“最坏情况”时间复杂度是多少?

  1. 线性搜索
  2. 二进制搜索
  3. 插入排序
  4. 合并排序
  5. 桶排序

算法的时间复杂度代表了算法的运行时间,n代表输入算法的参数数量。通常使用big O算法进行评估,例如某算法隐形时间为5n^4 + 6n^2 + 1,取最高阶为n^4,那么其算法复杂度为O(n^4)。所以以上算法的算法复杂度为:

  1. O(N)
  2. O(log(N))
  3. O(N2)
  4. O(N*log(N))
  5. O(N)

[88] 以下算法的空间复杂度是多少?

  1. 线性搜索
  2. 二进制搜索
  3. 插入排序
  4. 合并排序
  5. 桶排序

空间复杂度的概念类似于时间复杂度,但是衡量的值是算法运行时所需要的内存空间。以上算法的空间复杂度为:

  1. O(1)
  2. O(1)
  3. O(N)
  4. O(N)
  5. O(N)

[89] C/C++中,"&"和"&&"有什么区别?

&是按位与运算符,而&&是逻辑与运算符。 逻辑运算符使用布尔值-真(1)和假(0),并返回布尔值。 按位运算符对每个位执行位操作并返回位值。

按位运算符:如果a = 10而b = 6,则a&b将返回2(4'b1010&4'b0110 = 4'b0010)

逻辑运算符:如果a = 10而b = 6,则以下表达式将返回true,因为对两个布尔值进行操作,则为true c =(a == 10)&&(b == 6);

[90] “Struct” 和 “Union” 在 C/C++ 中,内存分配上有什么不同?

Struct分配足够的空间来存储结构中的所有字段/成员。 第一个存储在Struct的开头,第二个存储在Struct的开头,依此类推。

Union仅分配足够的空间来存储列出的最大字段,并且所有字段都存储在同一空间中。 这是因为在Union中,一次只能使用一种类型的封闭变量,而不是可以引用所有封闭变量的struct。

[91] 下面这个结构体需要多大的内存进行存储?

struct ID {
int IntID;
char CharID[8];
};

需要12个字节,int需要4个字节,char数组需要8个字节。

[92] 下面这个联合体需要多大的内存进行存储?

union ID {
int IntID;
char CharID[8];
};

需要8个字节,数组CharID需要8个字节。

[93] 什么是内核(kernel)?

内核是一种计算机程序,它用于管理来自软件的输入/输出请求,并将这些请求转换为CPU指令或其他指令。

[94] perl代表什么意思?

Practical Extraction and Reporting Language。

[95] perl中有多少种不同类型的变量?

  • 标量(scalars):标量用$定义,标量是perl中最简单的变量。 标量可以是数字,也可以是字符串或引用。
  • 数组(arrays):数组用@定义,数组是标量的有序列表,数组的索引是从0开始的。
  • 哈希(hashes):哈希用%定义,哈希是键/值对的无序集合,可以将键用作下标来访问。

[96] 什么是Cron Job?如何使用Cron Job?

Cron Job是操作系统中基于时间的作业调度程序。 它允许在指定的时间,日期,间隔等自动定期运行作业。例如:假设用户具有Shell或Perl脚本,该脚本计算UNIX / Linux中磁盘的人均磁盘空间使用情况。 在UNIX / Linux中为此脚本以指定的频率(或时间)设置Cron Job将确保该脚本在计划的时间(或频率)下自动运行,而用户无需每次都手动运行它。

[97] 在UNIX / Linux中,“ rsync”命令的用途是什么?

“ rsync”代表“Remote Sync(远程同步)”,它是在磁盘,网络,服务器和机器之间复制或同步文件/目录的常用命令。 rsync仅移动文件中已更改的那些部分,因此可以将需要复制的数据量减至最少。 “ rsync”在发送和接收数据时使用某些压缩和解压缩方法,进步减小带宽消耗。 “ rsync”命令最常见的用途之一是在两台计算机之间执行数据备份和镜像磁盘等操作。

[98] C/C++中""字符的用途是什么?

字符串总是以''作为串的结束符。因此当把一个字符串存入一个数组时,也把结束符 ''存入数组,并以此作为该字符串是否结束的标志。

[99] 什么是二叉树?

二叉树是链表概念的扩展。 一个二叉树的节点有两个指针:“一个左指针”和“一个右指针”。 每一个节点可以进一步分支以形成另外的节点,每个节点也具有两个指针。

[100] 什么是正则表达式中的特殊字符、量词和锚点?

  • 特殊字符是为正则表达式用于搜索的,具备特殊含义的元字符。 示例:,^,$,(),[],|,&
  • 量词用于指定匹配前面的正则表达式的“频率”。 示例:*, +, ?, {}
  • 锚点指正则匹配时的匹配的位置。锚点允许用户指定文本搜索的位置。示例:^, $, <, >

Object Oriented Programming Concepts

[101] 类和对象有什么区别?

类是可以组合在一起的一组属性和相关行为。 对象是类的实例,表示具有属性和行为的真实实体。 可以使用类数据成员来表示属性,而可以使用方法来表示行为。 例如:可以将动物表示为一类,而不同的动物(如狗,猫等)可以是该动物的对象。

[102] C++的类和结构体有什么区别?

最初,在C中定义了一个“结构体”,以将不同的数据类型组合在一起以执行某些已定义的功能。 但是,在C++中,这种结构体也扩展为包括函数的结构。 “类”也是一种数据类型,可以将不同的数据类型和其对应的方法进行分类。 C++中两者的区别之一是,类的所有成员默认情况下都是私有的,而结构的所有成员默认情况下都是公共的。

[103] Systemverilog中的类和结构体有什么区别?

在SystemVerilog中,基于要执行的某些功能,类和结构都用于定义一堆数据类型。 但是,结构是整体式的类型,在声明结构时会分配必要的内存。 类是动态类型,一旦声明了一个类,就只能将一个类句柄引用为null。 内存分配仅在创建该类的实际对象时发生。

[104] 什么是public, private 和 protected 成员?

这三者是类成员的不同访问属性

  • 类的private成员只能从该类内部访问。 这些数据成员在派生类中将不可见。
  • public成员可以从该类内部也可以在类外部访问。
  • protected数据成员与private成员类似,因为它们只能在该类中访问。 但是,与private成员不同,这些成员在派生类中也可见。

[105] 什么是多态

多态性是指具有多种形式的能力。 在OOP上下文中,这是指实体在运行时引用各种类的对象的能力。 这可以通过SystemVerilog中的继承和虚函数的概念(以及C++中存在的函数和运算符重载的概念)来实现。根据对象的类型,将从相应的类中调用适当的方法。

[106] 什么是Method Overriding和Method Overloading? 两者有什么区别?

  • Method Overriding:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写。
  • Method Overloading:重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

[107] 什么是运算符重载?

在面向对象的编程中,运算符重载是多态的一种特殊情况,可以重新定义或重载可用的不同内置运算符。 因此,程序员也可以将运算符与用户定义的类型一起使用。 C++支持此功能,而SystemVerilog不支持此功能。 以下示例显示了一个Testclass,其中运算符+被重载,从而可以把两个类型为“Testclass”的类对象相加。 然后,实现将来自两个对象的数据成员相加,并将其分配给结果类的数据成员。

#include <iostream>
class Testclass{
	public:
	int a;
	int b;
	Testclass operator+(const Testclass& obj);
} 

Testclass Testclass::operator+(const Testclass& obj2){
	Testclass tmp_obj = *this;
	tmp_obj.a = tmp_obj.a + obj2.a;
	tmp_obj.b = tmp_obj.b + obj2.b;
	return tmp_obj;
} 

int main(void){
	Testclass obj1, obj2, obj3;
	obj1.a = 1;
	obj1.b = 1;
	obj2.a = 2;
	obj2.b = 2;
	obj3.a = 0;
	obj3.b = 0;
	obj3 = obj1 + obj2;
	std::cout<<obj3.a<<" "<<obj3.b<<"
";
	return 0;
}

[108] 什么是构造函数?

构造函数是类的特殊成员函数,每当创建该类的实例时,构造函数就会自动调用。 在C++中,它与类具有相同的名称。 在SystemVerilog中,它作为new()函数实现。

[109] 什么是析构函数?

与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。 在C++中,它与类具有相同的名称,并带有波浪号字符前缀,而在SystemVerilog中,由于该语言支持自动垃圾收集,因此没有析构函数。

[110] OOP中的组合(composition)和继承(inheritance)之间有什么区别?

组合使两个类之间具有“has - a”关系。 当一个类实例化另一个类的对象时,该关系为“ has-a”,并且此属性称为composition。

继承使两个类之间具有“is - a”关系。 当一个类从另一个类派生时,该关系为“ is-a”,并且此属性称为继承。

下图说明了这一点。 基类汽车中派生出福特类,则该关系为“is-a”,这意味着福特类为汽车类。 如果福特类内部具有引擎类的对象,则关系为“has - a”,如图所示。

image-20191230192310409

[111] OOP的浅拷贝和深拷贝有什么区别?

在浅拷贝中,将创建一个新对象,该对象具有与原始对象中的值完全相同的副本。 如果对象的任何字段是对其他对象的引用,则仅复制引用地址(句柄)。在深拷贝中,将创建一个新对象,该对象具有与原始对象相同的值的精确副本。 如果任何对象都引用了其他对象,则还将复制属于该对象的所有值的副本,而不仅仅是内存地址或句柄。因此,称为深拷贝。

例如,对比如下两个类。

class A;
	int a;
	int b;
endclass
    
class B;
	int c;
	A objA;
endclass

如果在类B中实现了浅拷贝方法,则当我们将B复制到新对象时,仅复制“ objA”的内存句柄。 在深度复制的情况下,还将复制A的所有值(即其数据成员a和b),而不是“objA”的内存句柄。

[112] 什么是OOP的虚方法?

虚方法是在基类中声明的成员方法,并且可以由派生类重新定义。 要创建虚方法,在基类中的方法声明之前要加上关键字virtual。 在派生类中重新定义基类方法的这种方式也称为方法重写。使得调用方法时,是根据对象类型而不是句柄类型调用函数。

[113] 什么是多重继承?

多重继承是某些面向对象的计算机编程语言的功能,其中对象或类可以从多个父对象或父类继承特征和功能。 它不同于单一继承,在单一继承中,一个对象或类只能从一个特定的对象或类继承。注意:C++支持多重继承,而SystemVerilog语言则不支持。

[114] 什么是抽象类?

抽象类是包含一个或多个抽象方法的类。 抽象方法是已声明但不包含任何实现的方法。 抽象类可能无法实例化,并且需要子类为抽象方法提供实现。 在SystemVerilog中,类名前面带有虚拟关键字,以使其成为抽象类。 以下是如何使用函数定义为virtual定义抽象类的示例。 然后派生的类可以实现此功能。相当于一个模板类。

[115] 什么是类的静态方法?

静态方法是使用static关键字在类内部定义的方法。 可以在不创建类对象的情况下使用它们。 同样,如果有多个此类创建的对象,则仍然只有一个静态方法成为所有对象的一部分。

[116] 类的this指针是什么意思?

该指针是一个特殊的指针,可用于在类范围内引用该类的当前对象。

[117] type conversion 和 type casting的区别是?

type conversion 和 type casting的最大区别就是,type conversion有编译器自动(隐式)转换的,而type casting是显式完成的。

比较内容 type casting type conversion
意义 一个数据类型由用户分配给另一个数据类型,使用强制转换运算符,称为"type casting"。 编译器自动将一种数据类型转换为另一种数据类型称为"type conversion"。
应用 类型强制转换也可以应用于两个"不兼容"的数据类型。 仅当两个数据类型"兼容"时,才能实现类型转换。
算子 要将数据类型强制转换到另一个数据类型,需要强制转换运算符"()"。 无需操作符。
实现 它在程序设计过程中完成。 它在编译时显式完成。

下面第一个例子是type casting,第二个是type conversion

int a;
byte b;
...
...
b= (byte) a;
////////////////////////////////////////
int a=3;
float b;
b=a; // value in b=3.000.

UNIX/Linux

[118] 如何找到有关UNIX/Linux命令做什么的详细信息?

通过man <command-name> ,例如man grep

[119] 编写UNIX/Linux命令完成以下任务,假设文件名为file.txt

  1. 显示文件的前10行
  2. 显示文件的第10行
  3. 从文件中删除第13行
  4. 从文件中删除最后一行
  5. 反转字符串(例如:“ Hello” )
  6. 检查上一条命令是否成功
  7. 查找文件中的行数
  8. 查找文件中的字符数
  9. 查找文件中第17行的字符数
  10. 获取第三个单词文件中第17行的内容
  11. 将所有用户的文件权限更改为“读取”和“可执行”。
  12. 将文件的组访问权限更改为组。(假设新的组名称为“ new_group”)
  13. 将两个文件(file1.txt和file2.txt)的内容移动到一个文件(file.txt)
  14. 显示本账号下的所有进程
  15. uniquely排序文件(file1.txt)的内容并将其复制到另一个文件(file2.txt)
  16. 检查用户名
  17. 登录到远程主机(例如“远程服务器”)
任意一种: 
	a) head -10 file.txt 
	b) cat file.txt | head -10 
	c) sed “11,$ d” file.txt
head -10 file.txt | tail -1 
sed -i “13 d” file.txt 
sed -i “$ d” file.txt 
echo “Hello” | rev 
echo $? 
cat file.txt | wc -l 
cat file.txt | wc -c 
head -17 file.txt | tail -1 | wc -c 
head -17 file.txt | tail -1 | cut -f3 -d’ ‘ 
chmod 555 file.txt 
chgrp new_group file.txt 
cat file1.txt file2.txt > file.txt 
ps -aef 
sort -u file1.txt > file2.txt 
whoami 
ssh username@remote-server

[120] 编写UNIX/Linux命令,按照要求显示文件内容,假设文件名为file.txt

  1. 所有匹配“cat”的行
  2. 所有单词“ cat”的行
  3. 所有不包含“cat”的行
  4. 所有包含单词“ cat”的行(不区分大小写)
  5. 所有以“cat”开头的行
  6. 所有以“ cat”结尾的行
  7. 所有包含“cat”和“123”的行(“cat”出现在“123”之前)
grep “cat” file.txt 
grep -w “cat” file.txt 
grep -v -w “cat” file.txt 
grep -i “cat” file.txt 
grep “^cat” file.txt 
grep “cat$” file.txt 
grep “cat.*123” file.txt

[121] 编写UNIX/Linux命令以列出目录中所有文件的名称(例如/usr/bin/dir/)(及其子目录),文件应该包含不区分大小写的“I am preparing for Interview”。

grep -ilr “I am preparing for Interview” /usr/bin/dir/*

[122] 有一个文件(例如/usr/home/file.txt)包含目录列表。 编写一组UNIX/Linux命令,以查看该文件的内容,进入每个目录并运行一个进程(例如script.pl)。 假设文件(/usr/home/file.txt)的每一行仅包含一个目录的路径。

foreach x (`cat /usr/home/file.txt`) 
foreach> cd $x 
foreach> script.pl 
foreach> end

[123] 编写UNIX/Linux命令,该命令将所有非空白行从文件(file1.txt)移至另一个文件(file2.txt)

grep -v “^$” file1.txt > file2.txt

[124] 编写一个UNIX/Linux命令(假设filename = file.txt):

  1. 查找当前目录或其子目录中是否存在某个文件
  2. 查找某个文件是否在目录“/usr/bin/DIR”或其子目录中
  3. 查找某个文件是否仅存在于当前目录中
  4. 查找当前目录或其子目录中是否包含名称中包含特定单词“dummy”的文件
  5. 查找当前目录或其子目录中是否存在不区分大小写的文件“file”
  6. 查找所有名称不是“file.txt”且存在于当前目录或其子目录中的文件
  7. 重新运行以前执行的find命令
find . -name “file.txt” OR find -name “file.txt” 
find /usr/bin/DIR -name “file.txt” 
find -maxdepth 1 -name “file.txt” 
find . -name “*dummy*” 
find . -iname “file” 
find -not -name “file.txt” 
! find

[125] 编写一个UNIX/Linux命令:

  1. 列出在计算机上以你的名字设置的所有Cron Jobs
  2. 列出用户在计算机上设置的所有Cron Jobs
  3. 删除计算机上以你的名字设置的所有Cron Jobs
  4. 删除用户在计算机上的所有Cron Jobs(如果你有权这样做)
  5. 在计算机上以您的名字编辑Cron Job。
  6. 设置每天下午6:30运行的Cron Jobs
  7. 设置每分钟运行一次的Cron Jobs。
  8. 设置一个Cron Jobs,该作业在每个月的前20天上午6:30运行
  9. 设置仅在每月的星期五的6:30 AM和6:30 PM运行的Cron Jobs
crontab -l 
crontab -u <user_name> -l 
crontab -r 
crontab -u <user_name> -r 
crontab -e 
30 18 * * * <command_to_invoke_your_process> 
* * * * * <command_to_invoke_your_process>
30 6 1-20 * * <command_to_invoke_your_process> 
30 6 18 * * 6 <command_to_invoke_your_process> (assuming Sunday is represented by 0)

[126] 列出下列shell中的快捷键

  1. 杀死进程
  2. 将在终端上运行的进程移至后台
  3. 将光标移至Shell上命令的开头
  4. 将光标移至Shell上命令的结尾
  5. Ctrl + c
  6. Ctrl + z
  7. Ctrl + a
  8. Ctrl + e

Programming in C/C++

[127] 编写C代码以检测计算机中的架构是little Endian 还是 big Endian

什么是大小端请参考问题[32]

#include <stdio.h> 
int main() {
    unsigned int i = 1;
    char *c = (char*)&i;
    if (*c)
        printf("Little Endian 
");
    else
        printf("Big Endian 
");
    return 0;
}

[128] 经过下列代码后,b和c的值是多少?

a = 10;
b = a++;
c = ++a;

b等于10,而c等于12。后置自增运算符仅在赋值后才进行自增,因此b得到的是自增前的值。 前置增量运算符将首先进行自增,因此a将从11(在b = a++后变为11)增加到12

[129] 下列代码的输出是什么?

#include<stdio.h>
int xyz=10;
int main() {
    int xyz=20;
    printf("%d",xyz);
    return 0;
}

变量xyz定义了全局变量和局部变量,而在函数中,优先调用的是局部变量,所以将为打印出20.

[130] 下列代码中,y的值是多少?

int main() {
    int x=4;
    float y = * (float *) &x;
    return 0;
}

一个很小的值。 一些编译器可能会将答案显示为0。“(float *)&x”,告诉编译器指针指向存储在内存位置的浮点数。 浮点数的存储方式不同于整数(对于浮点数,位[31]表示带符号的位,位[30:23]表示指数,位[22:0]表示分数)。 因此,当解释为浮点数(00000000000000000000000000000100)时,值将为非常小。

[131] 下列C程序的输出是什么?

#include<stdio.h>
int main() {
    int i=0;
    for(i=0;i<20;i++)  {
        switch(i) {
            case 0:i+=5;
            case 1:i+=2;
            case 5:i+=5;
            default: i+=4;
            break;
        }
    	printf("%d
",i);
    }
    return 0;
}

输出是16,21。

注意两点,i在循环内进行了修改,case后没有跟着break。第一次进入循环,i将一次加5 2 5 4,然后打印输出16,最后再加1。第二次直接进入default,加4,然后输出21。

[132] 编写一个递归函数求n的阶乘,n为正整数

int factorial (int x) {
    if ( (x==0) || (x==1) )
        return 1;
    else
        return (x*factorial(x-1));
}

[133] 编写一个递归函数求斐波纳契数列

int fibonacci (int num){
    if( (num==0) || (num==1) )
        return num;
    else 
        return (fibonacci(num-1) + fibonacci(num-2));
}

[134] 下列代码在64位机上的输出是什么?

#include <stdio.h>
int main() {
    int x = 10000;
    double y = 56;
    int *p = &x;
    double *q = &y;
    printf("p and q are %d and %d", sizeof(p), sizeof(q));
    return 0;
} 

输出是p and q are 8 and 8 。

由于“p”和“q”是指针,因此它们只不过是64位计算机中的地址。 无论它们指向整数还是双精度数据类型,两者的大小均为64位(8字节)。

[135] 什么是链表?何时使用链表?

链表是由一组节点组成的数据结构,这些节点一起代表一个序列。链表是由一组节点组成的数据结构,这些节点一起代表一个序列。如果我们不知道要存储的数据量,则首选链表。 例如:我们可以在员工管理系统中使用链接列表,在这里我们可以轻松地添加新员工的记录(添加新节点-动态内存分配),删除旧员工的记录(删除节点),编辑 员工记录(在节点中编辑数据)。

在[136]-[140]中,使用下列变量和定义:

struct node;
typedef struct node NODE;
typedef int Element;
 
// A pointer to a node structure
typedef NODE *LINK;
 
// A node defined as having an element of data
// and a pointer to another node
struct node {  Element elem;   LINK next; };
 
// The Head or start of the List
typedef struct {  int size;  LINK start; } ListHead;

[136] 编写一个C程序用于创建单链表

要创建单链表,我们需要:

  1. 创建链表的HEAD(h)

  2. 初始化链表的大小(为零)

  3. 将起始指针指向NULL(在创建时为空)。

请参考以下函数来创建单链表:

ListHead createList() {
    ListHead h;
    h.size = 0;
    h.start = NULL;
    return h;
}

[137] 编写一个C程序用于在单链表的头部插入一个元素

在链表(h)的头部插入元素(e)时,我们需要:

  1. 为新节点动态分配内存。

  2. 为新节点中的元素分配值。

  3. 将新节点中的“next”指针指向HEAD先前指向的节点。

  4. 在链接列表HEAD中,增大“size”变量(随着添加了新节点),然后将“start”指针指向新节点。

ListHead InsertElementAtHead(Element e, ListHead h) {
    LINK nl= (LINK) malloc (sizeof(NODE));
    nl->elem = e;
    nl->next = h.start;
    h.start= nl;
    h.size++;
    return h;
}

[138] 编写一个C程序用于在单链表的尾部插入一个元素

在链接列表(h)的末尾插入元素(e)时,我们需要:

  1. 为新节点动态分配内存。

  2. 为新节点中的元素分配值。

  3. 将新节点中的“next”指针指向NULL(因为新节点代表链表的尾部)。

  4. 如果链表最初为空,则将HEAD中的“start”指针指向新节点,否则遍历链接列表以找出链接列表中的最后一个节点,并将最后一个节点中的“next”指针指向新节点。

  5. 在链表HEAD中增大“size”变量(随着添加了新节点)。

ListHead InsertElementAtTail(Element e, ListHead h) {
    LINK temp;
    LINK nl;
    nl=(LINK) malloc (sizeof(NODE));
    nl->elem=e;  nl->next=NULL;
    if(h.start==NULL)
        h.start=nl;
    else  {
        temp=h.start;
        while(temp->next!=NULL)
            temp=temp->next;
        temp->next=nl;
    }
    h.size++;
    return h;
}

[139] 编写一个C程序用于在单链表的pos处插入一个元素

在链表(h)中的pos处插入元素(e)时,我们需要:

  1. 为新节点动态分配内存,

  2. 为新节点中的元素分配值。

  3. 如果“pos”大于链表的大小,则返回错误消息(因为这是不可能的)。 否则,如果“ pos”为“ 0”,则将元素插入头部(如上所示)。 否则,将链表遍历到“ pos”之前的节点。 将新节点中的“next”指针指向“pos-1”处的节点所指向的节点,并将节点中“pos-1”处的“next”指针指向新节点。

  4. 在链表HEAD中增大“size”变量(随着添加了新节点)。

ListHead InsertAtPos(Element e, ListHead h, int pos) {
    LINK temp;
    LINK nl;
    nl=(LINK)malloc(sizeof(NODE));
    nl->elem=e;
    int count=0;
    if(pos>h.size) {
        printf("Error: Wrong position 
");
        return h;
    }
    if(pos==0) {
        nl->next=h.start;
        h.start=nl;
    }  else {
        for (temp = h.start; count<(pos-2); temp = temp->next, count++) ;
        nl->next=temp->next;
        temp->next=nl;
    }
    h.size++;
    return h;
}

[140] 编写一个C程序用于删除单链表的一个元素

从链表(h)中删除元素(e)时,我们需要:

1.检查链表是否为空。 如果为空,则无需删除任何内容。

2.如果链表不为空,则需要遍历链表以找到包含元素(e)的节点。 找到节点之后,我们需要在要删除的节点之前更改节点中的“next”指针,以指向要删除的节点的“next”指针中存的值。

3.减小链表HEAD中的“size”变量(因为删除了节点)。

ListHead DeleteElement(Element e, ListHead h) {
    LINK cur, prev;
    cur=h.start;
    if(cur==NULL)  {
        printf ("Empty List 
");
        return h;
    }
    while(cur!=NULL)  {
        if(cur->elem==e)    {
            if(cur==h.start)
                h.start=cur->next;
            else
                prev->next=cur->next;
            free(cur);
            h.size--;
            break;
        } 
        prev=cur;
        cur=cur->next;
    }
    return h;
}

Programming in PERL

[141] 下列Perl代码的输出是什么?

my @value_array = ("Index0","Index1");
my $value;
foreach $value (@value_array){
	$value =~ s/Index//;
}
print "@value_array
";

结果:0 1

在foreach中使用$value索引,将会改变数组的值

[142] 下列Perl代码的输出是什么?

my @value_array = ("Index0","Index1");
my $value;
for(my $i=0; $i<@value_array; $i++) {
	$value = $value_array[$i];
	$value =~ s/Index//;
}
print "@value_array
"

$value对于for循环来说是局部的,不会影响数组内容

[143] 在Perl中的“-w”和“use strict”的作用是?

-w是用于标记warning,对潜在的歧义代码进行警告。

use strict是Perl中编译指令,是提供给Perl编译器的指令,告诉编译器,如果perl代码中有不好的编码风格,那么提示编译失败。也就是说,加上use strict后,我们的Perl代码的编写必须遵循一些规范,否则编译器会报错。

[144] 下列Perl代码的输出是什么?

my $line_in_a_file = "I am preparing for an Interview";
my $line_in_a_file =~ s/a/A/;
print "$line_in_a_file
";

匹配第一个a,替换为A。因此输出为“I Am preparing for an Interview”

[145] 下列Perl代码的输出是什么?

my $line_in_a_file = "I am preparing for an Interview";
my $line_in_a_file =~ s/a/A/g;
print "$line_in_a_file
";

g代表global,进行全局匹配,将所有的a都替换为A。因此输出为“I Am prepAring for An Interview”

[146] 在Perl中如何将两个字符串进行拼接?在空白处填充

my $string1 = "I am preparing ";
my $string2 = "for an Interview";
my $string = __?__

Perl使用“.”进行填充。因此空白处应该填写$string1.$string2

[147] 下面程序的输出是什么?

#!/usr/bin/perl
use warnings;
use strict;
my $scalar =0;
my @array = ("A","B","C","D");
$scalar = @array;
print "Scalar is $scalar
";

标量会存储数组的元素数量,因此打印出来的值是4

[148] 正则匹配的特殊字符有哪些?解释他们的作用

  1. 转义字符。 使元字符成为文字
  2. ^匹配字符串/行开头的字符
  3. $匹配字符串/行结尾的字符
  4. .匹配除换行符以外的所有字符
  5. *匹配0或更多次
  6. +匹配1或更多次
  7. -表示字符类中的范围(如a-z)
  8. &匹配到的字符,通过$&引用
  9. ()分组字符
  10. []字符类以匹配单个字符
  11. {}指定量词范围
  12. <>指定单词锚点
  13. ?匹配0或1次
  14. |从多个模式中选择一个

[149] 正则表达式中的量词有哪些?他们的用法是?

  1. *匹配0或更多次
  2. +匹配1或更多次
  3. ?匹配0或1次
  4. {N}匹配N次
  5. {N,}匹配至少N次
  6. {N,M}匹配至少N次之多M次

[150] 正则表达式的锚位有哪些?他们的用法是?

  1. ^匹配字符串开头
  2. $匹配字符串结尾
  3. <单词开头
  4. >单词结尾
  5. 单词与非单词的边界
  6. B匹配所不能匹配的位置

在[151]-[155]中使用如下代码,针对问题再下面的空白处填空

#!/usr/bin/perl
use warnings;
use strict;
my $input_file = "input_file.txt";
my $output_file = "output_file.txt";
my @input_array;
 
open(OUTPUT_FILE,'>',$output_file) or die "Cannot Open $output_file file for writing
$!
"; open(INPUT_FILE,'<',$input_file) or die "Cannot Open $input_file for reading
$!
";
 
while(<INPUT_FILE>){
	if($_ =~ /__?__/){
		print OUTPUT_FILE $_;
	}
}

close INPUT_FILE;
close OUTPUT_FILE;

[151] 将input_file.txt中仅包含小写字母(a到z)的所有行复制到output_file.txt

^([a-z]+)$

[152] 将input_file.txt中仅包含字母(a到z或者A-Z)的所有行复制到output_file.txt

^([a-zA-Z]+)$

[153] 将input_file.txt中仅包含字母或数字(a到z或者A-Z或者0-9)的所有行复制到output_file.txt

^([a-zA-Z0-9]+)$

[154] 将input_file.txt中的所有行复制到output_file.txt

$

[155] 将input_file.txt中的仅包含“”或者“$”的所有行复制到output_file.txt

^([\$]+)$

[156] Perl中chop和chomp的作用是什么?

chop:删除字符串的最后一个字符,并返回该字符

chomp:删除字符串结尾的换行符,并返回删除的字符数

[157] 下列Perl代码的输出是什么?

#!/usr/bin/perl
use warnings;
use strict;
my $example_1 = "chop_example";
my $example_2 = "chop_example";
chop($example_1);
my $b = chop($example_2);
print "$example_1 AND $b
";

chop_exampl AND e

参考[157]

[158] 下列Perl代码的输出是什么?

#!/usr/bin/perl
use warnings;
use strict;
my $example_1 = "chomp_example
";
my $example_2 = "chomp_example
";
chomp($example_1);
my $b = chomp($example_2);
print "$example_1 AND $b
";

chomp_example AND 1

参考[157]

原文地址:https://www.cnblogs.com/icparadigm/p/12266588.html