C语言性能优化:从基础到高级的全面指南

引言

C 语言以其高效、灵活和功能强大而著称,被广泛应用于系统编程、嵌入式开发、游戏开发等领域。然而,要写出高性能的 C 语言代码,需要对 C 语言的特性和底层硬件有深入的了解。本文将详细介绍 C 语言性能优化的背后技术,并通过具体的代码示例来展示如何实现性能优化。本文分为多个部分,从基本概念和技巧到高级性能优化技术,全面覆盖 C 语言性能优化的各个方面。
在这里插入图片描述

1. 基本概念和技巧
1.1 数据对齐

数据对齐是指数据的内存地址与数据大小的整数倍对齐。大多数现代计算机系统都要求数据对齐,因为对齐的数据访问速度更快。在 C 语言中,可以通过 #pragma pack 指令来设置数据对齐的方式。

#include <stdio.h>
#pragma pack(1) // 设置数据对齐为1字节
struct Example {char a;int b;char c;
};
#pragma pack() // 恢复默认数据对齐方式int main() {struct Example ex;printf("Size of struct: %zu\n", sizeof(ex)); // 输出结构体大小return 0;
}

在上面的代码中,通过设置 #pragma pack(1),将数据对齐方式设置为 1 字节。这样,结构体 Example 中的数据将按照 1 字节对齐,而不是默认的 4 字节对齐。这会导致结构体的大小变小,但可能会降低访问速度。因此,在实际开发中,需要根据具体情况来选择合适的数据对齐方式。

1.2 循环展开

循环展开是一种通过增加每次迭代中执行的操作数来减少循环次数的技术。这可以减少循环的开销,提高代码的执行速度。

#include <stdio.h>void loop_unrolling(int *arr, int n, int value) {int i;for (i = 0; i < n; i += 2) {arr[i] = value;arr[i + 1] = value;}
}int main() {int arr[10];loop_unrolling(arr, 10, 5);for (int i = 0; i < 10; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}

在上面的代码中,通过将每次迭代中的操作数从 1 增加到 2,将循环次数减少了一半。这样可以减少循环的开销,提高代码的执行速度。但需要注意的是,循环展开会增加代码的大小,因此需要根据具体情况来选择是否使用循环展开。

1.3 函数内联

函数内联是一种通过将函数调用展开为函数体来减少函数调用开销的技术。在 C 语言中,可以通过 inline 关键字来声明内联函数。

#include <stdio.h>inline int add(int a, int b) {return a + b;
}int main() {int result = add(3, 4);printf("Result: %d\n", result);return 0;
}

在上面的代码中,通过将 add 函数声明为内联函数,编译器会将函数调用展开为函数体,从而减少函数调用的开销。但需要注意的是,内联函数会增加代码的大小,因此需要根据具体情况来选择是否使用内联函数。

2. 编译器优化
2.1 编译器选项

编译器提供了多种优化选项,这些选项可以影响编译过程,从而生成更高效的机器代码。以下是一些常用的编译器优化选项。

  • -O0:无优化(默认选项),用于调试。
  • -O1:一级优化,主要包括去除冗余代码、常量折叠等,不会进行复杂的优化。
  • -O2:二级优化,除了包含一级优化的所有内容外,还包括循环展开、指令重排等。
  • -O3:三级优化,在二级优化的基础上,增加更多的优化措施,如自动向量化。
  • -Ofast:允许编译器进行一些可能违反语言标准的优化,通常能提供更高的性能。
  • -march=native:启用针对本机 CPU 架构的优化,使得生成的代码能够更好地利用特定硬件的特性。
gcc -O0 -o compute_O0 compute.c # 无优化版本
gcc -O1 -o compute_O1 compute.c # 一级优化版本
gcc -O2 -o compute_O2 compute.c # 二级优化版本
gcc -O3 -o compute_O3 compute.c # 三级优化版本
gcc -Ofast -o compute_Ofast compute.c # 可能违反标准的优化版本
gcc -march=native -o compute_native compute.c # 针对本地架构优化版本

通过比较不同优化级别的执行时间,可以选择最适合当前程序的优化选项。

2.2 编译器内置函数

现代编译器通常提供一些内置函数,这些函数可以替代标准库函数或手动编写的代码,以提供更好的性能。

  • __builtin_expect:用于分支预测优化。
  • __builtin_prefetch:用于数据预取,以减少缓存未命中的次数。
#include <stdio.h>// 假设我们有一个检查错误码的函数
int error_check(int error_code) {if (error_code == 0) {// 正常情况} else {// 错误处理}
}// 使用 __builtin_expect 优化
int error_check_optimized(int error_code) {if (__builtin_expect(error_code, 0)) {// 错误处理} else {// 正常情况}
}int main() {int result = error_check_optimized(0);return 0;
}

在这个例子中,我们假设 error_code 很可能为 0,那么这个分支就不太可能被执行。通过使用 __builtin_expect,可以优化分支预测,提高性能。

3. 高级性能优化技术
3.1 缓存优化

现代计算机体系结构中,缓存是提高数据访问速度的关键组件。理解缓存的工作原理对于优化程序性能至关重要。缓存优化主要包括两个方面:缓存行利用和减少缓存失效。

3.1.1 缓存行利用

缓存是由缓存行组成的,通常是 64 字节。当数据被加载到缓存中时,它会填充整个缓存行。因此,连续的数据访问(如数组访问)可以充分利用缓存行,提高数据访问的局部性。

#include <stdio.h>void cache_line_utilization(int *arr, int n) {for (int i = 0; i < n; i++) {arr[i] = i;}
}int main() {int n = 1024;int arr[n];cache_line_utilization(arr, n);// ...后续使用 arr 的代码...return 0;
}

在上面的代码中,cache_line_utilization 函数通过连续访问数组 arr 来充分利用缓存行,从而提高性能。

3.1.2 减少缓存失效

缓存失效是指缓存中的数据不再有效,需要从主存中重新加载。减少缓存失效可以提高程序性能。

#include <stdio.h>void reduce_cache_misses(int *arr, int n) {for (int i = 0; i < n; i += 64) { // 64 是假设的缓存行大小for (int j = 0; j < 64 && i + j < n; j++) {arr[i + j] = i + j;}}
}int main() {int n = 1024;int arr[n];reduce_cache_misses(arr, n);// ...后续使用 arr 的代码...return 0;
}

在上面的代码中,reduce_cache_misses 函数通过减少跨缓存行的跳跃来减少缓存失效,从而提高性能。

3.2 指令级优化

指令级优化涉及到编译器和处理器的指令集架构。通过理解和利用这些底层细节,可以编写出更高效的代码。

3.2.1 循环展开和向量化

现代处理器通常支持 SIMD(单指令多数据)指令,允许同时对多个数据执行相同的操作。通过循环展开和向量化,可以利用这些指令来提高性能。

#include <stdio.h>
#include <emmintrin.h> // SSE 指令集void vectorization(int *arr, int n, int value) {for (int i = 0; i < n; i += 4) {__m128i vec = _mm_set1_epi32(value); // 创建一个包含 value 的向量_mm_storeu_si128((__m128i *)&arr[i], vec); // 将向量存储到 arr 中}
}int main() {int n = 1024;int arr[n];vectorization(arr, n, 5);// ...后续使用 arr 的代码...return 0;
}

在上面的代码中,我们使用了 SSE 指令集来实现向量化。这种方法可以显著提高性能,尤其是在处理大型数据集时。

3.2.2 分支预测优化

现代处理器使用分支预测来猜测程序的控制流,以提高指令流水线的效率。优化分支可以提高性能。

#include <stdio.h>int main() {int arr[1024];for (int i = 0; i < 1024; i++) {arr[i] = i % 2 == 0 ? i : -i;}// ...后续使用 arr 的代码...return 0;
}

在这个例子中,我们通过条件表达式来优化分支,减少不必要的分支预测错误。

4. 内存管理优化
4.1 静态分配与动态分配

静态分配和动态分配各有优缺点。静态分配在编译时确定内存大小,适用于大小固定的数组。动态分配在运行时确定内存大小,适用于大小不确定的数组。

#include <stdio.h>
#include <stdlib.h>int main() {// 静态分配数组int arr[100];// 动态分配数组int *dynArr = malloc(100 * sizeof(int));if (dynArr == NULL) {fprintf(stderr, "Memory allocation failed\n");exit(EXIT_FAILURE);}// 使用 dynArr 进行操作for (int i = 0; i < 100; i++) {dynArr[i] = i;}// 释放内存free(dynArr);return 0;
}

在上面的代码中,我们展示了静态分配和动态分配的区别,并演示了如何动态分配和释放内存。

4.2 内存对齐

适当对齐数据结构可以提高内存访问速度。减少缓存未命中,提高性能。

#include <stdio.h>
#include <stdalign.h>struct Example {int a;char b;double c;
} __attribute__((aligned(8)));int main() {struct Example ex;printf("Size of struct: %zu\n", sizeof(ex)); // 输出结构体大小return 0;
}

在上面的代码中,通过指定 __attribute__((aligned(8))),我们确保结构体的每个实例在内存中从 8 的倍数地址开始,这有助于提高内存访问的效率,尤其是在 64 位处理器上。

4.3 避免内存泄漏

合理管理动态分配的内存,避免内存泄漏。对于长期运行的程序尤为重要。

#include <stdio.h>
#include <stdlib.h>int main() {int *ptr = malloc(100 * sizeof(int));if (ptr == NULL) {fprintf(stderr, "Memory allocation failed\n");exit(EXIT_FAILURE);}// 使用 ptr 进行操作for (int i = 0; i < 100; i++) {ptr[i] = i;}// 释放内存free(ptr);return 0;
}

在上面的代码中,我们展示了如何动态分配内存并在使用完毕后释放内存,避免内存泄漏。

5. 算法和数据结构优化
5.1 选择合适的算法和数据结构

选择合适的算法和数据结构可以显著提高程序的效率。例如,对于需要频繁插入和删除操作的数据,使用链表比使用数组更高效。

#include <stdio.h>
#include <stdlib.h>typedef struct Node {int data;struct Node *next;
} Node;void insert(Node **head, int value) {Node *new_node = (Node *)malloc(sizeof(Node));new_node->data = value;new_node->next = *head;*head = new_node;
}int main() {Node *head = NULL;insert(&head, 1);insert(&head, 2);// ...后续操作...return 0;
}

在上面的代码中,我们使用链表来实现插入和删除操作,相比于数组,链表的插入和删除操作更高效。

5.2 查表优化

查表是一种常见的优化技术,特别是在需要频繁进行相同计算的情况下。通过预先计算并将结果存储在表中,可以避免在运行时重复计算。

#include <stdio.h>static long factorial_table[] = {1, 1, 2, 6, 24, 120, 720 /* etc */ };long factorial(int i) {return factorial_table[i];
}int main() {int i = 5;printf("Factorial of %d is %ld\n", i, factorial(i));return 0;
}

在上面的代码中,我们预先计算了阶乘值并存储在 factorial_table 数组中,通过查表来获取阶乘值,避免了在运行时重复计算。

5.3 使用位操作

位操作可以直接操作数据的最小单元——位,常用于优化数据结构和实现加密算法。

#include <stdio.h>int main() {unsigned char flags = 0;flags |= (1 << 2);  // 设置第3位flags &= ~(1 << 2); // 清除第3位printf("Flags: %02X\n", flags);return 0;
}

在上面的代码中,我们使用位操作来设置和清除特定的位,这种方式比使用布尔变量更高效。

6. 并行计算和多线程优化
6.1 使用多线程

多线程可以充分利用多核处理器的计算能力,提高程序的执行效率。C 语言中可以使用 POSIX 线程库(pthread)来实现多线程。

#include <stdio.h>
#include <pthread.h>void *thread_function(void *arg) {int *data = (int *)arg;for (int i = 0; i < 1000000; i++) {(*data)++;}return NULL;
}int main() {pthread_t threads[4];int data[4] = {0};for (int i = 0; i < 4; i++) {pthread_create(&threads[i], NULL, thread_function, &data[i]);}for (int i = 0; i < 4; i++) {pthread_join(threads[i], NULL);}for (int i = 0; i < 4; i++) {printf("Thread %d result: %d\n", i, data[i]);}return 0;
}

在上面的代码中,我们创建了 4 个线程,每个线程独立地对一个整数进行累加操作。通过多线程,可以显著提高计算效率。

6.2 使用 OpenMP

OpenMP 是一种并行编程模型,可以简化多线程编程。通过在代码中添加简单的指令,可以轻松实现并行计算。

#include <omp.h>
#include <stdio.h>int main() {int sum = 0;#pragma omp parallel for reduction(+:sum)for (int i = 0; i < 1000000; i++) {sum += i;}printf("Sum: %d\n", sum);return 0;
}

在上面的代码中,我们使用 OpenMP 的 #pragma omp parallel for 指令将循环并行化,并使用 reduction 子句来处理累加操作。通过这种方式,可以显著提高计算效率。

7. 性能分析和调试
7.1 使用性能分析工具

为了有效地进行性能优化,需要使用一系列的性能分析工具来识别和诊断性能瓶颈。以下是一些常用的性能分析工具及其使用场景。

  • gprof:一个功能强大的性能分析工具,可以显示程序运行的 CPU 时间分布,帮助开发者找到优化的热点。
  • Valgrind:一个编程工具,主要用于内存调试、内存泄漏检测和性能分析。其性能分析工具 Callgrind 可以生成详细的调用图和性能数据。
  • perf:Linux 内核提供的一个性能分析工具,可以用来分析程序的性能问题,特别是 CPU 缓存使用、分支预测等方面。
# 使用 gprof
gcc -pg -o my_program my_program.c
./my_program
gprof my_program > profile.txt# 使用 Valgrind
valgrind --tool=callgrind ./my_program# 使用 perf
perf record -g ./my_program
perf report
7.2 性能优化原则

在进行性能优化时,应遵循以下原则:

  • 先测量,后优化:不要基于猜测进行优化,而是要通过测量来确定性能瓶颈。
  • 关注主要矛盾:优化那些对性能影响最大的部分,遵循 80/20 法则。
  • 逐步迭代:性能优化是一个迭代过程,需要逐步调整和验证。
  • 保持代码可读性:在优化性能的同时,尽量保持代码的清晰和可维护性。
8. 实际应用案例
8.1 用户输入验证

在实际开发中,用户输入验证是一个常见的应用场景。通过 scanf 函数可以方便地读取用户的输入并进行验证。

#include <stdio.h>
#include <stdlib.h>int main() {int age;printf("请输入您的年龄:");if (scanf("%d", &age) != 1 || age <= 0) {printf("无效的年龄输入!\n");return 1;}printf("您的年龄:%d\n", age);return 0;
}
8.2 文件读取

scanf 函数可以结合文件输入流读取文件中的数据。

#include <stdio.h>int main() {FILE *file = fopen("data.txt", "r");if (file == NULL) {perror("文件打开失败");return 1;}int a, b;if (fscanf(file, "%d %d", &a, &b) != 2) {printf("文件读取错误!\n");fclose(file);return 1;}printf("文件中的数据:a = %d, b = %d\n", a, b);fclose(file);return 0;
}
8.3 数据解析

scanf 函数可以用于解析复杂的输入数据格式。

#include <stdio.h>int main() {char name[50];int age;float salary;printf("请输入员工信息(姓名 年龄 薪水):");if (scanf("%49s%d%f", name, &age, &salary) != 3) {printf("输入格式错误!\n");return 1;}printf("员工信息:姓名:%s,年龄:%d,薪水:%.2f\n", name, age, salary);return 0;
}
9. 总结

C 语言因其高效、灵活和功能强大而广受欢迎。通过理解底层优化、编译器优化、内存管理和高级编程技巧,程序员可以编写出性能卓越的 C 程序。本文提供了详细的优化策略和代码案例,希望对读者深入理解 C 语言性能优化有所帮助。在实际应用中,性能优化是一个复杂的过程,需要根据具体的应用场景和目标平台进行细致的分析和调整。

参考文献

[1] C语言代码优化11种实用方法 - 知乎
[2] C语言程序性能优化:十大策略及代码案例解析
[3] C语言代码优化艺术:深入细节,提升效率与性能的实践指南
[4] 高性能计算|C语言常见优化策略 - 知乎
[5] C语言性能优化 - 裸睡的猪 - 博客园
[6] 超全 | 只有高手才知道的C语言高效编程与代码优化方法(一 …
[7] C语言性能优化 - CSDN博客
[8] C语言代码优化方法详解 - CSDN博客
[9] 优化C/C++代码性能的27条建议——<Tips for Optimizing C …
[10] 18|极致优化(上):如何实现高性能的 C 程序?
[11] C语言代码性能优化:提升程序效率的10大技巧 - CSDN文库
[12] C语言性能深度剖析:从底层优化到高级技巧及实战案例分析
[13] C语言性能优化参考手册 - CSDN博客
[14] C语言程序性能优化:十大策略及代码案例解析
[15] 性能优化技巧:C语言程序的高效实现 - CSDN文库
[16] C语言代码优化实战指南与案例分析 - CSDN文库
[17] [大师C语言 (第十篇)]C语言性能优化的技术详解_c语言性能 …
[18] 如何在C语言中优化代码性能 - PingCode
[19] 高性能计算|C语言常见优化策略 - 知乎
[20] 19|极致优化(下):如何实现高性能的 C 程序?-深入C语言 …
[21] 【C 言专栏】优化 C 语言程序性能的策略 - 阿里云开发者社区
[22] c语言如何提高性能 | PingCode智库

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891037.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

go多版本管理工具g win安装配置

go多版本管理工具g 基本介绍仓库安装配置配置环境配置系统变量配置path变量测试使用配置完环境变量之后&#xff0c;打开终端进行测试使用查看 g 的环境变量配置&#xff0c;g env 为环境变量配置&#xff0c;g -v为当前版本信息查看可下载列表下载安装指定版本go&#xff0c;并…

PlasmidFinder:质粒复制子的鉴定和分型

质粒&#xff08;Plasmid&#xff09;是一种细菌染色体外的线性或环状DNA分子&#xff0c;也是一种重要的遗传元素&#xff0c;它们具有自主复制能力&#xff0c;可以在细菌之间传播&#xff0c;并携带多种重要的基因(如耐药基因与毒力基因等)功能。根据质粒传播的特性&#xf…

细说STM32F407单片机通过IIC读写EEPROM 24C02

目录 一、操作说明 二、工程配置 1、时钟、DEBUG、GPIO、USART6、NVIC、Code Generator 2、 IIC2 &#xff08;1&#xff09;Master Features组&#xff0c;主设备参数 &#xff08;2&#xff09;Slave Features组&#xff0c;从设备参数 三、软件设计 1、KELED 2、E…

神经网络-Inception

Inception网络是由Google开发的一种深度卷积神经网络架构&#xff0c;旨在解决计算机视觉领域中的图像分类和物体识别任务。 Inception网络最初在2014年被提出&#xff0c;并在ImageNet图像分类挑战赛上取得了很好的结果。其设计灵感来自于模块化的思想&#xff0c;将不同尺度…

PyTorch Instance Normalization介绍

Instance Normalization(实例归一化) 是一种标准化技术,与 Batch Normalization 类似,但它对每个样本独立地对每个通道进行归一化,而不依赖于小批量数据的统计信息。这使得它非常适合小批量训练任务以及图像生成任务(如风格迁移)。 Instance Normalization 的原理 对每…

国内独立开发者案例及免费送独立开发蓝图书

独立开发者在国内越来越受到关注&#xff0c;他们追求的是一种自由且自给自足的工作状态。 送这个&#xff1a; 少楠light&#xff08;Flomo、小报童、如果相机&#xff09;&#xff1a;他们是独立开发者的典范&#xff0c;不仅开发了多款产品&#xff0c;还坚信“剩者为王”…

【小程序】自定义组件的data、methods、properties

目录 自定义组件 - 数据、方法和属性 1. data 数据 2. methods 方法 3. properties 属性 4. data 和 properties 的区别 5. 使用 setData 修改 properties 的值 自定义组件 - 数据、方法和属性 1. data 数据 在小程序组件中&#xff0c;用于组件模板渲染的私有数据&…

MATLAB用find函数结合all,any函数高效解决问题

如本节中最后提到的问题&#xff0c;我们输出后还需要判断&#xff0c;不是特别的一目了然&#xff0c;这时候我们可以再加上 f i n d find find函数直接标记序号并输出。首先我们先来了解 f i n d find find的用法&#xff0c; f i n d ( a ) find(a) find(a)表示将矩阵或向量…

2022博客之星年度总评选开始了

作者简介&#xff1a;陶然同学 专注于Java领域开发 熟练掌握Java、js等语言的“Hello World” CSDN原力计划作者、CSDN内容合伙人、Java领域优质作者、Java领域新星作者、51CTO专家、华为云专家、阿里云专家等 &#x1f3ac; 陶然同学&#x1f3a5; 由 陶然同学 原创&#…

vue2 升级为 vite 打包

VUE2 中使用 Webpack 打包、开发&#xff0c;每次打包时间太久&#xff0c;尤其是在开发的过程中&#xff0c;本文记录一下 VUE2 升级Vite 步骤。 安装 Vue2 Vite 依赖 dev 依赖 vitejs/plugin-vue2": "^2.3.3 vitejs/plugin-vue2-jsx": "^1.1.1 vite&…

20241227在ubuntu20.04.6系统中,如何用watch命令每秒钟调用nvidia-smi来监控GPU

watch -n 1 nvidia-smi 20241227在ubuntu20.04.6系统中&#xff0c;如何用watch命令每秒钟调用nvidia-smi来监控GPU 2024/12/27 17:04 缘起&#xff1a;在ubuntu20.04.6系统中&#xff0c;使用M6000显卡来跑whisper&#xff0c;显存拉满/占用巨大&#xff0c;但是CPU占用比低&…

[江科大STM32] 第五集STM32工程模板——笔记

保存&#xff0c;进去选芯片型号&#xff0c;我们是F10C8T6 再添加一些文件&#xff0c;自己看路径 然后去 复习这三文件 打开KEIL add existing那个&#xff0c;添加已经存在的文件 还有5个.c.h文件也要添加进来 回到KEIL 点击旁边的settings 如果你用寄存器开发就建到这里就可…

Bitmap(BMP)图像信息分析主要说明带压缩的形式

文章目录 参考资料Bitmap图片结构Bitmap图片组成实例说明 参考资料 微软官方-位图存储 Bitmap图片结构 序号名称说明1Bitmap File HeaderBitmap文件头2Bitmap Info HeaderBitmap信息头3Color Palette Data调色板数据4Bitmap Image Data图像数据 说明 Bitmap文件头的大小为…

二分和离散化

为什么把二分和离散化放一起&#xff1a;因为离散化其实是一种二分整数的过程。 二分 相信大家都接触过二分查找&#xff08;折半查找&#xff09;&#xff0c;这就是二分的思想。 二分通过每次舍弃一半并不存在答案的区间&#xff0c;进而快速锁定要求的答案&#xff08;二…

ESP-IDF学习记录(2)ESP-IDF 扩展的简单使用

傻瓜式记录一个示例的打开&#xff0c;编译&#xff0c;运行。后面我再一个个运行简单分析每个demo的内容。 1.打开示例代码 2.选择项目&#xff0c;文件夹 3.选择串口 4.选择调试方式 5.根据硬件GPIO口配置menuconfig 6.构建项目 7.烧录设备&#xff0c;选择串口UART方式 运行…

SpringMVC学习(一)——请求与响应处理

目录 一、SpringMVC简介 二、RequestMapping&#xff1a;请求路径映射 三、RestController 四、请求限定 五、请求处理 1.使用普通变量&#xff0c;收集请求参数 2.使用RequestParam明确指定获取参数 3.目标方法参数是一个pojo 4.RequestHeader&#xff1a;获取请求…

数据分析的分类和EDIT思维框架

为了服务于企业不同层次的决策&#xff0c;商业数据分析过程需要提供相应的数据科学产出物。 一般而言&#xff0c;数据分析需要经历从需求层、数据层、分析层到输出层四个阶段。 第一个阶段是需求层——确定目标&#xff0c;具体目标需要依据具体的层次进行分析&#xff1a…

实验五 时序逻辑电路部件实验

一、实验目的 熟悉常用的时序逻辑电路功能部件&#xff0c;掌握计数器、了解寄存器的功能。 二、实验所用器件和仪表 1、双 D触发器 74LS74 2片 2、74LS162 1片 3、74194 1片 4、LH-D4实验仪 1台 1.双…

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针-CSDN博客 类型含义使用场景示例nil表示一个指向 Objective - C 对象的空指针。在 Objective - C 和 Swift&#xff08;与 Objective - C 交互时&#xff09;中用于表示对象不存在。当一个对象变量没有指向任何有效的对象实例…

JS面试题|[2024-12-28]

1.JS的设计原理是什么&#xff1f; JS引擎 运行上下文 调用栈 事件循环 回调 执行流程&#xff1a; JS引擎将代码解析为电脑可以执行的代码&#xff0c;调用一些API&#xff08;运行上下文&#xff09;让浏览器执行 JS是单线程的&#xff0c;每次从调用栈里面取出来的代码进行调…