C语言--基础面试真题

这篇具有很好参考价值的文章主要介绍了C语言--基础面试真题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、局部变量和静态变量的区别

  • 普通局部变量和静态局部变量区别

    • 存储位置:

      • 普通局部变量存储在栈上

      • 静态局部变量存储在静态存储区

    • 生命周期:

      • 当函数执行完毕时,普通局部变量会被销毁

      • 静态局部变量的生命周期则是整个程序运行期间,即使函数调用结束,静态局部变量的值也会被保留

    • 初始值:

      • 普通局部变量在每次函数调用时都会被初始化,它们的初始值是不确定的,除非显式地进行初始化

      • 静态局部变量在第一次函数调用时会被初始化,然后保持其值不变,直到程序结束

  • #include <stdio.h>
    ​
    void normal_func() {
        int i = 0;
        i++;
        printf("局部变量 i = %d\n", i);
    }
    ​
    void static_func() {
        static int j = 0;
        j++;
        printf("static局部变量 j = %d\n", j);
    }
    ​
    int main() {
        // 调用3次normal_func()
        normal_func();
        normal_func();
        normal_func();
    ​
        // 调用3次static_func()
        static_func();
        static_func();
        static_func();
    ​
        return 0;
    }
  • 运行结果:

  • 局部变量 i = 1
    局部变量 i = 1
    局部变量 i = 1
    static局部变量 j = 1
    static局部变量 j = 2
    static局部变量 j = 3

2、预处理

  • C语言对源程序处理的四个步骤:预处理、编译、汇编、链接。

    • 预处理

      • 宏定义展开、头文件展开、条件编译,这里并不会检查语法

    • 编译

      • 检查语法,将预处理后文件编译生成汇编文件

    • 汇编

      • 将汇编文件生成目标文件(二进制文件)

    • 链接

      • 将目标文件链接为可执行程序

    gcc -E hello.c -o hello.i //处理文件包含,宏和注释 
    gcc -S hello.i -o hello.s //编译为汇编文件 
    gcc -c hello.s -o hello.o //经汇编后为二进制的机器指令
    gcc hello.o -o hello      //链接所用的到库
    ​
    1 预处理:预处理相当于根据预处理命令组装成新的 C 程序,不过常以 i 为扩展 名。 
    2 编 译:将得到的 i 文件翻译成汇编代码 .s 文件。 
    3 汇 编:将汇编文件翻译成机器指令,并打包成可重定位目标程序的 O 文件。 该文件是二进制文件,字节编码是机器指令。 
    4 链 接:将引用的其他 O 文件并入到我们程序所在的 o 文件中,处理得到最终 的可执行文件
  • C编译器提供的预处理功能主要包括:

    • 文件包含 #include

    • 宏定义 #define

    • 条件编译 #if #endif ……

3、文件包含处理

  • 文件包含处理

    • 指一个源文件可以将另外一个文件的全部内容包含进来

    • C语言提供了#include命令用来实现文件包含的操作

  • #include< > 与 #include ""的区别

    • <> 表示系统直接按系统指定的目录检索

    • "" 表示系统先在 "" 指定的路径(没写路径代表当前路径)查找头文件,如果找不到,再按系统指定的目录检索

4、宏定义

  • 在预编译时将宏名替换成字符串的过程称为"宏展开"(也叫宏替换)。

    • 宏名一般用大写,以便于与变量区别

    • 宏定义不作语法检查,只有在编译被宏展开后的源程序才会报错

    • 宏定不要不要行末加分号

#define PI 3.14
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define FUNC(a)  func(a)
​
void func(int a) {
    int b = a;
}
​
int main() {
    double a = PI;
    int temp = MAX(1, 2+3);
    FUNC(10);
​
    return 0;
}

5、条件编译

一般情况下,源程序中所有的行都参加编译。但有时希望对部分源程序行只在满足一定条件时才编译,即对这部分源程序行指定编译条件。

防止头文件被重复包含
#ifndef _SOMEFILE_H
#define _SOMEFILE_H
​
//需要声明的变量、函数
//宏定义
//结构体
​
#endif
软件裁剪

同样的C源代码,条件选项不同可以编译出不同的可执行程序:

#include <stdio.h>
​
// #define A 有注释,没有注释,观察运行结果
#define A
​
int main() {
#ifdef A
    printf("这是大写操作\n");
#else
    printf("这是小写操作\n");
#endif
​
    return 0;
}

6、递归

  • 函数递归调用:

    • 函数可以调用函数本身(不要用main()调用main(),不是不能这么做,而是不建议,往往得不到你想要的结果)。

  • 递归的优点

    • 递归给某些编程问题提供了最简单的方法。

  • 递归的缺点

    • 一个有缺陷的递归会很快耗尽计算机的资源,递归的程序难以理解和维护

 C语言--基础面试真题,c语言,面试,开发语言

7、普通函数调用

#include <stdio.h>
​
void fun_b(int b) {
    printf("b = %d\n", b);
​
    return;
}
​
void func_a(int a) {
    fun_b(a - 1);
​
    printf("a = %d\n", a);
}
​
int main(void) {
    func_a(2);
    printf("main\n");
​
    return 0;
}

运行顺序:

  • 结论:

    • 先调用,后返回(栈结构)

    • 调用谁,返回谁的位置

      C语言--基础面试真题,c语言,面试,开发语言

运行结果:

b = 1
a = 2
main

8、函数递归调用

 C语言--基础面试真题,c语言,面试,开发语言

#include <stdio.h>
​
//0的阶乘是1  1的阶乘1    return 1
//n! =(n-1)!*n
//(n-1)! = (n-2)!*(n-1)
//n = 1
​
​
// 递归函数计算阶乘
int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1;
    } else {
        return n * factorial(n - 1);
    }
}
​
int main() {
    int n;
    printf("请输入一个整数:");
    scanf("%d", &n);
​
    // 调用递归函数计算阶乘并输出结果
    int result = factorial(n);
    printf("%d 的阶乘是 %d\n", n, result);
​
    return 0;
}
​

运行顺序:

9、大小端验证

        所谓的大端模式,是指数据的低位(就是权值较小的后面那几位)保存在内存的高地址中,而数据的高位,保存在内存的低地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;
        所谓的小端模式,是指数据的低位保存在内存的低地址中,而数 据的高位保存在内存的高地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低,和我们的逻辑方法一致。
​
1)大端模式:
​
低地址 -----------------> 高地址
​
0x12  |  0x34  |  0x56  |  0x78
​
2)小端模式:
​
低地址 ------------------> 高地址
​
0x78  |  0x56  |  0x34  |  0x12
#include <stdio.h>
#include <stdint.h>
​
int check_endianness() {
    uint32_t temp = 0x44332211; // 4个字节,32位
    uint8_t * p = NULL;  // 8位
​
    p = (uint8_t *)&temp;  // 只取uint8_t的长度
    printf("%#x\n", *p);
    printf("%#x\n", p[0]); // *p 和 p[0]等价
​
    uint16_t * p1 = (uint16_t *)&temp; 
    printf("*p1 = %#x\n", *p1);
​
    if (*p == 0x11 ) {
        return 0; // 0是小端
    } else {
        return 1; // 大端
    }
}
​
int main() {
    int res = check_endianness();
    if (res == 0) {
        printf("小端\n");
    } else {
        printf("大端\n");
    }
​
    return 0;
}

10、大小端转换

#include <stdio.h>
​
int changeBigEndian(int data) {
​
    return (data >> 24 & 0x000000ff) |
        (data >> 8 & 0x0000ff00) |
        (data << 8 & 0x00ff0000) |
        (data << 24 & 0xff000000);
}
​
int main() {
​
​
    int mem = 0x44332211;
​
    printf("%0x\n", changeBigEndian(mem));
    return 0;
}
​

11、二分查找

#include <stdio.h>
​
// 二分查找函数
int binarySearch(int arr[], int size, int target) {
    int left = 0;
    int right = size - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) {
            return mid; // 找到目标元素,返回索引
        } else if (arr[mid] < target) {
            left = mid + 1; // 在右半部分继续查找
        } else {
            right = mid - 1; // 在左半部分继续查找
        }
    }
    return -1; // 目标元素不存在,返回-1
}
​
int main() {
    int arr[] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19};
    int size = sizeof(arr) / sizeof(arr[0]);
    int target = 11;
    int index = binarySearch(arr, size, target);
    if (index != -1) {
        printf("目标元素 %d 在数组中的索引为 %d\n", target, index);
    } else {
        printf("目标元素 %d 不在数组中\n", target);
    }
    return 0;
}
​

12、什么是指针,在什么地方使用的

 指针(Pointer)是一种特殊的变量类型,它用于存储内存地址。指针的实质就是内存“地址”。
使用范围:
动态内存分配:指针常用于动态分配内存,例如使用 malloc()、calloc() 或 new 分配内存,并使用指针来管理和访问分配的内存块。
​
数组和字符串:数组名本身就是指向数组第一个元素的指针,在函数参数传递、数组访问等场景中经常用到指针。
​
函数指针:函数指针是指向函数的指针变量,可以用来在运行时动态确定调用的函数,或者将函数作为参数传递给其他函数。
……

13、函数指针是什么

函数指针是指向函数的指针变量,它存储了函数的地址,可以用来调用该函数。在 C 语言中,函数名可以视为函数在内存中的地址,因此可以将函数名赋值给函数指针变量,从而实现通过函数指针来调用函数。

#include <stdio.h>
​
int getData(int a, int b) {
    return a + b;
}
​
int main() {
​
    int(*func)(int, int);
​
    func = getData;
    printf("%d\n", func(5, 8));
​
    return 0;
}
​

复议:指针函数

  • 指针函数是一个返回指针的函数。它的返回值是一个指针,指向某种数据类型的内存地址。

  • 指针函数通常用于动态内存分配、返回数组、返回字符串等场景。

int* create_array(int size) {
    int* arr = malloc(size * sizeof(int)); // 动态分配内存
    return arr;
}

14、声明和定义的区别

  • 声明告诉编译器,某个名称(如变量、函数、类等)存在,但不分配内存空间或提供实现细节。

  • 声明通常包括名称和类型信息,以及可能的参数列表。

  • 声明可以出现在函数或变量的定义之前,以便在使用之前提供有关名称的信息。

int add(int a, int b);
  • 定义不仅声明了名称的存在,还为其分配了内存空间或提供了实现细节。

  • 对于变量,定义会分配内存空间;对于函数,定义会提供函数体的实现。

  • 每个定义都是一个声明,但不是每个声明都是一个定义。

// 函数定义
int add(int a, int b) {
    return a + b;
}

15、extern关键字是干什么用

用来修饰全局变量,全局变量本身是全局可用的,但是由于文件是单个完成编译,并且编译是自上而下的,所以说,对于不是在本范围内定义的全局变量,要想使用必须用 extern 进行声明,如果不加上 extern ,就会造成重定义。

注意,经 extern 声明的变量,不可以再初始化。文章来源地址https://www.toymoban.com/news/detail-860156.html

16、位运算

#include <stdio.h>
#include <inttypes.h>
​
int main() {
    // 将变量a的第2位设置为1,其他位保持不变
    uint8_t a = 0b10110011; // 0xb3;
    a |= (1 << 2);          // 或者 x = x | (1 << 2);
    printf("%02x\n", a);    // b7,  10110111
​
    // 将变量b的第2位、第6位设置为1,其他位保持不变
    uint8_t b = 0b10110011; // 0xb3;
    b |= (1 << 2 | 1 << 6);
    printf("%02x\n", b);    // f7,11110111
​
    // 将变量c的第5位设置为0,其他位保持不变
    uint8_t c = 0b10110011;  // 0xb3;
    c &= ~(1 << 5);
    printf("%02x\n", c);    // 93,10010011
​
    // 将变量d的第0~3位设置为0,其他位保持不变
    uint8_t d = 0b11111111;  // 0xff;
    d &= ~(1 << 0 | 1 << 1 | 1 << 2 | 1 << 3);
    printf("%02x\n", d);    // f0,11110000
​
    // 将变量e的第2位取反,其他位保持不变
    uint8_t e = 0b10110011;  // 0xb3;
    e ^= (1 << 2);
    printf("%02x\n", e);    // b7,  10110111
​
    return 0;
}

17、说说什么是野指针,怎么产生的,如何避免

野指针是指向"垃圾"内存的指针,也就是说,它的值是不确定的。野指针通常由以下几种情况产生:
​
未初始化的指针:如果你声明了一个指针变量但没有给它赋值,那么它就是一个野指针。例如:int *ptr;。
已删除的指针:如果你使用delete或free删除了一个指针,但没有将它设置为NULL,那么它就成了一个野指针。例如:
超出作用域的指针:如果你返回了一个函数内部的局部变量的地址,那么这个地址在函数返回后就不再有效,因此返回的指针就是一个野指针。
​
​
初始化: ptr = NULL;
​

18、堆和栈有什么区别?

- 栈区(stack)
​
- - 栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
​
- 堆区(heap)
​
- - 堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

到了这里,关于C语言--基础面试真题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 大数据开发(Spark面试真题-卷一)

    Spark Streaming是 Spark提供用于实时流式数据处理和分析的库 。它 基于离散时间间隔将原始流式数据划分为小的批处理,然后将这些批次数据提供给Spark引擎进行处理 。 Spark Streaming的工作原理如下: 不断从流源接收数据,并将其划分为微批次 。 将每个微批次传递给Spark引擎 。

    2024年03月12日
    浏览(44)
  • 百度 测试|测试开发 面试真题|面经 汇总

    事业群:MEG base:北京 一面:2022.8.12 时长:50min 自我介绍 个人项目,我的项目是围绕着学校课程的项目来的,面试官就让我介绍这门课讲了些什么 (学校里)性能测试做了什么工作,性能测试中需要关注什么部分,我从前端和后端分开来讲的 接口测试关注的部分 实习中做

    2024年02月10日
    浏览(48)
  • 大数据开发(Spark面试真题-卷三)

    Spark的阶段划分是指将一个Spark作业划分为多个阶段(Stage)来执行的过程。每个阶段包含一组相互依赖的任务,并且每个任务可以并行执行。Spark的阶段划分是为了提高作业的执行效率和并行度。 Spark的阶段划分分为两个阶段:转换阶段(Transformation Stage)和动作阶段(Actio

    2024年03月10日
    浏览(84)
  • 【华为OD机试真题 C语言】95、最少面试官数 | 机试真题+思路参考+代码解析

    🍂个人博客首页: KJ.JK   🍂专栏介绍: 华为OD机试真题汇总,定期更新华为OD各个时间阶段的机试真题,每日定时更新,本专栏将使用C语言进行更新解答,包含真题,思路分析,代码参考,欢迎大家订阅学习 🎃题目描述 某公司组织一场公开招聘活动,假设由于人数和场地

    2024年02月11日
    浏览(43)
  • Go 语言面试题(一):基础语法

    := 声明+赋值 = 仅赋值 指针用来保存变量的地址。 例如 运算符,也称为解引用运算符,用于访问地址中的值。 &运算符,也称为地址运算符,用于返回变量的地址。 允许 Go 没有异常类型,只有错误类型(Error),通常使用返回值来表示异常状态。 Goroutine 是与其他函数或方

    2024年02月14日
    浏览(48)
  • C语言面试之旅:掌握基础,探索深度(面试实战之单片机——IO)

            梦想和自由一样,都有代价,但都值得。                                                                                                                                                ----小新         单片机是一种微控制器,它包含一个处

    2024年02月05日
    浏览(43)
  • JAVA后端开发面试基础知识(一)——JVM

    Class loader(类装载) 根据给定的全限定名类名(如: java.lang.Object)来装载class文件到 Runtime data area中的method area。 Execution engine(执行引擎) 执行classes中的指令。 Native Interface(本地接口) 与native libraries交互,是其它编程语言交互的接口。 Runtime data area(运行时数据区域) 这就是我们常说

    2024年03月10日
    浏览(61)
  • JAVA后端开发面试基础知识(八)——Spring

    Spring是一个轻量级Java开发框架 我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inverse of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、

    2024年03月10日
    浏览(74)
  • 【面试】美团面试真题和答案

    问题来源于某客,如下图所示: 问题链接:https://www.nowcoder.com/feed/main/detail/b12f8ece42f6485d8e462ab872c4f8d8 答案解析 线程池的创建方法总共有 7 种,但总体来说可分为 2 类: 通过 ThreadPoolExecutor 创建的线程池; 通过 Executors 创建的线程池。 线程池的创建方式总共包含以下 7 种(其

    2024年02月12日
    浏览(47)
  • Java安全 URLDNS链分析,网络安全开发面试基础

    this代表的是当前对象的指针,也可以用 this.name 的方式调用当前对象中的成员 那我们去 URLStreamHandler类 当中,查看下 hashCode方法 的代码 protected int hashCode(URL u) { int h = 0; // Generate the protocol part. String protocol = u.getProtocol(); if (protocol != null) h += protocol.hashCode(); // Generate the host pa

    2024年04月23日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包