
目录
3. 头文件#ifndef、#define、#endif的作用
1. 全局变量和局部变量的区别
全局变量:
- 在函数外部声明的变量,整个程序都可以访问。
- 声明时会被默认初始化,可以在任何函数中使用。
- 生命周期长,整个程序执行期间都存在。
- 全局变量存储在全局数据区(data)中。
#include <stdio.h>
// 全局变量
int count = 0;
// 函数
void add() {
count = count + 1; // 函数内部可以直接使用全局变量
}
int main() {
printf("初始值: %d\n", count);
add(); // 调用函数,count会变成1
printf("第一次加后: %d\n", count);
add(); // 再调用一次,count会变成2
printf("第二次加后: %d\n", count);
return 0;
}
局部变量:
- 在函数内部或代码块内部声明的变量,只能在所属的函数或代码块中访问。
- 声明时没有默认初始化,需要手动赋值才能使用。
- 生命周期短,只在所属的函数或代码块的执行期间存在。
- 局部变量存储在栈区(stack)。
#include <stdio.h>
void function1() {
int a = 10; // 局部变量a,只在function1内有效
printf("function1中的a: %d\n", a);
}
void function2() {
int a = 20; // 局部变量a,只在function2内有效(与function1的a无关)
printf("function2中的a: %d\n", a);
}
int main() {
int a = 5; // 局部变量a,只在main函数内有效
printf("main中的a: %d\n", a);
function1();
function2();
printf("main中的a还是: %d\n", a); // a的值没有改变
return 0;
}
2. #include<>和#include""的区别
使用#include<>:
- 用于包含系统提供的标准库头文件。
- 在编译器的搜索路径中寻找头文件。
- 编译器会先在系统的标准头文件目录中查找,如果找不到则报错。
使用#include"":
- 用于包含用户自定义的头文件或项目中使用的其他非系统头文件。
- 在当前源文件的相对路径或指定的绝对路径中寻找头文件。
- 编译器会首先在当前源文件所在目录中查找,如果找不到再根据指定的路径查找。
3. 头文件#ifndef、#define、#endif的作用
- #ifndef:用于判断当前头文件是否已经被包含。如果该宏之前没有被定义过,则继续编译下面的代码。如果该宏之前已被定义过,则跳过下面的代码,直接到 #endif。
- #define:用于定义一个宏。通过定义一个特定的宏名称,例如MY_HEADER_H表示头文件已被包含。
- #endif:用于结束 #ifndef / #define / #endif 块。标记了头文件的结束位置。
#ifndef MYHEADER_H // 如果 MYHEADER_H 还没有被定义
#define MYHEADER_H // 定义 MYHEADER_H
extern int sum = 100; // 常量定义
void Hello(); // 函数声明
#endif // 结束条件编译
4. 数组名与指针的区别
数组名:
- 是一个常量指针,指向数组的首元素。
- 大小固定为整个数组的大小。
- 无法被改变或重新赋值。
- 无法进行指针运算。
指针:
- 是一个变量,存储一个内存地址。
- 大小固定为指针类型的大小。
- 可以指向任意类型的对象。
- 可以被改变或重新赋值。
- 可以进行指针运算,如加法、减法等。
5. const和#define的区别
- const是一种编译器关键字,而#define是预处理器指令。const在编译阶段进行处理,而#define在预处理阶段进行处理。
- const定义的常量具有类型,而#define没有。const在声明时需要指定常量的类型,编译器会进行类型检查。而#define只是简单的文本替换,没有类型检查。
- const定义的常量有作用域限制,可以根据声明位置的不同而有不同的作用域。而#define定义的常量没有作用域限制,整个程序中都有效。
- const生成符号表中的一个符号,有明确的名字和类型,可以进行调试和符号查找。而#define没有生成符号表,不会产生对应的符号。
| 特性 | const | #define |
|---|---|---|
| 处理阶段 | 编译阶段 | 预处理阶段 |
| 类型检查 | 有 | 无 |
| 作用域 | 有作用域限制 | 整个文件有效 |
| 调试支持 | 支持,在符号表中 | 不支持,预处理后消失 |
| 内存分配 | 分配内存 | 不分配内存,只是替换 |
| 安全性 | 更安全 | 容易出错 |
6. strcpy与memcpy的区别
strcpy:
- 用于字符串拷贝。
- 源字符串中的内容会被复制到目标字符串中,直到遇到字符串结束符 ’\0’。
- 目标字符串必须有足够的空间来存储被复制的内容,否则可能导致缓冲区溢出。
memcpy:
- 用于字节级别的内存拷贝。
- 可以拷贝任意类型的内存块,不仅限于字符串。
- 不会检查字符串结束符,通过指定要拷贝的字节数进行拷贝。
- 可以用于拷贝部分或完整的数组、结构体等。
7. sizeof与strlen的区别
sizeof:
- 用于获取数据类型或变量的字节大小。
- 可以接受多种参数,包括数据类型、变量名、数组名等。
- 返回的是整个数据类型或变量占用的内存空间大小。
strlen:
- 用于获取以’\0’结尾的字符串的实际长度。
- 在运行时计算,需要遍历字符串的内容来确定长度。
- 返回的是字符串中的字符个数,不包括字符串结束符’\0’。
8. 结构体和共用体的区别
结构体:
- 成员在内存中独立存储,每个成员占用独立的内存空间。
- 内存占用是成员之和,每个成员都占用独立的空间。
- 成员可以同时被访问,通过成员名字来访问。
- 适合存储和处理多个不同类型的数据,如员工信息、图形对象等。
共用体:
- 成员共享同一块内存空间,只能存储一个成员的值。
- 内存占用是最大成员的大小,所有成员共享该空间。
- 成员只能同时访问其中的一个,存取时要明确指定。
- 适合存储和处理只使用其中一种类型的数据,可以节省内存空间或进行数据类型转换。
9. malloc和calloc的区别
malloc:
- malloc 分配的内存是未初始化的,其中的字节内容是不确定的(可能是随机值)。
- 如果内存分配失败,malloc 返回一个空指针 NULL,可以通过检查返回值来判断是否分配成功。
calloc:
- calloc 分配的内存会被初始化为全0。
- calloc 在分配失败时会自动抛出错误(异常),可以使用异常处理机制来捕获和处理错误。
10. 堆和栈的区别
| 特性 | 栈 (Stack) | 堆 (Heap) |
|---|---|---|
| 管理方式 | 自动分配释放 | 手动分配释放 |
| 速度 | 快(一级缓存) | 慢(二级缓存/主存) |
| 生命周期 | 函数作用域内 | 直到手动释放 |
| 空间大小 | 较小(通常~2MB) | 较大(可达GB级别) |
| 碎片问题 | 无碎片 | 可能产生内存碎片 |
| 生长方向 | 向低地址生长 | 向高地址生长 |
| 使用场景 | 局部变量、函数参数 | 动态数据结构、大内存需求 |


C语言核心概念对比解析

6486

被折叠的 条评论
为什么被折叠?



