You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何我的C语言密码生成器代码出现内存损坏问题?

C语言密码生成器乱码问题排查与修复

我用C语言开发了一款可自定义长度和字符集的密码生成器,测试发现执行passgen 10时能正常生成包含全字符的10位密码,但执行passgen 10 include-numbers时输出乱码(如9l724è470`),猜测是rand函数越界访问内存导致内存损坏,但找不到具体原因。相关代码如下:

#include "str2int.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define CAPITALS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define SMALL "abcdefghijklmonpqrstuvwxyz"
#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmonpqrstuvwxyz"
#define NUMBERS "1234567890"
#define SPECIALS "!#$@&"
#define ALLCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmonpqrstuvwxyz1234567890!#$@&"

int passlength, errcode;
char* characters;
int numinclude = 0, specialinclude = 0, alphainclude = 0, capitalinclude = 0, smallinclude = 0;

int main(int argc, char* argv[]) {
    srand(time(NULL));
    if (argc > 1) {
        passlength = str2int(argv[1]);
        errcode = errorCheck();
        if (errcode != 0) {
            printf("passgen %s\n", argv[1]);
            printf("        ^ Here\n");
            printf("Invalid input for integer");
            return 0;
        }
    } else {
        printf("Usage: passgen <passlength> [add-all] [include-numbers] [include-specials] [include-alphabet] [include-capitals] [include-small]");
        printf("\n[] = Optional, <> = Required\n");
        printf("Options:\n");
        printf("    add-all: Includes all characters. Default option if no flags are provided.\n");
        printf("    include-numbers: Adds numbers to the list of usable characters.\n");
        printf("    include-specials: Adds special characters (!#$@&) to the list of usable characters.\n");
        printf("    include-alphabet: Adds the entire alphabet (including capital and small letters) to the list of usable characters.\n");
        printf("    include-capitals: Adds the capital alphabet to the list of usable characters.\n");
        printf("    include-small: Adds the small alphabet to the list of usable characters.\n");
        return 0;
    }
    if (argc > 2) {
        for (int i = 2; i < argc; i++) {
            if (strcmp(argv[i], "add-all") == 0) {
                characters = (char*) realloc(characters, 68);
                strcat(characters, ALLCHARS);
                break;
            }
            if (strcmp(argv[i], "include-numbers") == 0) {
                if (numinclude == 0) {
                    characters = (char*) realloc(characters, 11);
                    strcat(characters, NUMBERS);
                    numinclude = 1;
                }
            }
            if (strcmp(argv[i], "include-specials") == 0) {
                if (specialinclude == 0) {
                    characters = (char*) realloc(characters, 6);
                    strcat(characters, SPECIALS);
                    specialinclude = 1;
                }
            }
            if (strcmp(argv[i], "include-alphabet") == 0) {
                if (alphainclude == 0) {
                    characters = (char*) realloc(characters, 53);
                    strcat(characters, ALPHABET);
                    alphainclude = 1;
                    capitalinclude = 1;
                    smallinclude = 1;
                }
            }
            if (strcmp(argv[i], "include-capitals") == 0) {
                if (capitalinclude == 0) {
                    characters = (char*) realloc(characters, 27);
                    strcat(characters, CAPITALS);
                    capitalinclude = 1;
                    if (capitalinclude && smallinclude) {
                        alphainclude = 1;
                    }
                }
            }
            if (strcmp(argv[i], "include-small") == 0) {
                if (smallinclude == 0) {
                    characters = (char*) realloc(characters, 27);
                    strcat(characters, SMALL);
                    smallinclude = 1;
                    if (smallinclude && capitalinclude) {
                        alphainclude = 1;
                    }
                }
            }
        }
        goto generate;
    }
    characters = (char*) realloc(characters, 68);
    characters = ALLCHARS;
    generate:
    printf("Generating password with length %d\n", passlength);
    for (int x = 0; x < passlength; x++){
        printf("%c", characters[rand() % ((int) strlen(characters))]);
    }
}

问题根源分析

  1. 未初始化指针引发的strcat错误
    characters是全局指针,默认初始化为NULL。第一次调用realloc(NULL, 11)会分配11字节内存,但这块内存是未初始化的垃圾数据,没有字符串终止符\0。strcat需要从目标字符串的\0位置开始追加内容,此时它会越过分配的内存去寻找终止符,直接导致越界访问,破坏内存结构,最终输出乱码。

  2. realloc大小计算错误
    每次添加字符集时,只按单个字符集的大小分配内存,没有考虑已经存在的字符长度。比如如果同时添加数字和特殊字符,第二次realloc的大小只给了6字节,根本装不下之前的数字加新的特殊字符,必然导致内存溢出。

  3. 默认分支的指针赋值错误
    characters = ALLCHARS;直接把字符串常量的地址赋值给指针,之前realloc分配的内存被泄漏。虽然这个分支暂时能正常运行,但如果后续尝试修改characters,会触发只读内存错误。

  4. 遗漏必要头文件
    原代码没有包含<string.h>,strcmp、strcat、strlen这些函数的声明缺失,会导致编译器隐式声明,可能引发未定义行为。

修复方案

  1. 初始化字符集指针
    把characters初始化为空字符串,确保有\0终止符:

    char* characters = calloc(1, 1); // 分配1字节并初始化为0,即\0
    
  2. 正确计算内存分配大小
    每次添加新字符集时,计算当前字符集长度 + 新字符集长度 + 1(终止符),再重新分配内存:

    // 以添加数字为例
    if (strcmp(argv[i], "include-numbers") == 0) {
        if (numinclude == 0) {
            size_t current_len = strlen(characters);
            size_t num_len = strlen(NUMBERS);
            characters = realloc(characters, current_len + num_len + 1);
            if (!characters) { // 检查内存分配是否成功
                perror("realloc failed");
                return 1;
            }
            strcat(characters, NUMBERS);
            numinclude = 1;
        }
    }
    
  3. 修正默认分支的赋值逻辑
    不要直接赋值字符串常量,改用strcpy把内容复制到分配好的内存中:

    size_t all_len = strlen(ALLCHARS);
    characters = realloc(characters, all_len + 1);
    if (!characters) {
        perror("realloc failed");
        return 1;
    }
    strcpy(characters, ALLCHARS);
    
  4. 添加内存分配检查
    每次realloc或calloc后都要检查返回值是否为NULL,避免空指针操作导致程序崩溃。

  5. 释放内存避免泄漏
    在程序结束前调用free(characters),释放分配的内存。

修复后的完整代码

#include "str2int.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#define CAPITALS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define SMALL "abcdefghijklmnopqrstuvwxyz" // 修正原宏拼写错误(mon→mn)
#define ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
#define NUMBERS "1234567890"
#define SPECIALS "!#$@&"
#define ALLCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!#$@&"

// 示例str2int和errorCheck实现,可根据实际情况调整
int str2int(const char* s) {
    return atoi(s);
}

int errorCheck() {
    // 可添加实际错误检查逻辑,比如判断passlength是否为正整数
    return 0;
}

int main(int argc, char* argv[]) {
    srand(time(NULL));
    int passlength = 0, errcode = 0;
    char* characters = calloc(1, 1); // 初始化为空字符串
    int numinclude = 0, specialinclude = 0, alphainclude = 0, capitalinclude = 0, smallinclude = 0;

    if (!characters) {
        perror("calloc failed");
        return 1;
    }

    if (argc > 1) {
        passlength = str2int(argv[1]);
        errcode = errorCheck();
        if (errcode != 0 || passlength <= 0) {
            printf("passgen %s\n", argv[1]);
            printf("        ^ Here\n");
            printf("Invalid input: password length must be a positive integer\n");
            free(characters);
            return 0;
        }
    } else {
        printf("Usage: passgen <passlength> [add-all] [include-numbers] [include-specials] [include-alphabet] [include-capitals] [include-small]\n");
        printf("[] = Optional, <> = Required\n");
        printf("Options:\n");
        printf("    add-all: Includes all characters. Default option if no flags are provided.\n");
        printf("    include-numbers: Adds numbers to the list of usable characters.\n");
        printf("    include-specials: Adds special characters (!#$@&) to the list of usable characters.\n");
        printf("    include-alphabet: Adds the entire alphabet (including capital and small letters) to the list of usable characters.\n");
        printf("    include-capitals: Adds the capital alphabet to the list of usable characters.\n");
        printf("    include-small: Adds the small alphabet to the list of usable characters.\n");
        free(characters);
        return 0;
    }

    if (argc > 2) {
        for (int i = 2; i < argc; i++) {
            if (strcmp(argv[i], "add-all") == 0) {
                size_t all_len = strlen(ALLCHARS);
                characters = realloc(characters, all_len + 1);
                if (!characters) {
                    perror("realloc failed");
                    return 1;
                }
                strcpy(characters, ALLCHARS);
                break;
            }
            if (strcmp(argv[i], "include-numbers") == 0) {
                if (numinclude == 0) {
                    size_t current_len = strlen(characters);
                    size_t num_len = strlen(NUMBERS);
                    characters = realloc(characters, current_len + num_len + 1);
                    if (!characters) {
                        perror("realloc failed");
                        return 1;
                    }
                    strcat(characters, NUMBERS);
                    numinclude = 1;
                }
            }
            if (strcmp(argv[i], "include-specials") == 0) {
                if (specialinclude == 0) {
                    size_t current_len = strlen(characters);
                    size_t spec_len = strlen(SPECIALS);
                    characters = realloc(characters, current_len + spec_len + 1);
                    if (!characters) {
                        perror("realloc failed");
                        return 1;
                    }
                    strcat(characters, SPECIALS);
                    specialinclude = 1;
                }
            }
            if (strcmp(argv[i], "include-alphabet") == 0) {
                if (alphainclude == 0) {
                    size_t current_len = strlen(characters);
                    size_t alpha_len = strlen(ALPHABET);
                    characters = realloc(characters, current_len + alpha_len + 1);
                    if (!characters) {
                        perror("realloc failed");
                        return 1;
                    }
                    strcat(characters, ALPHABET);
                    alphainclude = 1;
                    capitalinclude = 1;
                    smallinclude = 1;
                }
            }
            if (strcmp(argv[i], "include-capitals") == 0) {
                if (capitalinclude == 0) {
                    size_t current_len = strlen(characters);
                    size_t cap_len = strlen(CAPITALS);
                    characters = realloc(characters, current_len + cap_len + 1);
                    if (!characters) {
                        perror("realloc failed");
                        return 1;
                    }
                    strcat(characters, CAPITALS);
                    capitalinclude = 1;
                    if (capitalinclude && smallinclude) {
                        alphainclude = 1;
                    }
                }
            }
            if (strcmp(argv[i], "include-small") == 0) {
                if (smallinclude == 0) {
                    size_t current_len = strlen(characters);
                    size_t small_len = strlen(SMALL);
                    characters = realloc(characters, current_len + small_len + 1);
                    if (!characters) {
                        perror("realloc failed");
                        return 1;
                    }
                    strcat(characters, SMALL);
                    smallinclude = 1;
                    if (smallinclude && capitalinclude) {
                        alphainclude = 1;
                    }
                }
            }
        }
    } else {
        // 默认添加所有字符
        size_t all_len = strlen(ALLCHARS);
        characters = realloc(characters, all_len + 1);
        if (!characters) {
            perror("realloc failed");
            return 1;
        }
        strcpy(characters, ALLCHARS);
    }

    printf("Generating password with length %d\n", passlength);
    for (int x = 0; x < passlength; x++){
        int char_count = strlen(characters);
        printf("%c", characters[rand() % char_count]);
    }
    printf("\n"); // 添加换行符,输出更规范

    free(characters);
    return 0;
}

内容的提问来源于stack exchange,提问作者TimeSquared

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 16:48:24