首页 > 解决方案 > char数组C末尾的奇怪字符

问题描述

我试图编写Weasel Program实现。我编译了三个版本,所有版本完全相同,名称分别为“weasel.exe”、“weasel2.exe”和“weasel3.exe”。“weasel.exe”产生预期的输出,

Gen 0: YPSZNCWPSDUMLYKRIKNSEOJCERIL
Gen 10: YPSZNCWPSDUMLYKRIKNSE JCERIL
Gen 92: YPSZNCKPSDUMLYKRIKNSE JCERIL
Gen 129: YPSZNCKPSDUMLYKRIKNSE JCEREL
Gen 240: YPSZICKPSDUMLYKRIKNSE JCEREL
Gen 388: YPSZICKPSDTMLYKRIKNSE JCEREL
Gen 570: YPSZICKPSDTMLYKRIKNSE JEEREL
Gen 634: YPSZICKPSDTMLYKRIKN E JEEREL
Gen 1102: YPSZICKPSDTMLSKRIKN E JEEREL
Gen 1185: YPSZICKPYDTMLSKRIKN E JEEREL
Gen 1223: YESZICKPYDTMLSKRIKN E JEEREL
Gen 1491: YESZICKPYITMLSKRIKN E JEEREL
Gen 1613: YESZICKPYITMISKRIKN E JEEREL
Gen 1627: YETZICKPYITMISKRIKN E JEEREL
Gen 1750: YETZICKPYITMISKRIKN E WEEREL
Gen 1855: YETZICKPJITMISKRIKN E WEEREL
Gen 2461: METZICKPJITMISKRIKN E WEEREL
Gen 2536: METZICKPJITMIS RIKN E WEEREL
Gen 2876: METZICKPJITMIS JIKN E WEEREL
Gen 3354: METZICKPJITMIS JIKN E WEAREL
Gen 3668: METZICKPJITMIS JIKN E WEASEL
Gen 3837: METZINKPJITMIS JIKN E WEASEL
Gen 3938: METZINKP ITMIS JIKN E WEASEL
Gen 4147: METHINKP ITMIS JIKN E WEASEL
Gen 4314: METHINKP ITMIS JIKE E WEASEL
Gen 4803: METHINKP ITMIS JIKE A WEASEL
Gen 5441: METHINKP ITMIS LIKE A WEASEL
Gen 6090: METHINKS ITMIS LIKE A WEASEL
Gen 7712: METHINKS IT IS LIKE A WEASEL

但是“weasel2.exe”会产生这个

Gen 896:  CB FNEOJWT WAVACDESTKWELIEF/c PnW~lcÏ
Gen 897:  CB FNEOJWT WAVACDESTKWELIEF
Gen 981:  CP FNEOJWT WAVACDESTKWELIEF
Gen 1014:  CP FNEOJWT WAVLCDESTKWELIEFOWSE'•&E
Gen 1015:  CP FNEOJWT WAVLCDESTKWELIEF
Gen 1087:  CP FNEOJWT WAVLCDESTKWELSEF/c PnW~lcÏ

数组末尾还有其他不需要的字符。对于某些世代来说,这包括看起来像我的 PATH 环境变量的部分内容。根据我在其他 SO 问题上阅读的内容,这是因为数组末尾没有“\0”字符,但是当我添加

[Line 75] offspring[i][28] = '\0';

对于后代分配循环,程序因内存访问冲突而崩溃。甚至允许写入的最低索引是 8。“weasel2.exe”永远运行,虽然有些字符确实与 weasel 字符串匹配,但其他字符会继续无限变化,即使它们确实设法“进化”到右边特点。“weasel3.exe”每次都会因内存访问冲突而崩溃。编译时我收到以下警告:

weasel2.c:85:警告:来自不兼容指针类型的赋值
weasel2.c:89:警告:来自不兼容指针类型的赋值

但据我所知,这不是问题的原因(但我也可能错了)。

造成这种情况的原因是什么,为什么行为只在编译之间有所不同,而不是每个单独程序的测试?

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

int randomNumber(int high) {
    return rand() % high;
}

int generateOffspring(char* currentGen, int currentLen, char* letters, char** offspring, unsigned long children, int probability) {
    for (unsigned int i = 0; i < children; i++) {
        for (unsigned int c = 0; c < currentLen; c++) {
            if (randomNumber(probability) < 1) {
                offspring[i][c] = letters[randomNumber(27)];
            }
            else {
                offspring[i][c] = currentGen[c];
            }
        }
    }

    return 0;
}

int findLeastDistance(char* string, char** strings, int len, int count) {
    unsigned int matches = 0;
    unsigned int bestMatches = 0;
    unsigned int bestMatch = 0;

    for (unsigned int i = 0; i < count; i++) {
        for (unsigned int c = 0; c < len; c++) {
            if (string[c] == strings[i][c]) {
                matches++;
            }
        }

        if (matches > bestMatches) {
            bestMatches = matches;
            bestMatch = i;
        }

        matches = 0;
    }

    return bestMatch;
}

int main(int argc, char *argv[]) {
    system("PAUSE");
    time_t t;
    srand((unsigned)time(&t));
    unsigned int probability;

    if (argc == 2) {
        probability = (int)argv[1];
    }
    else {
        probability = 10000;
    }

    unsigned int children = 100;

    char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
    char target[] = "METHINKS IT IS LIKE A WEASEL";
    char currentGen[29];

    for (unsigned int i = 0; i < 29; i++) {
        currentGen[i] = letters[randomNumber(27)];
    }

    char** offspring[children];

    for (unsigned int i = 0; i < children; i++) {
        offspring[i] = malloc(29);
    }

    unsigned int bestMutation;
    unsigned int counter = 0;
    
    for (;;) {
        generateOffspring(currentGen, 28, letters, offspring, children, probability);

        bestMutation = findLeastDistance(target, offspring, 28, children);

        if (strcmp(currentGen, offspring[bestMutation])) {
            printf("Gen %d: %s\n", counter, offspring[bestMutation]);
        }

        strcpy(currentGen, offspring[bestMutation]);

        if (!strcmp(currentGen, target)) {
            break;
        }

        counter++;
    }

    for (unsigned int i = 0; i < children; i++) {
        free(offspring[i]);
    }

    free(currentGen);

    printf("\nEnd\n");

    return 0;
}

标签: arrayscnull-terminated

解决方案


第一步,generateOffspring按如下方式更改循环:

for (unsigned int c = 0; c < currentLen - 1; c++) {
    if (randomNumber(probability) < 1) {
        offspring[i][c] = letters[randomNumber(27)];
    }
    else {
        offspring[i][c] = currentGen[c];
    }
}
offspring[i][currentLen - 1] = 0; // terminate offspring[i]

请记住,要存储长度为 N 个字符的字符串,您需要一个长度至少为 N+1 个元素的数组来存储 0 终止符。

正如 Antti Haapala 指出的那样,你有同样的问题currentGen,所以你需要将那个循环更改为

for (unsigned int i = 0; i < 28; i++) {
    currentGen[i] = letters[randomNumber(27)];
}
currentGen[28] = 0;

推荐阅读