---
title: "C++ 编程注意事项"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/cpp-programming-pitfalls/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/02-编程语言与运行时/C++ 编程注意事项.md"
content_hash: d4a6ef3cbb3fbd09ae62e6ba8d153ab7e999addab22b7d61e6a2742ace89af3c
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# C++ 编程注意事项
需要长期存在的数据应该声明为静态或全局变量。
- 函数内声明的局部变量在函数返回后会被销毁。如果这些变量需要在函数返回后继续使用，会导致未定义行为。
- 对于需要持续使用的结构体变量，应该将其声明为静态或全局变量，确保其生命周期覆盖整个程序运行时间。

```cpp
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` 函数用于分配指定字节数的内存，但不会对分配的内存进行初始化。

```cpp
void* malloc(size_t size);
```
- **效率高**：由于不进行初始化操作，`malloc` 通常比 `calloc` 快。

为字符串分配内存后调用 `memset` 将内存初始化为零是一个良好的编程习惯，但并不是严格必要的.

```cpp
#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的情况:
```cpp
#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` 完全覆盖了内存并且自动添加了字符串的零终止符，因此字符串操作是安全的。
```cpp
#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` 终止符, 打印时可能会读取到未初始化的内存内容.
```cpp
#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` 函数用于分配内存，并将分配的内存块初始化为零。
```cpp
void* calloc(size_t num, size_t size);
```

- `num`：要分配的元素个数。
- `size`：每个元素的大小（以字节为单位）。

`calloc` 通常用于分配和初始化数组，其参数是元素个数和每个元素的大小。

```cpp
char *path = (char *) calloc(10, PATH_MAX);
```

`calloc` 将分配 `10 * PATH_MAX` 字节的内存，并将这段内存初始化为零。

```cpp
// 使用示例：分配一个可以存储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`。
