C/C++面试八股题(七)
目录:
1.C 和 C++ 中常见的变量定义方式有哪些?
2.简单说明一下extern关键字,以及extern”C” 有什么作用?
3.请说明一下static关键字?
4.简单说说#include<> 和 #include""的区别是什么?
5.简单说说什么是原码、什么是反码、什么是补码?
6.请说说const关键字的用法?
内容:
1.C 和 C++ 中常见的变量定义方式有哪些?
基本类型
例子:
int x = 10; // 定义一个整数变量 x,初始化为 10 float y = 3.14f; // 定义一个浮点数变量 y,初始化为 3.14 double z = 3.14159; // 定义一个双精度浮点数变量 z,初始化为 3.14159 char c = 'A'; // 定义一个字符变量 c,初始化为字符 'A'
const
常量变量
const
定义常量,使得该变量值不可修改。例子:
const int MAX = 100; // 定义常量 MAX,值为 100,不可修改 const float PI = 3.14f; // 定义常量 PI,值为 3.14
volatile
变量
volatile
用来修饰变量,告诉编译器该变量的值可能随时变化(例如在中断或硬件寄存器中),因此每次访问时都必须从内存中读取,而不是从寄存器中缓存。指针变量
例子:
int a = 10; int *p = &a; // 定义一个指针 p,指向变量 a 的地址
常量指针与指针常量与常量指针常量(在专栏四也有讲过)
常量指针
- 常量指针概念:常量指针是一个指针,它指向的内容是常量。也就是说,通过这个指针不能修改它所指向的数据,但指针本身的值(即它指向的地址)可以改变。
- 语法形式:const 数据类型* 指针变量名;
int a = 10; int b = 20; const int* p = &a; // *p = 30; // 这是错误的,不能通过p修改它指向的值 p = &b; // 这是正确的,p可以指向另一个变量
- 解释:在这个例子中,
p
被定义为一个常量指针,指向a
。我们不能通过p
来修改a
的值,因为p
所指向的内容被视为常量。但是,p
本身可以重新指向其他变量,比如让它指向b
。
指针常量
- 指针常量概念:指针常量是一个常量,它一旦被初始化指向一个地址后,就不能再指向其他地址,但可以通过这个指针修改它所指向的数据。
- 语法形式:数据类型* const 指针变量名;
int a = 10; int* const p = &a; *p = 20; // 这是正确的,可以通过p修改它指向的值 // p = &b; // 这是错误的,p不能再指向其他地址
- 解释:这里
p
是一个指针常量,它被初始化为指向a
。之后就不能再让p
指向其他变量了,但是可以通过p
来修改a
的值,如将a
的值修改为20
。
常量指针常量
const int * const p = &a; // 定义一个常量指针常量 p,指向常量 a p = &b; // 错误:p 是常量指针,不能修改指向的地址 *p = 20; // 错误:不能修改 *p 指向的内容
6. 数组变量
例子:
int arr[5] = {1, 2, 3, 4, 5}; // 定义一个包含 5 个整数的数组 arr char str[] = "Hello"; // 定义一个字符串数组,大小由初始化推导
结构体变量
例子:
struct Point { int x; int y; }; struct Point p1 = {10, 20}; // 定义一个结构体变量 p1,初始化为 (10, 20)
枚举变量
例子:
enum Color { RED = 1, GREEN, BLUE }; // 定义一个枚举类型 Color,包含 RED、GREEN、BLUE enum Color color = RED; // 定义一个枚举变量 color,值为 RED
类型定义 (typedef
)
typedef
用于为已有的数据类型定义新的别名,简化复杂类型的定义。例子:
typedef unsigned int uint; // 为 unsigned int 类型定义别名 uint uint a = 5; // 使用别名 uint 定义变量 a
auto
关键字(C++11)(了解即可)
auto
关键字会根据初始化表达式推导出变量的类型,减少冗长的类型声明。例子:
auto x = 5; // auto 推导出 x 是 int 类型 auto y = 3.14; // auto 推导出 y 是 double 类型
register
关键字 (了解即可)
register
用于建议编译器将变量存储在寄存器中,以便更快的访问。现代编译器通常会忽略此修饰符,自动优化。 extern
关键字
extern
用于声明一个变量在其他文件中定义,使其可以在当前文件中访问。 inline
关键字(C++)(了解即可)
inline
关键字用于声明内联函数,它建议编译器将函数体直接插入调用点,减少函数调用的开销。例子:
inline int add(int a, int b) { return a + b; }
static
关键字
static
修饰变量时,表示该变量的生命周期延续至程序结束,并且仅在定义它的函数或文件内可见(局部静态变量或文件静态变量)。例子:
- 局部静态变量:
void func() { static int count = 0; // 局部静态变量,保留函数调用间的值 count++; printf("%d\n", count); }
thread_local
关键字(C++11)(了解即可)
thread_local
关键字用于定义线程局部变量,每个线程都有一个独立的副本。mutable
关键字(C++)(了解即可)
mutable
允许修改 const
成员变量的值,通常用于类中标记那些即使在常量对象中也可以修改的成员变量。例子:
class MyClass { mutable int count; public: MyClass() : count(0) {} void increment() const { count++; // 即使在 const 成员函数中,也可以修改 count } };
2.简单说明一下extern关键字,以及extern”C” 有什么作用?
extern
用于变量声明
extern
关键字常用于声明在其他文件中定义的全局变量。通过 extern
,extern 可以声明一个变量,使得该变量是来自其他文件的变量在本文件可以被访问。
例子:
file1.c:
#include <stdio.h> int globalVar = 100; // 在 file1.c 中定义一个全局变量
file2.c:
#include <stdio.h> extern int globalVar; // 声明在其他地方定义的全局变量 void printGlobalVar() { printf("globalVar = %d\n", globalVar); // 访问 file1.c 中的 globalVar }
main.c:
#include <stdio.h> extern void printGlobalVar(); // 声明函数 int main() { printGlobalVar(); // 调用 file2.c 中的函数 return 0; }
extern int globalVar;
在file2.c
和main.c
中声明了一个全局变量globalVar
,但并没有定义它。定义在file1.c
中。extern
告诉编译器该变量在其他文件中定义,链接器会在链接阶段找到其定义并进行链接。
extern
用于函数声明
extern
还可以用于声明函数,通常用于在头文件中声明函数原型,告诉编译器函数在其他源文件中定义。
例子:
file1.c:
#include <stdio.h> void myFunction() { printf("Hello from myFunction!\n"); }
file2.c:
extern void myFunction(); // 声明在其他地方定义的函数 void callFunction() { myFunction(); // 调用 file1.c 中的 myFunction }
main.c:
extern void callFunction(); // 声明函数 int main() { callFunction(); // 调用 file2.c 中的函数 return 0; }
extern void myFunction();
在file2.c
和main.c
中声明了函数myFunction
,它在file1.c
中定义。extern
让编译器知道myFunction
的声明,但实际定义是在file1.c
中。
extern”C”
我们可以这样理解C++ 和 C 有不同的 名称修饰机制。extern "C"
使得 C++ 编译器以 C 语言的方式代码。
比如常见用法:
声明 C 语言函数: 如果你需要在 C++ 代码中调用一个 C 语言库函数,你可以用 extern "C"
来声明该函数。
例子:
// C 语言代码 (example.c) #include <stdio.h> void hello() { printf("Hello from C!\n"); } // C++ 代码 (main.cpp) extern "C" { void hello(); // 声明 C 语言中的函数 } int main() { hello(); // 调用 C 函数 return 0; }
防止 C++ 名称修饰: 如果你在 C++ 中包含 C 语言的头文件,并且需要将这些函数暴露给 C++,可以使用 extern "C"
来防止名称修饰。
例子:
// example.h (C语言头文件) void hello(); // C 语言声明 // example.cpp (C++ 中使用 C 语言函数) extern "C" { #include "example.h" // 使用 C 语言风格的头文件 } int main() { hello(); // 调用 C 函数 return 0; }
注:
extern "C"
只能在 C++ 中使用,因为它是 C++ 特有的语法。- 对于 C 语言的代码,
extern "C"
不需要使用,因为 C 本身就不进行名称修饰。 extern "C"
应该只包围函数声明或定义,而不是整个 C++ 代码块。
3.请说明一下static关键字
在局部变量中:
static
修饰的变量,称为 静态局部变量。静态局部变量的生命周期是整个程序的运行周期,但它的作用域仅限于该函数或代码块。换句话说,静态局部变量在函数被调用时不会被销毁,而是在函数之间保持其值。例子:
#include <stdio.h> void counter() { static int count = 0; // 静态局部变量 count++; printf("count = %d\n", count); } int main() { counter(); // 输出: count = 1 counter(); // 输出: count = 2 counter(); // 输出: count = 3 return 0; }
static int count = 0;
声明了一个静态局部变量count
。即使函数counter()
多次调用,count
变量的值会在不同调用间保持不变。- 静态局部变量在程序运行期间 不会被销毁,它只会在程序启动时初始化一次,之后每次调用该函数时都可以继续使用之前的值。
在全局变量中:
static
还可以修饰 全局变量,使得该变量的作用域仅限于定义它的源文件,而无法被其他文件访问。这种方式用于避免全局变量的名字冲突,确保该变量只在当前文件中可见。例子:
// file1.c #include <stdio.h> static int globalVar = 100; // 静态全局变量 void printGlobalVar() { printf("globalVar = %d\n", globalVar); } // file2.c #include <stdio.h> extern void printGlobalVar(); int main() { printGlobalVar(); // 访问 file1.c 中的静态全局变量 // printf("globalVar = %d\n", globalVar); // 错误:无法访问 file1.c 中的静态全局变量 return 0; }
static int globalVar = 100;
声明了一个静态全局变量,globalVar
仅在file1.c
中有效,file2.c
无法直接访问。- 使用
static
修饰全局变量的作用是它仅在当前文件中有效,防止其他文件通过extern
访问该变量。
4.简单说说#include<> 和 #include""的区别是什么?
#include <>
- 通常用于包含系统的标准库头文件或第三方库头文件。
- 编译器会按照系统的默认路径来查找该文件。这些路径通常包括操作系统的标准库路径和编译器配置的头文件搜索路径。
- 一般用于包含系统头文件,诸如 stdlib.h、stdio.h、iostream 等。这些头文件是编译器自带的标准库头文件,通常存放在系统类库目录中。
#include ""
- 通常用于包含用户定义的头文件或项目中的其他头文件。
- 编译器首先在当前源文件所在的目录查找该文件。如果没有找到,编译器才会按照系统的默认路径(与 #include <> 相同)进行查找。例如,#include "myheader.h" 会首先在当前目录下查找 myheader.h 文件。如果在当前目录下找不到,编译器才会去系统的默认路径进行查找。
5.简单说说什么是原码、什么是反码、什么是补码?
原码:
在原码表示法中,整数的符号位(通常是最高位)用于表示数值的符号:
- 0 表示正数。
- 1 表示负数。
- 其余位表示数字的大小(绝对值)。
表示例子:
- 对于一个 n 位的数,最高位是符号位,其它位表示数字的绝对值。
- 例如:对于 8 位原码表示,+5 的原码:00000101-5 的原码:10000101
反码:
表示例子:
- 正数的反码:与原码相同。
- 负数的反码:符号位不变,数值部分按位取反。
- 例如:对于 8 位反码表示,+5 的反码:00000101(与原码相同)-5 的反码:11111010(将原码的数值部分取反)
补码
表示例子:
- 正数的补码:与原码相同。
- 负数的补码:将其原码的每一位取反,然后加 1。
- 例如:对于 8 位补码表示,+5 的补码:00000101(与原码相同)-5 的补码:11111011(原码是 10000101,取反是 11111010,再加 1 得到 11111011)
补码的计算:
- 正数的补码与原码相同。
- 负数的补码可以通过以下步骤得到: 将原数的绝对值转换为二进制。对二进制数取反(即每一位取反)。在反码的基础上加 1。
例子:
- +5 的补码:
00000101
(正数与原码相同) - -5的补码: 5 的原码:00000101取反得到:11111010加 1 得到:11111011
6.请说说const关键字的用法
const
是 C 和 C++ 中的一个关键字,用于表示常量。修饰变量
const
修饰的变量,表示该变量的值在初始化之后不能再修改。例子:
const int x = 10; // 定义一个常量整数 x,值为 10,不能修改 x = 20; // 错误:不能修改常量 x 的值
const
可以修饰 基本数据类型,如int
、float
、char
等,使得这些变量成为常量。- 常量必须在定义时初始化,初始化后不可修改。
修饰指针
const
可以修饰指针的不同部分,具体使用方式会影响指针的行为,下面是三种常见的情况: const
修饰指针本身
const
修饰指针时,指针本身是常量,意味着指针不能指向其他的地址,但指针指向的内容可以改变。int a = 5, b = 10; int * const p = &a; // p 是常量指针,指向的地址不能改变 *p = 20; // 可以修改 p 指向的值 p = &b; // 错误:不能改变常量指针 p 的值
const
修饰指针指向的内容
const
修饰指针所指向的内容时,意味着不能通过该指针修改内容,但指针可以指向其他地址。const int *p = &a; // p 是指向常量的指针,不能通过 p 修改所指向的值 *p = 20; // 错误:不能通过 p 修改值 p = &b; // 可以改变指针 p 指向的地址
const
修饰指针和内容
const
修饰指针本身和指针指向的内容时,指针和内容都不能修改。const int * const p = &a; // p 是常量指针,且指向的内容也是常量 *p = 20; // 错误:不能通过 p 修改值 p = &b; // 错误:不能改变常量指针 p 的值
修饰函数参数
const
可以确保函数不会修改传入的参数,特别是对于指针或引用类型的参数。例子:
void printValue(const int x) { printf("%d\n", x); // x = 20; // 错误:x 是常量,不能修改 }
例子(指针作为参数):
void printArray(const int *arr, int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); // arr[i] = 10; // 错误:不能修改指针指向的内容 } }
例子(引用作为参数):
void modifyValue(const int &x) { // x = 20; // 错误:不能修改引用的内容 }
修饰函数返回值
const
也可以用于修饰函数的返回值,表示返回的值是常量,不能被修改。例子:
const int getValue() { return 10; // 返回一个常量值 }
在此例中,返回值 10
是常量,因此它不能被修改。
嵌入式/C++八股 文章被收录于专栏
本人双飞本,校招上岸广和通。此专栏覆盖嵌入式常见面试题,有C/C++相关的知识,数据结构和算法也有嵌入式相关的知识,如操作系统、网络协议、硬件知识。本人也是校招过来的,大家底子甚至项目,可能都不错,但是在面试准备中常见八股可能准备不全。此专栏很适合新手学习基础也适合大佬备战复习,比较全面。最终希望各位友友们早日拿到心仪offer。也希望大家点点赞,收藏,送送小花。这是对我的肯定和鼓励。 持续更新中