C语言面试高频(一)

1.全局变量和局部变量的区别

全局变量:

  • 在函数外部声明的变量,整个程序都可以访问。
  • 声明时会被默认初始化,可以在任何函数中使用。
  • 生命周期长,整个程序执行期间都存在。
  • 全局变量存储在全局数据区(data)中

局部变量:

  • 在函数内部或代码块内部声明的变量,只能在所属的函数或代码块中访问。
  • 声明时没有默认初始化,需要手动赋值才能使用。
  • 生命周期短,只在所属的函数或代码块的执行期间存在。
  • 局部变量存储在栈区(stack)

2.int main(int argc, char ** argv)函数中,参数argc和argv分别代表什么意思?

在C语言中,主函数int main(int argc, char **argv)用来作为程序的入口,argcargv是其参数。

  • argc是整型参数,表示命令行参数的个数。它记录了程序在运行时附带的命令行参数的数量,至少为1,因为程序自身的名称也算一个参数。
  • argv是字符指针数组,用来存储命令行参数的字符串。每个元素指向一个以null结尾的字符串,表示一个命令行参数。
  • argv[0]指向程序的名称,argv[1]指向第一个参数,以此类推,argv[argc-1]指向最后一个参数。

举个例子,假设我们在命令行中执行以下命令:

./program arg1 arg2 arg3

那么argc的值为4,argv的值如下所示:

argv[0] = "./program"
argv[1] = "arg1"
argv[2] = "arg2"
argv[3] = "arg3"
argv[4] = NULL

3.static关键字

  • 声明静态变量,使其生命周期延长或作用域限定在当前文件内。
  • 声明静态函数,使其作用域限定在当前文件内。
  • 声明静态成员变量,使其属于类本身而不是对象,多个对象共享同一份内存。
  • 使用静态限定符,控制变量的初始化和生命周期。

举例:

  1. 在函数内部使用 static:
#include <stdio.h>

void increment() {
  static int count = 0;
  count++;
  printf("调用次数:%d\n", count);
}

int main() {
  for (int i = 0; i < 5; i++) {
    increment();
  }
  return 0;
}
在每次调用 increment 函数时,count 的值会持续增加,而不会被重置。
这是因为 count 被声明为 static,其生命周期跨越了函数调用。

2.在文件作用域使用 static:

//这里的例子是防止同学们以后要避免这样去使用。更好的去理解static的隐藏性
// File1.c
static int globalVar = 10; //变量只可在file1.c里使用

// File2.c
extern int globalVar;
int main() {
  printf("globalVar 的值:%d\n", globalVar);
  return 0;
}

在 File1.c 文件中,我们声明了一个具有文件作用域的静态全局变量 globalVar。
在file2里是extern不到。

4.const关键字

  • 值不可修改:一旦常量被赋值后,其值将保持不变,不能再对其进行修改。
  • 作用域限制:常量的作用域通常被限制在声明时所在的作用域内部
  • 编译时确定:常量的值在编译时就已确定,并在运行时保持不变

举例:

1.使用 const 声明常量变量:

#include <stdio.h>

int main() {
  const int PI = 3.14;
  const char GREETING[] = "Hello, world!";

  return 0;
}
在这个例子中,我们使用 const 关键字来声明了一个整数常量 PI 和一个字符数组常量 GREETING。
这些常量在声明后不能被修改。

2.使用 const 参数声明函数:

#include <stdio.h>

int sum(const int a, const int b) {
  return a + b;
}

int main() {
  int num1 = 5, num2 = 10;
  int result = sum(num1, num2);

  printf("两数之和:%d\n", result);

  return 0;
}
在 sum 函数的参数中,我们使用 const 关键字声明了 a 和 b 为只读参数。
在函数内部不能修改这些参数的值。

3.使用 const 修饰函数返回值:

#include <stdio.h>

const char* getMessage() {
  return "Hello, world!";
}

int main() {
  const char* message = getMessage();

  printf("消息:%s\n", message);

  return 0;
}
在这个例子中,getMessage 函数的返回类型前面的 const 关键字指示函数返回一个指向常量字符的指针。
返回的字符串不能通过指针进行修改。

5.const 和 #define的区别

  • const是一种编译器关键字,而#define是预处理器指令。const在编译阶段进行处理,而#define在预处理阶段进行处理。
  • const定义的常量具有类型,而#define没有。const在声明时需要指定常量的类型,编译器会进行类型检查。而#define只是简单的文本替换,没有类型检查。
  • const定义的常量有作用域限制,可以根据声明位置的不同而有不同的作用域。而#define定义的常量没有作用域限制,整个程序中都有效。
  • const生成符号表中的一个符号,有明确的名字和类型,可以进行调试和符号查找。而#define没有生成符号表,不会产生对应的符号。

6.extern关键字

  • 声明一个在其他文件中定义的外部变量或函数。
  • 告诉编译器在链接过程中需要找到对应的定义。
  • 允许在当前文件中使用这些外部变量或函数而不需要重新定义。

举例:

// File1.c
extern int globalVar = 10;

// File2.c
#include <stdio.h>

extern int globalVar;

int main() {
   printf("globalVar 的值:%d\n", globalVar);
   return 0;
}
在 File1.c 中,我们使用 extern 关键字来定义一个全局变量 globalVar,并初始化为 10。
在 File2.c 中,我们使用 extern 关键字来声明同名的全局变量 globalVar,以表示它是在其他源文件中定义的。
然后,在 main 函数中,我们可以访问并打印 globalVar 的值。

7.#include<> 和 #include""的区别

使用 #include<>:

  • 用于包含系统提供的标准库头文件。
  • 在编译器的搜索路径中寻找头文件。
  • 编译器会先在系统的标准头文件目录中查找,如果找不到则报错。

使用 #include"":

  • 用于包含用户自定义的头文件或项目中使用的其他非系统头文件。
  • 在当前源文件的相对路径或指定的绝对路径中寻找头文件。
  • 编译器会首先在当前源文件所在目录中查找,如果找不到再根据指定的路径查找。

8.C语言的基本类型有哪些(32位系统),占用字节空间

char

1

short int

2

int/long int

4

char * /int * /任何的指针

4

float

4

double

8

9.头文件#ifndef/#define/#endif的作用

  • #ifndef:用于判断当前头文件是否已经被包含。
  • 如果该宏之前没有被定义过,则继续编译下面的代码。
  • 如果该宏之前已被定义过,则跳过下面的代码,直接到 #endif。
  • #define:用于定义一个宏。
  • 通过定义一个特定的宏名称,例如MY_HEADER_H表示头文件已被包含。
    • #endif:用于结束 #ifndef / #define / #endif 块。
    • 标记了头文件的结束位置。

    通过使用这种组合,可以防止同一个头文件被多次包含,以避免重复定义和编译错误。

    举例:

    #ifndef MYHEADER_H     // 如果 MYHEADER_H 还没有被定义
    #define MYHEADER_H     // 定义 MYHEADER_H
    
    void sayHello();       // 函数声明
    
    const int MAX_VALUE = 100;  // 常量定义
    
    #endif               // 结束条件编译
    
    上述是一般的使用模板
    

    10.volatile声明的作用

    volatile声明的变量是指可能会被意想不到地改变的变量,这样编译器就不会轻易优化该变量。它主要用于多线程编程中,用来保证共享变量的内存可见性。(注:指针也可用volatile)

    三个常见场景

    • 多线程中的共享变量
    • 中断程序中访问到的非自动变量
    • 并行设备的硬件寄存器

    11.strcpy与memcpy的区别

    strcpy:

    • 用于字符串拷贝。
    • 源字符串中的内容会被复制到目标字符串中,直到遇到字符串结束符 ‘\0’。
    • 目标字符串必须有足够的空间来存储被复制的内容,否则可能导致缓冲区溢出。

    memcpy:

    • 用于字节级别的内存拷贝。
    • 可以拷贝任意类型的内存块,不仅限于字符串。
    • 不会检查字符串结束符,通过指定要拷贝的字节数进行拷贝。
    • 可以用于拷贝部分或完整的数组、结构体等。

    安全性:

    • strcpy函数不进行源字符串长度的检查,如果源字符串太长,可能会导致目标字符串缓冲区溢出。
    • memcpy函数本身没有长度限制,应确保源和目标内存区域不会发生重叠,否则可能会导致数据损坏。
    • 为了提高安全性,可以使用像strcpy_s、strncpy_s这样提供了长度限制的函数。

    总结:

    • strcpy适用于字符串拷贝,可以自动识别字符串结束符。
    • memcpy适用于字节级别的内存拷贝,适用于任意类型的数据。

    12.一个变量既可以是const还可以是volatile类型吗

    可以,一个变量可以同时具有const和volatile。const表示变量的值不能被改变,而volatile属性表示变量的值可能会被外部程序改变。

    13.sizeof与strlen的区别

    sizeof:

    • 用于获取数据类型或变量的字节大小。
    • 可以接受多种参数,包括数据类型、变量名、数组名等。
    • 返回的是整个数据类型或变量占用的内存空间大小。

    strlen:

    • 用于获取以’\0’结尾的字符串的实际长度。
    • 在运行时计算,需要遍历字符串的内容来确定长度。
    • 返回的是字符串中的字符个数,不包括字符串结束符’\0’。

    举例:

    char str[] = "Hello";
    size_t size_str = sizeof(str);
    size_t length_str = strlen(str);
    // size_str 的值为 6,因为包括字符串 "Hello" 的 5 个字符和结尾的 '\0',共 6 个字节
    // length_str 的值为 5,因为字符串 "Hello" 有 5 个字符,不包括结尾的 '\0'
    

    注意事项:

    • sizeof返回的是静态的大小,而strlen返回的是实际的字符串长度。
    • 在使用strlen时要确保操作的对象是以’\0’结尾的字符串,否则可能出现不确定的结果。
    • sizeof可以用于任何数据类型或变量,而strlen只适用于字符串。

    14.常见的变量定义

    • int a;:定义了一个变量 a,它的类型是 int
    • int *a;:定义了一个指针 a,它指向 int 类型的变量。
    • int **a;:定义了一个指针 a,它指向一个指向 int 类型的指针。
    • int a[10];:定义了一个数组 a,该数组有 10 个元素,每个元素是 int 类型。
    • int *a[10];:定义了一个数组 a,该数组有 10 个元素,每个元素是 int 类型的指针。
    • int (*a)[10];:定义了一个指针 a,该指针指向一个数组,该数组有 10 个元素,每个元素是 int 类型。
    • int (*a)(int);:定义了一个指针 a,该指针指向一个参数是 int,返回值是 int 的函数。
    • int (*a[10])(int);:定义了一个数组 a,该数组的元素是一个指向参数是 int,返回值是 int 的函数指针。

    15.数组名与指针的区别

    数组名:

    • 是一个常量指针,指向数组的首元素。
    • 大小固定为整个数组的大小。
    • 无法被改变或重新赋值。
    • 无法进行指针运算。

    指针:

    • 是一个变量,存储一个内存地址。
    • 大小固定为指针类型的大小。
    • 可以指向任意类型的对象。
    • 可以被改变或重新赋值。
    • 可以进行指针运算,如加法、减法等。

    #offer收割机##嵌入式面经##面经##c++面经#
    c++/嵌入式面经专栏 文章被收录于专栏

    本人2022年毕业于山东大学,目前就职intel。打算把之前校招时做的笔记通过专栏发出来,本专栏适合于C/C++、嵌入式方向就业的同学,本篇面经总结数千篇面经的知识集合,实时更新全网最新的嵌入式/C++最新内容,囊括了C语言、C++、操作系统、计算机网络、嵌入式、算法与数据结构、数据库等一系列知识点,在我看来这些是求职者在面试中必须掌握的知识点。最后呢祝各位能找到自己合适的工作。

    全部评论
    同学们有什么建议可以留言哦,每天都会看
    3 回复 分享
    发布于 2023-10-03 17:48 山东
    对于一些知识点的高频我会标注出来可以按需进行学习
    3 回复 分享
    发布于 2023-10-05 12:15 山东
    大佬,请问关于C语言的面经会有几期更新啊?
    1 回复 分享
    发布于 2023-10-04 22:40 北京
    点赞三连继续更新
    1 回复 分享
    发布于 2023-10-05 20:04 山东
    小伙伴们转载要表明出处哦,尊重一下原创
    1 回复 分享
    发布于 2023-10-06 16:34 山东
    这个static是不是有点问题,文件作用域下的static修饰的变量,无法被其他文件使用?
    1 回复 分享
    发布于 2023-10-07 11:32 四川
    “可以,一个变量可以同时具有const和volatile”。这是谁写的啊?一个变量同时拥有两个相反的类型,人才吧?
    1 回复 分享
    发布于 08-25 19:06 重庆
    请问老哥对现在CPP就业方向了有解吗,26 届双飞本211 硕,现在打算选一个有竞争力的方向,做一个项目,然后年末去投实习,请问老哥有什么见解吗
    1 回复 分享
    发布于 09-26 22:28 辽宁
    感谢佬的分享!
    点赞 回复 分享
    发布于 2023-10-07 00:16 北京
    大佬c语言这些就够了吗
    点赞 回复 分享
    发布于 2023-10-07 10:47 北京
    有代码注释了我这种菜鸡看着都没那么枯燥了
    点赞 回复 分享
    发布于 2023-10-07 10:48 北京
    大佬牛逼
    点赞 回复 分享
    发布于 2023-10-16 09:02 湖南
    为什么数组名是常量指针?常量指针的意思是pointer to const, 指向常量的指针,数组首元素不是const吧?,例如a[0] = 1, 我可以对首元素任意更改啊
    点赞 回复 分享
    发布于 2023-10-27 17:14 山东
    面试的时候真的考的有那么简单嘛?
    点赞 回复 分享
    发布于 03-11 08:37 四川
    加油
    点赞 回复 分享
    发布于 04-01 21:32 四川
    写得容易理解,nice!
    点赞 回复 分享
    发布于 05-13 10:16 四川
    有PDF版本吗,方便打印出来看
    点赞 回复 分享
    发布于 05-28 22:56 上海
    第十五个问题,数组名不是一个常量指针。 常量指针:指针所指向的数据是一个常量,不能通过改指针修改数据,但指针本身可以指向其他地址。 指针常量: 指针本身是常量,不能改变指向的地址,但可以修改该地址的值。所以数组名的行为类似于指针常量,但在声明,类型,内存分配方式上有本质的区别。 如何记忆呢?用常量关键字(const)和指针符号(*)的相对位置去记忆。 常量(const)指针(*): const放在*前面,例如const int *p;或int const *p。 指针(*)常量(const): *在const在之前,int *const p;
    点赞 回复 分享
    发布于 06-29 21:49 江苏
    初始化的全局变量放在.data和没有初始化的全局变量放在.bss段吧
    点赞 回复 分享
    发布于 07-07 21:31 广东
    大大有没有PDF版的呢,想勾勾画画加深印象
    点赞 回复 分享
    发布于 07-13 22:25 四川

    相关推荐

    去B座二楼砸水泥地:不过也可以理解,这种应该没参加过秋招
    点赞 评论 收藏
    分享
    勇敢的联想人前程似锦:如果我是你,身体素质好我会去参军,然后走士兵计划考研211只需要200多分。
    点赞 评论 收藏
    分享
    28 101 评论
    分享
    牛客网
    牛客企业服务