C语言面试题

1.static的作用

1. 修饰全局变量:不可以被外部文件访问,只可以在本文件中使用,限定它的作用域
2. 修饰函数:不可以被外部文件访问,只可以在本文件中使用,限定函数的作用域

如果想要其他文件可以引用本地的函数,则要在函数定义时使用关键字,extern,表示该函数是外部函数可以被其他文件调用,另外要在引用这个函数的文件中使用extern声明要用的外部函数即可。
3. 修饰局部变量:延长局部变量的声明周期,局部变量的作用域没变。只有在第一次执行时进行初始化,后边不在进行初始化

4.static修饰的变量通常存在于静态存储区域

2.const的作用

在C语言中,const关键字有多种作用:

(1)用于定义常量:将变量声明为只读,其值不能被修改。const int a = 10;

1. 修饰局部变量:在栈区分配空间,可以通过指针进行间接修改,不可以通过自身修改
2. 修饰全局变量:在rodata段分配空间,不可以通过指针间接修改,也不肯通过自身修改

(2)用于函数参数类型声明:声明函数的形参为只读,在函数内部不允许修改该参数的值。void foo(const int a);

(3)用于指针类型声明:声明指针指向的内存区域为只读,指针所指向的内存区域的值不能被修改。const int *p;

用于指针类型声明:声明指针内存区域为只读,指针的内存区域的值(指针的指向)不能被修改。int *const p;

(4)用于结构体、联合体成员的定义:将结构体、联合体中的某个成员变量声明为只读,其值不能被修改。

struct MyStruct { const int a; int b; };

(5)用于函数返回值类型声明:声明函数的返回值类型为只读,该函数返回的值不能被修改。 const int foo();

3.全局变量和局部变量在内存中是否有区别?如果有,区别是什么?

存储位置:

全局变量:

存储在全局数据区(Global Data Area)中。这是程序运行时分配的一块内存区域,全局变量在整个程序执行期间都存在,从程序启动到结束。

局部变量:

存储在栈区(Stack)中。栈区是在函数调用时动态分配的内存区域,局部变量的存储空间在函数调用时创建,在函数执行结束后被释放。

生命周期:

全局变量: 生命周期等同于整个程序的执行周期。它在程序启动时被创建,在程序结束时被销毁。

局部变量: 生命周期与所在函数的执行周期相关。它在函数调用时被创建,函数执行结束后被销毁。

作用域:

全局变量: 全局变量在整个程序中可见,可以被任何函数访问。其作用域是整个程序。

局部变量: 局部变量只在定义它的函数中可见,称为局部作用域。 它对其他函数是不可见的,只在所在函数的范围内有效。

初始化:

全局变量: 如果全局变量没有显式初始化,将被默认初始化为零或空,具体取决于变量的类型。

局部变量: 局部变量的值在定义时不会自动初始化,它们的值是不确定的,除非显式初始化。

线程安全性:

全局变量: 在多线程环境中,全局变量的访问可能需要考虑线程同步的问题,以防止竞争条件。 局部变量: 局部变量一般不涉及线程安全性问题,因为它们只在函数的执行期间存在,每个线程有自己的栈。

4.局部变量和全局变量是否可以重名?

局部变量和全局变量可以重名,但是它们位于不同的作用域,因此在特定的作用域内,编译器会根据就近原则选择使用哪个变量。

5.全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

全局变量可以定义在可被多个.C文件包含的头文件中,但是这样可能会导致一些问题。全局变量的定义会在包含该头文件的每个.C文件中生成一份拷贝,这可能导致链接时的冲突或者意外的行为。

通常来说,应该将全局变量的定义放在一个.C文件中,并且在需要使用这个全局变量的其他文件中使用extern关键字来声明该变量。这样可以确保只有一个全局变量的实例被创建,并且可以避免链接时的冲突。

6.简述程序的内存分配

7.解释堆区和栈区的区别?

  • 栈区(Stack):

    • 栈区是程序运行时自动分配和释放的内存区域,用于存储函数的局部变量、函数的参数值以及函数调用过程中的返回地址等临时数据。
    • 栈区的大小和生命周期由编译器自动管理,每个线程都有自己的栈,栈的大小在程序编译时就已经确定。
    • 栈区的内存分配和释放是按照后进先出(LIFO)的原则进行的,即最后分配的内存先被释放。
  • 堆区(Heap):

    • 堆区是动态分配的内存区域,用于存储程序运行时动态申请的数据,例如使用malloc()calloc()realloc()等函数分配的内存、全局变量等。
    • 堆区的大小和生命周期不受编译器控制,需要程序员手动分配和释放内存。堆中的内存分配和释放通常由程序员负责,需要调用特定的函数进行操作。
    • 堆区的内存分配和释放是动态的,可以根据需要随时进行分配和释放,但是需要注意避免内存泄漏和内存碎片等问题。

8.简述gcc四部曲

9.volatile关键字

  • 防止编译器优化: 编译器在编译过程中可能会对变量进行优化,例如将变量缓存到寄存器中,以提高访问速度。但对于被volatile修饰的变量,编译器会知道其可能会被外部因素修改,因此会禁止这种优化,每次访问都会重新从内存中读取变量的值。

  • 处理多线程环境下的共享变量: 在多线程编程中,如果多个线程共享一个变量,而且其中一个线程对该变量进行了修改,其他线程也需要立即看到这个变量的变化。在这种情况下,将共享变量声明为volatile可以确保每次读取该变量时都会从内存中重新获取其值,而不是使用之前的缓存值。

10.数组和指针的区别

  • 内存分配方式:

    • 数组在声明时需要指定其大小,内存空间在编译时就已经分配好了,大小是固定的。
    • 指针只是一个存储地址的变量,它不会分配内存空间。需要通过指针指向已分配的内存区域,或者通过动态分配内存来使用。
  • 使用方式:

    • 数组可以直接通过下标访问其元素,数组名本身代表数组的地址。
    • 指针需要通过间接寻址(解引用)来访问其所指向的内存地址中的内容。
  • 可变性:

    • 数组的大小在声明时就已经确定,无法改变。
    • 指针可以随时指向不同的内存地址,可以动态改变其指向的位置。

作为函数参数:

  • 数组作为函数参数时,实际上传递的是数组的地址,因此函数中对数组元素的修改会影响到原始数组。
  • 指针作为函数参数时,可以通过传递地址或者传递指针本身来实现对函数外部变量的修改,但需要注意指针的作用域和生命周期。


11. 野指针出现的情况

    什么是野指针:指向非法地址空间的指针变量。
    返回局部变量的地址
    定义局部指针变量时,没有置NULL
    使用free释放堆区空间之后,没有置NULL

12. 指针数组和数组指针的区别

1. 定义指针数组的格式
数据类型 * 数组名[列数] = {初始值};

2. 特点:
指针数组本质是一个数组,数组每个元素的类型为指针类型(数据类型 *),
及指针数组的每个元素存储的是一个地址。

指针数组就可以看成是一个特殊的一维数组。
 

1. 定义数组指针的格式
数据类型 (*数组指针变量名)[列数] = 地址; 

2. 特点:
1> 数组指针的本质是一个指针类型的变量;   本质是指针
2> 数组指针类型的变量指向的是有"列数"的二维数组,二维数组的行数没有要求   

13. 指针函数和函数指针的区别

返回类型 * 函数名(形参列表)
    {
        // 函数体
        不可以返回局部变量的地址
        可以返回全局变量地址/可以返回static修饰的局部变量的地址
        /可以返回malloc分配的堆区地址/可以返回通过形参传递的地址
    }


    函数指针做回调函数
    返回类型 (*函数指针变量名)(形参列表);
        // 将函数指针变量名当成函数名使用
        // 回调函数

  • 49
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值