未知的C字符串截断/覆盖

时间:2016-10-16 18:16:38

标签: c memory truncation

我有一个简单的字符串操作有趣的内存问题。问题本身实际上并不是在读取字符串时,而是在我尝试调用字符串之前。

const string FIZZ = " Fizz\n";
const string BUZZ = " Buzz\n";
const string FIZZBUZZ = " FizzBuzz\n";
    ...
var sb = new StringBuilder();
int i = 0;

while(true)
{       
    sb.Append(i+3);
    sb.Append(FIZZ);        
    sb.Append(i+5);
    sb.Append(BUZZ);        
    sb.Append(i+6);
    sb.Append(FIZZ);        
    sb.Append(i+9);
    sb.Append(FIZZ);        
    sb.Append(i+10);
    sb.Append(BUZZ);        
    if(i+12 > 100)
        break;
    sb.Append(i+12);
    sb.Append(FIZZ);    
    i+=15;
    sb.Append(i);
    sb.Append(FIZZBUZZ);
}

Console.Write(sb.ToString());

每当我运行它时,字符串如果在char * newToken之后被截断,则是malloc'd。因此打印输出结果

char *removeInvalid(char *token){
    fprintf(stderr," Before: %s \n", token);
    char *newToken = malloc(sizeof(100) + 1);
    fprintf(stderr," After: %s \n", token);
}

任何人都知道这是什么?我查看了malloc的其他示例,但无法弄清楚它是如何出错的。

编辑:以下完整代码。请注意我是一名刚开始学习C的大学生,所以任何人都不是完美的。但它可以解决这个错误。

函数调用如下。 Main-> initialReadAVL(此部分完美运行) 然后在调用commandReadAVL之后执行commandReadAVL-> ReadHelper(这里再次正常工作。 然后CleanUpString-> removeSpaces(工作正常) 然后CleanUpString-> removeInvalid(这就是错误)

Before: Willy Wanka's Chochlate Factory
After: Will Wanka's Chochlate F!

ReadString()

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "node.h"
#include "avl.h"
#include "scanner.h"
#include "bst.h"

/* Options */
int avlSwitch = 0;
int bstSwitch = 0;
int insertSwitch = 0;
int deleteSwitch = 0;
int frequencySwitch = 0;
int displaySwitch = 0;
int statisticSwitch = 0;

int ProcessOptions(int argc, char **argv);
char *cleanUpString(char *token);
char *turnToLowerCase(char *token);
char *removeSpaces(char *token);
char *removeInvalid(char *token);
char *readHelper(FILE *in);
void Fatal(char *fmt, ...);
void preOrder(struct node *root);
void initialReadAVL(avl *mainAVL, FILE *in);
void initialReadBST(bst *mainBST, FILE *in);
void commandReadBST(bst *mainBST, FILE *commandList);
void commandReadAVL(avl *mainAVL, FILE *commandList);

int main(int argc, char **argv) {
    struct avl *mainAVL;
    struct bst *mainBST;
    FILE *text;
    FILE *commandList;


    if(argc != 4){
        Fatal("There must be 4 arguments of form 'trees -b corpus commands' \n");
    }

    int argIndex = ProcessOptions(argc,argv);

    text = fopen(argv[2], "r");
    commandList = fopen(argv[3], "r");

    //Protect against an empty file.
    if (text == NULL){
        fprintf(stderr,"file %s could not be opened for reading\n", argv[2]);
        exit(1);
    }

    if (commandList == NULL){
        fprintf(stderr,"file %s could not be opened for reading\n", argv[3]);
        exit(1);
    }


    if (avlSwitch){
        mainAVL = newAVL();
        initialReadAVL(mainAVL, text);
        preOrder(mainAVL->root);
        fprintf(stderr,"\n");
        commandReadAVL(mainAVL, commandList);
        preOrder(mainAVL->root);
        fprintf(stderr,"\n");
    }
    else if (bstSwitch){
        mainBST = newBST();
        initialReadBST(mainBST, text);
        preOrder(mainBST->root);
        commandReadBST(mainBST, commandList);
        preOrder(mainBST->root);
    }


    return 0;
}


void commandReadAVL(avl *mainAVL, FILE *commandList){
    char *command;
    char *textSnip;
    while(!feof(commandList)){
        command = readHelper(commandList);
        textSnip = readHelper(commandList);
        textSnip = cleanUpString(textSnip);

        if(command != NULL){
            switch (command[0]) {
            case 'i':
                fprintf(stderr,"%s \n", textSnip);
                insertAVL(mainAVL, textSnip);
                break;
            case 'd':
                deleteAVL(mainAVL, textSnip);
                break;
            case 'f':
                break;
            case 's':
                break;
            case 'r':
                break;
            default:
                Fatal("option %s not understood\n",command);
            } 
        }

    }
}

void commandReadBST(bst *mainBST, FILE *commandList){
    char *command;
    char *textSnip;
    while(!feof(commandList)){
        command = readHelper(commandList);
        textSnip = readHelper(commandList);
        textSnip = cleanUpString(textSnip);
        if(command != NULL){
            switch (command[0]) {
                case 'i':
                    insertBST(mainBST, textSnip);
                    break;
                case 'd':
                    deleteBST(mainBST, textSnip);
                    break;
                case 'f':
                    break;
                case 's':
                    break;
                case 'r':
                    break;
                default:
                    Fatal("option %s not understood\n",command);
                } 
        }
    }
}


char *readHelper(FILE *in){
    char *token;
    if (stringPending(in)){
        token = readString(in);
    }
    else {
        token = readToken(in);
    }
    return token;
}

void initialReadBST(bst *mainBST, FILE *in){
    char *token;
    while(!feof(in)){

        token = readHelper(in);
        token = cleanUpString(token);
        if (token != NULL){
            insertBST(mainBST, token);
        }
    }
}

void initialReadAVL(avl *mainAVL, FILE *in){
    char *token;
    while(!feof(in)){

        token = readHelper(in);
        token = cleanUpString(token);
        if (token != NULL){
            insertAVL(mainAVL, token);
        }
    }
}

//Helper Function to clean up a string using all the prerequisites. 
char *cleanUpString(char *token){
    char *output = malloc(sizeof(*token)+ 1);
    if (token != NULL){
        output = removeSpaces(token);
         fprintf(stderr,"before : %s \n", output);
        output = removeInvalid(output);
         fprintf(stderr,"%s \n", output);
        output = turnToLowerCase(output);
        return output;
    }
    return NULL;

}

//Helper function to turn the given string into lower case letters
char *turnToLowerCase(char *token){
    char *output = malloc(sizeof(*token) + 1);
    for (int x = 0; x < strlen(token); x++){
            output[x] = tolower(token[x]);
        }
    return output;
}

//Helper function to remove redundent spaces in a string.
char *removeSpaces(char *token){
    char *output;
    int x = 0;
    int y = 0;

    while (x < strlen(token)){
        if (token[x]== ' ' && x < strlen(token)){
            while(token[x] == ' '){
                x++;
            }
            output[y] = ' ';
            y++;
            output[y] = token[x];
            y++;
            x++;
        }
        else {
            output[y] = token[x];
            y++;
            x++;
        }

    }
    return output;

}

char *removeInvalid(char *token){
    fprintf(stderr," Before: %s \n", token);
    char *newToken = malloc(sizeof(* token)+ 1);
    fprintf(stderr," After: %s \n", token);


    int x = 0;
    int y = 0;
    while (x < strlen(token)){
        if (!isalpha(token[x]) && token[x] != ' '){
            x++;
        }
        else {
            newToken[y] = token[x];
            y++;
            x++;
        }
    }
    return newToken;
}


//Processes a system ending error. 
void Fatal(char *fmt, ...) {
    va_list ap;

    fprintf(stderr,"An error occured: ");
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);

    exit(-1);
    }


//Processes the options needed to be executed from the command line
int ProcessOptions(int argc, char **argv) {
    int argIndex;
    int argUsed;
    int separateArg;

    argIndex = 1;

    while (argIndex < argc && *argv[argIndex] == '-')
        {
        /* check if stdin, represented by "-" is an argument */
        /* if so, the end of options has been reached */
        if (argv[argIndex][1] == '\0') return argIndex;

        separateArg = 0;
        argUsed = 0;

        if (argv[argIndex][2] == '\0')
            {
            separateArg = 1;
            }

        switch (argv[argIndex][1])
            {
            case 'b':
                bstSwitch = 1;
                break;
            case 'a':
                avlSwitch = 1;
                break;
            default:
                Fatal("option %s not understood\n",argv[argIndex]);
            }

        if (separateArg && argUsed)
            ++argIndex;

        ++argIndex;
        }

    return argIndex;
}


void preOrder(struct node *root) {
    if(root != NULL)
    {
        fprintf(stderr,"%s ", root->key);
        preOrder(root->lChild);
        preOrder(root->rChild);
    }

}

INPUT:Commands.txt

char *
readString(FILE *fp)
    {
    int ch,index;
    char *buffer;
    int size = 512;

    /* advance to the double quote */

    skipWhiteSpace(fp);
    if (feof(fp)) return 0;

    ch = fgetc(fp);
    if (ch == EOF) return 0;

    /* allocate the buffer */

    buffer = allocateMsg(size,"readString");

    if (ch != '\"')
        {
        fprintf(stderr,"SCAN ERROR: attempt to read a string failed\n");
        fprintf(stderr,"first character was <%c>\n",ch);
        exit(4);
        }

    /* toss the double quote, skip to the next character */

    ch = fgetc(fp);

    /* initialize the buffer index */

    index = 0;

    /* collect characters until the closing double quote */

    while (ch != '\"')
        {
        if (ch == EOF)
            {
            fprintf(stderr,"SCAN ERROR: attempt to read a string failed\n");
            fprintf(stderr,"no closing double quote\n");
            exit(6);
            }
        if (index > size - 2) 
            {
            ++size;
            buffer = reallocateMsg(buffer,size,"readString");
            }

        if (ch == '\\')
            {
            ch = fgetc(fp);
            if (ch == EOF)
                {
                fprintf(stderr,"SCAN ERROR: attempt to read a string failed\n");
                fprintf(stderr,"escaped character missing\n");
                exit(6);
                }
            buffer[index] = convertEscapedChar(ch);
            }
        else
            buffer[index] = ch;
        ++index;
        ch = fgetc(fp);
        }

    buffer[index] = '\0';

    return buffer;
    }

INPUT testFile.txt

i "Willy Wonka's Chochlate Factory"

谢谢!

4 个答案:

答案 0 :(得分:0)

你几乎肯定在代码的某些部分有一个缓冲区溢出,而你没有向我们展示。如果我猜测,我会说你为token分配的存储量太少,不能包含你写入的全部字符串。

您是否有机会使用token中的相同错误代码分配removeInvalid()

malloc(sizeof(100) + 1);
       ^^^^^^^^^^^ this doesn't allocate 101 characters, it allocates sizeof(int)+1

答案 1 :(得分:0)

char *readHelper(FILE *in){
    char * token = malloc(sizeof(char *) + 1);
    if (stringPending(in)){
        token = readString(in);
    }
    else {
        token = readToken(in);
    }
    return token;
}

如果不能看到readStringreadToken,很难理解这一点,但这可能是正确的。

首先,分配比指向一个或多个字符所需的字节多一个字节。这样的事情会有什么用处?如果您没有存储指向一个或多个字符的指针,为什么要使用sizeof(char *)?如果要存储指向一个或多个字符的指针,为什么要添加一个?很难想象导致这一行代码的推理。

然后,在if中,您会立即失去从malloc返回的值,因为您通过使用它来存储其他内容来覆盖token。如果您没有使用分配给token的值,为什么要分配它?

直言不讳地说,很多代码根本没有任何意义。没有评论,很难理解推理,所以我们可以指出它的错误。

要么在这一行代码背后有理由,在这种情况下它只是完全错误的推理。或者更糟糕的是,代码行没有任何推理,希望它能以某种方式工作。这两种方法都不会产生工作代码。

当您尝试调试代码时,请首先删除您通过实验添加或您不理解的任何内容。如果您理解malloc(sizeof(char *) + 1),请说明您的想法,以便纠正您的理解。

为什么你认为你需要一个比指向一个或多个字符的指针大一个字节的缓冲区?

答案 2 :(得分:0)

char *turnToLowerCase(char *token){
    char *output = malloc(sizeof(*token) + 1);
    for (int x = 0; x < strlen(token); x++){
            output[x] = tolower(token[x]);
        }
    return output;
}

这可能是你的主要问题。您为两个字符分配了足够的空间,然后继续存储多个字符。你可能想要:

    char *output = malloc(strlen(token) + 1);

由于tokenchar**tokenchar。所以sizeof(*token)sizeof(char) - 绝对不是你想要的。

答案 3 :(得分:0)

在David Schwartz和其他海报的帮助下,我能够找到问题中的错误。当我为令牌/输出分配内存时,我没有分配足够的空间..使用

的错误代码
malloc(sizeof(100) + 1);

malloc(sizeof(*token) + 1);

两者都只产生了几个要分配的字节。这导致缓冲区问题导致随机字母和数字/截断发生。第一个导致空间等效于int + 1,第二个导致char + 1.(因为我正在使用sizeof 标记,它只是它最初开始的大小,一个字符)< / p>

为了解决这个问题,我将令牌变量的分配更改为

malloc(strlen(token) + 1);

这会分配一个等于&#34;字符串&#34;的空格。令牌长度+ 1.为我的问题留出适当的空间,最终会留下&lt; = token的空格。