欢迎关注个人公众号摸鱼范式,后台回复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] 下面关于栈的观点哪个是错的?
- 栈只能push或者pop
- 可以使用栈实现FIFO
- 栈对于嵌套循环,子程序调用很有用
- 队长堆算术表达式计算有用
选项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] 什么是链表?一共有几种类型的链表?
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
一共有三种不同类型的链表:
- 单向链表
- 双向链表
- 循环链表
[87] 以下算法的“最坏情况”时间复杂度是多少?
- 线性搜索
- 二进制搜索
- 插入排序
- 合并排序
- 桶排序
算法的时间复杂度代表了算法的运行时间,n代表输入算法的参数数量。通常使用big O算法进行评估,例如某算法隐形时间为5n^4 + 6n^2 + 1,取最高阶为n^4,那么其算法复杂度为O(n^4)。所以以上算法的算法复杂度为:
- O(N)
- O(log(N))
- O(N2)
- O(N*log(N))
- O(N)
[88] 以下算法的空间复杂度是多少?
- 线性搜索
- 二进制搜索
- 插入排序
- 合并排序
- 桶排序
空间复杂度的概念类似于时间复杂度,但是衡量的值是算法运行时所需要的内存空间。以上算法的空间复杂度为:
- O(1)
- O(1)
- O(N)
- O(N)
- 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”命令最常见的用途之一是在两台计算机之间执行数据备份和镜像磁盘等操作。