C++ 编程注意事项
C++ 编程注意事项 需要长期存在的数据应该声明为静态或全局变量。 函数内声明的局部变量在函数返回后会被销毁。如果这些变量需要在函数返回后继续使用,会导致未定义行为。 对于需要持续使用的结构体变量,应该将其声明为静态或全局变量,确保其生命周期覆盖整个程序运行时间。
本文目录 3 个章节
C++ 编程注意事项
需要长期存在的数据应该声明为静态或全局变量。
- 函数内声明的局部变量在函数返回后会被销毁。如果这些变量需要在函数返回后继续使用,会导致未定义行为。
- 对于需要持续使用的结构体变量,应该将其声明为静态或全局变量,确保其生命周期覆盖整个程序运行时间。
void installSignalHandlers() {
struct sigaction s_action;
s_action.sa_sigaction = signalHandler;
s_action.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&s_action.sa_mask);
for (int i = 0; i < kNumHandledSignals; ++i) {
sigaddset(&s_action.sa_mask, kExceptionSignals[i]);
}
for (int i = 0; i < kNumHandledSignals; i++) {
if (sigaction(kExceptionSignals[i], &s_action, &oldHandlers[kExceptionSignals[i]]) != 0) {
LOGE("Failed to install signal handler for signal %d", kExceptionSignals[i]);
} else {
LOGD("Successfully installed signal handler for signal %d", kExceptionSignals[i]);
}
}
}
当函数返回时,s_action 变量被销毁,后续信号处理可能会访问已经无效的内存。
malloc和calloc
malloc
malloc 函数用于分配指定字节数的内存,但不会对分配的内存进行初始化。
void* malloc(size_t size);
- 效率高:由于不进行初始化操作,
malloc通常比calloc快。
为字符串分配内存后调用 memset 将内存初始化为零是一个良好的编程习惯,但并不是严格必要的.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
size_t size = 100;
char *str = (char *)malloc(size);
if (str == NULL) {
perror("malloc failed");
return 1;
}
memset(str, 0, size);
snprintf(str, size, "Hello, world!");
printf("%s\n", str);
free(str);
return 0;
}
如果不手动进行初始化, 并且在使用时未完全覆盖分配的内存,可能会留下未初始化的字节。这些字节的内容是不确定的,可能导致未定义的行为或垃圾数据。
字符串操作(例如 strlen, strcat, strcmp)依赖于零终止符。如果字符串没有正确终止,可能会导致函数读取超出预期范围的内存,导致未定义行为或者崩溃。
- 如果你分配内存后立即将其完全覆盖(例如,通过
strcpy或者snprintf),则不需要初始化内存为零。 - 如果你只是部分覆盖内存,并且依赖于剩余部分是零,那么你需要初始化内存为零。
手动初始化为0的情况:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
size_t size = 100;
char *str = (char *)malloc(size);
if (str == NULL) {
perror("malloc failed");
return 1;
}
memset(str, 0, size);
snprintf(str, size, "Hello, world!");
printf("%s\n", str);
free(str);
return 0;
}
不手动初始化, 但是安全的情况: snprintf 直接将字符串写入分配的内存块。由于 snprintf 完全覆盖了内存并且自动添加了字符串的零终止符,因此字符串操作是安全的。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
size_t size = 100;
char *str = (char *)malloc(size);
if (str == NULL) {
perror("malloc failed");
return 1;
}
snprintf(str, size, "Hello, world!");
printf("%s\n", str);
free(str);
return 0;
}
内存未完全覆盖导致异常的情况: 字符串没有写入\0 终止符, 打印时可能会读取到未初始化的内存内容.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
size_t size = 100;
char *str = (char *)malloc(size);
if (str == NULL) {
perror("malloc failed");
return 1;
}
strcpy(str, "Hello"); // 只写入 "Hello"
printf("%s\n", str); // 打印时可能会读取到未初始化的内存内容
free(str);
return 0;
}
calloc
calloc 函数用于分配内存,并将分配的内存块初始化为零。
void* calloc(size_t num, size_t size);
num:要分配的元素个数。size:每个元素的大小(以字节为单位)。
calloc 通常用于分配和初始化数组,其参数是元素个数和每个元素的大小。
char *path = (char *) calloc(10, PATH_MAX);
calloc 将分配 10 * PATH_MAX 字节的内存,并将这段内存初始化为零。
// 使用示例:分配一个可以存储10个字符串的内存块,每个字符串的最大长度为 PATH_MAX
char *pathArray = (char *) calloc(10, PATH_MAX);
if (pathArray == NULL) {
// 内存分配失败,进行错误处理
}
// 使用 pathArray 处理10个字符串
for (int i = 0; i < 10; i++) {
snprintf(&pathArray[i * PATH_MAX], PATH_MAX, "String %d", i);
printf("String %d: %s\n", i, &pathArray[i * PATH_MAX]);
}
// 释放内存
free(pathArray);
pathArray 被用作一个二维字符数组,能够存储 10 个字符串,每个字符串的最大长度为 PATH_MAX。