Неизвестное усечение / перезапись строки C

У меня интересная проблема с памятью из-за простой манипуляции со строками. Сама проблема на самом деле не в чтении строки, а прямо перед ней, когда я пытаюсь вызвать строку.

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

Всякий раз, когда я запускаю это, строка if усеченная сразу после char * newToken имеет значение malloc'd. Таким образом, распечатка этого приводит к

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

Кто-нибудь знает, что это? Я посмотрел на другие примеры malloc, но не могу понять, как это происходит здесь.

РЕДАКТИРОВАТЬ: ПОЛНЫЙ КОД НИЖЕ. Обратите внимание, я студент колледжа, который только начал C, так что это не идеально для всех. Но это работает до этой ошибки.

Вызов функции происходит следующим образом. Main->initialReadAVL (эта часть работает отлично) Затем после вызова commandReadAVL, который отправляется commandReadAVL->ReadHelper (здесь снова работает нормально. Затем CleanUpString->removeSpaces(работает нормально) Затем CleanUpString->removeInvalid(ЭТО ТАМ, ГДЕ ОШИБКИ)

#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);
    }

}

ReadString ()

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;
    }

ВВОД: Commands.txt

i "Willy Wonka's Chochlate Factory"

INPUT testFile.txt

a b c d e f g h i j k l m n o p q r s t u v w x y z

Спасибо!

4 ответа

Решение
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);

поскольку token это char*, *token это char, Так sizeof(*token) является sizeof(char) - определенно не то, что вы хотите.

С помощью Дэвида Шварца и других постеров я смог найти ошибку в моей проблеме. Когда я выделял память для моего токена / вывода, я не выделял достаточно места.. Используя ошибочный код

malloc(sizeof(100) + 1);

а также

malloc(sizeof(*token) + 1);

оба из которых дали только пару байтов для выделения. Это вызвало проблему с буфером, в результате чего произошли случайные буквы и цифры / усечение. Первый приводит к пробелу, эквивалентному int + 1, а второй к char + 1. (поскольку я брал токен sizeof, равный размеру того, с чего он изначально начинался, char)

Чтобы это исправить, я изменил распределение моей токен-переменной на

malloc(strlen(token) + 1);

Это выделяет пробел, эквивалентный "токену" длины токена + 1. Разрешение соответствующего пространства для моей задачи, которое в конечном итоге будет содержать пробел <= token.

У вас почти наверняка есть переполнение буфера в некоторой части кода, которую вы нам не показываете. Если бы я догадался, я бы сказал, что вы выделяете слишком мало памяти для token чтобы содержать полную строку, которую вы пишете в нее в первую очередь.

Вы случайно не выделяли token используя тот же ошибочный код, который вы имеете в removeInvalid():

malloc(sizeof(100) + 1);
       ^^^^^^^^^^^ this doesn't allocate 101 characters, it allocates sizeof(int)+1
char *readHelper(FILE *in){
    char * token = malloc(sizeof(char *) + 1);
    if (stringPending(in)){
        token = readString(in);
    }
    else {
        token = readToken(in);
    }
    return token;
}

Трудно понять это, не видя readString или же readToken, но это не может быть правдой.

Во-первых, вы выделяете на один байт больше, чем необходимо для указателя на один или несколько символов. Какая польза от такой вещи? Если вы не храните указатель на один или несколько символов, зачем использовать sizeof(char *)? Если вы храните указатель на один или несколько символов, зачем добавлять один? Трудно представить причину, которая приводит к этой строке кода.

Затем в if, вы сразу теряете ценность, которую вы получили от malloc потому что ты перезаписываешь token используя его для хранения чего-то еще. Если вы не собираетесь использовать значение, которое вы присвоили tokenзачем ты это вообще назначил?

Грубо говоря, большая часть этого кода просто не имеет никакого смысла. Без комментариев трудно понять причину, чтобы мы могли указать, что с ней не так.

Либо за этой строкой кода были рассуждения, и в этом случае это совершенно неверное рассуждение. Или, что еще хуже, строка кода была добавлена ​​без объяснения причин в надежде, что она как-нибудь сработает. Ни один из методов не даст рабочий код.

Когда вы пытаетесь отладить код, сначала удалите все, что вы добавили экспериментально или что вы не поняли. Если ты понимаешь malloc(sizeof(char *) + 1), затем, пожалуйста, объясните, что вы думаете, что он делает, чтобы ваше понимание могло быть исправлено.

Почему вы думали, что вам нужен буфер, который был на один байт больше, чем размер указателя на один или несколько символов?

Другие вопросы по тегам