C语言面试题1
C语言必问(待更新)
1、变量的声明和定义有什么区别
为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入 extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。
2、C语言中,变量的作用域
在C语言中,变量的作用域决定了变量在程序中的可见性和生命周期。C语言中的变量作用域主要可以分为以下两种:
局部作用域(Local Scope)在函数内部定义的变量具有局部作用域,这些变量只在定义它们的函数内部可见,并且当函数执行完毕后,这些变量的内存空间会被释放。函数每次调用时,都会为这些局部变量分配新的内存空间,并在函数返回时释放。司部变量可以使用aut0关键字显式声明(但实际上在C语言中,如果不使用static或extern关键字,局部变量默认为auto),但通常省略这个关键字全局作用域(Global Scope):
在所有函数之外定义的变量具有全局作用域。这些变量在整个程序中都可见,并且它们的生命周期贯穿整个程序的执行。全局变量在程序开始执行时分配内存空间,并在程序结束时释放。
全局变量可以使用extern关键字在另一个源文件中声明,以便在其他文件中访问,但是,extern只是声明变量,并不分配内存空间,真正的内存空间是
在定义全局变量的源文件中分配的。此外,C语言还提供了一个static关键字,它可以用来修改局部变量的作用域和生命周期:
当static用于修饰局部变最时,该变最具有文件作用域(File Scope),但它仍然是静态存储期的(Static Storage Duration)。这意味着该变量只在其定义的源文件中可见,但它在程序的整个执行期间都存在,不会因为函数返回而被销毁。每次函数调用时,它都保留上一次的值。当static用于修饰全局变量时,它限制了该变量的可见性,使其只在其定义的源文件中可见,但不影响其生命周期。注意:在C语言中,并没有块级作用域(Block Scope)的概念,像JavaScript中的let和const那样在代码块中定义的变量具有块级作用域。在C语言中,代码块(如if、or、while等控制结构中的代码块)中的变量仍然是局部作用域,但它们的生命周期并不局限于代码块,而是取决于包含它们的函数的执行。
3、局部变量能否和全局变量重名
能,局部会屏蔽全局。
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。
4、数组的特点
C语言中,数组是一种存储相同类型数据的连续内存块的数据结构。它具有以下几个特点:
1.长度固定:数组在声明时需要指定长度,在程序运行期间长度不能改变。
2.存储相同类型数据:数组中的元素必须是相同类型的数据,如整型、浮点型、字符型等
3.连续内存存储:数组中的元素在内存中是连续存储的,这样可以通过下标进行快速访问
4.下标访问:数组中的元素可以通过下标来访问和修改,数组的下标从0开始。
5.缺乏越界检查:C语言中的数组缺乏越界检査,即访问数组时没有对下标是否越界进行检查,这需要程序员自己负责确保不越界访问。否则可能会导致内存访问错误或程序崩溃。int arr[5] ;int arr[5]=1000;
5、什么是内存访问越界
所谓的内存访问越界,简单的说,你向系统申请了一块内存,在使用这块内存的时候,超出了你申请的范围。内存越界访问有两种:一种是读越界,即读了不属于自己的数据,如果所读的内存地址是无效的,程度立刻就崩溃了。如果所读内存地址是有效的在读的时候不会出问题,但由于读到的数据是随机的,它会产生不可预料的后果。int arr{5];print("%d\n",ar5]);另外一种是写越界,又叫缓冲区溢出,所写入的数据对别人来说是随机的,它也会产生不可预料的后果。int arr[5] ;arr[5]=1000;
6、什么是内存泄漏以及如何解决
内存泄漏是指程序在申请内存后,没有释放已申请的内存空间。
内存泄漏常见情况:
1.忘记释放内存。
解决办法:记得使用delete/free来释放内存,更优化的方案是使用C++智能指针。比如C++ 11引入了智能指针,它可以自动管理内存,当智能指针离开作用域时,它会自动释放所管理的内存。这样,就可以避免忘记释放内存的问题。
2.重复申请内存,未释放内存再次申请,会导致原内存泄露。解决办法:在申请新内存之前,先释放旧内存。int p=mal0c(4);p=mall0c(4);
3.静态变量导致的内存泄漏。静态变量在程序运行期间不会释放,如果静态变量持有大量内存,也会导致内存泄漏解决办法:尽量避免静态变量持有大量内存,或者在程序退出前手动释放内存。
void test01()
charp=malloc(100);
int main()
test01();
7、什么是野指针,有什么危害
在C语言中,“野指针(Wid Pointer)**是一个没有被正确初始化的指针,或者是一个已经被释放(如使用free(函数)但仍然被引用的指针。野指钱指向的内存地址是未知的、不可预测的,因此使用野指针进行操作是非常危险的。
野指针的成因
未初始化:指针变量在使用前没有被赋予有效的内存地址。
内存释放后继续使用:在使用mal0c0),cal0c0或real0c()分配内存后,使用free0释放了内存,但指针变量本身没有被置为NULL,之后又被误用。
指针越界:指针访问数组或内存块时超出了其合法范围。
错误操作:如错误的指针运算、指针强制类型转换等。
野指针的危害程序崩溃:野指针可能指向无效的内存地址,尝试访问或修改这些地址上的内容会导致程序崩溃(如段错误、访问冲突等),数据损坏:如果野指针指向了已经分配给其他变量的内存地址,修改该地址上的内容可能会导致其他变量的值被意外改变,从而导致数据损坏。安全漏洞:在更复杂的系统中,野指针可能导致安全漏洞,如缓)中区溢出、堆溢出等,这些漏洞可能被恶意软件利用来执行恶意代码或窃取敏感信息
难以调试:由于野指针的行为是不可预测的,因此它们可能导致难以调试的问题。在程序崩溃时,很难确定崩溃的原因是否与野指针有关
如何避免野指针
初始化指针:在定义指针变量时,将其初始化为NULL或其他有效的内存地址,检查指针:在使用指针之前,检查它是否为NULL或是否指向了有效的内存地址,释放内存后重置指针:在使用free()释放内存后,将指针重置为NULL,以防止再次误用。避免越界:确保指针访问的内存地址在台法范围内。
使用安全的编程实践:如使用结构体和类来封装指针,使用智能指针(如C++中的std::unique_ptr和std::shared_ptr)等。
8、数组指针与指针数组的区别
区分这两个概念,要把这两个概念分成两个部分,限定修饰词和主体;前面的词是限定词,后面的词是主体;主体就是标识符自身的类型,限定词就是存放的数据
对于指针数组来说:“指针”是限定词,“数组”是主体;对于数组指针来说:“数组”是限定词,“指针”是主体;
指针数组说明这个标识符是一个数组,这个数组的存放的数据是指针数据数组指针说明这个标识符是一个指针,这个指针的存放的数据(或者说是指向的)是一个数组
例如:/0的结合优先级高于
int arr[10];
//由于标识符优先与[结合,所以标识符arr是一个数组,存放的数据是int
//()的结合优先级高于几
int (parr)[10];
// 由于标识符优先与结合,所以标识符parr是一个指针,存放的数据是一个数组的地址
9、函数指针与指针函数的区别
函数指针:函数指针是指向函数的指针变量,函数指针的声明方式为:返回类型(指针变量名)(参数列表)。
通过函数指针可以动态选择调用不同的函数。
也可以作为参数传递给其他函数,以实现回调函数的功能。
作为数据结构的成员,用于实现函数的动态绑走。
指针函数:指针函数是一个返回指针的函数。它的声明方式为:返回类型(函数名)(参数列表)。
指针函数返回一个指针,可以是指向任何类型的指针。
可以动态地分配内存并返回指向该内存的指针。
也可以用于实现动态创建对象或数据结构的功能。
函数指针用于指向函数的地址并调用相应的函数,而指针函数是返回指针的函数,用于动态分配内存或创建对象。
10、sizeof与strlen的区别
strlen用于计算字符串的长度,sizeof用于测量数据类型或者变量的所占的字节数,
strlen函数是C标准库中的一个函数,用于计算字符串的长度,即字符串中非空字符的个数(不包括结尾的空字符"0)。sizeof操作符是CIC++语言中的一-个运算符,用于获取对象(变量、数据类型等)所占用的字节数。
3、注意,size0不会计算指针的指向的内存空间大小,而是返回指针本身的大小。因此,size0i的结果在编译时确定,并且对于不同的编译器和平台可能会有所差异。
(sizeof是一个运算符,能够计算出变量的字节大小,对于字符串来说,sizeof会把字符串最后的那个"0字节也计算进去。strlen是C语言库里得供的-个计算字符串长度的函数,遇到"0"就会结束,所以并不是适用于所有的情况。)