C - Почему мой массив перезаписывается? - PullRequest
0 голосов
/ 05 июня 2018

Я мог бы начать с того, что обычно я программист на С ++, и я неопытен в том, чтобы делать ввод-вывод с С. Я пишу простую программу, которая пытается прочитать список слов пользователя, расположив по алфавитубуквы в каждом слове (с сохранением заглавных букв) и распечатайте их обратно в полученном порядке.

Кажется, я точно определил проблемную область (выделена в нижней части моего кода), но даже с микроскопом в моем коде это кажется мне какой-то византийской ошибкой.Вот мой код:

int comp (const void* left, const void* right) { //for qsort
    return ( *(char*)left - *(char*)right );
}

int main() {
    printf("Please enter a sentence. Press enter to end.\n");
    char str[500]; 
    if (!fgets(str, sizeof(str), stdin)) { //grab line
        printf("die\n");
        return 1;
    }
    str[strcspn(str, "\n")] = 0; //finds newline and kills it
    printf("You entered:\n%s\n", str);

    char abc[sizeof(str)];    //alphabetized array
    int abc_count = 0;        //keeps track of effective size of abc
    char c;   
    char* word = &c;  //will read each word from line
    char* strr = str;  //made to get around a compiler error
    int offset;
    char capitals[25]; //keeps track of capital letters
    int cap = 0;
    int i = 0;

    while (sscanf(strr, " %s%n", word, &offset) == 1) { //%n gives number of characters read by sscanf

        if (i == 0) offset++;  //fixes length of first word, which has no space preceding it
        printf("read a word of size %d\n", offset-1);
        //make sure all letters are lowercase
        cap = 0;
        for (int j = 0; j < (offset-1); j++) {
            if ( isupper(word[j])) {  //if letter is capitalized
                word[j] = tolower(word[j]);     //lowercase it for sorting
                capitals[cap] = word[j]; //remember letter for later
                cap++;
            }
        }
        qsort(word, (offset-1)/sizeof(*word), sizeof(*word), comp);    //Alphabetize. "-1" makes offset not count space in length
        //recapitalize letters
        for (int j = 0; j < (offset-1); j++) {
            for (int k = 0; k < cap; k++) {
                if (word[j] == capitals[k]) {
                    word[j] = toupper(word[j]);  //recapitalize letter
                    capitals[k] = 0;    //capital has been used
                    break;
                }
            }
        }
        //write word to abc                ///// PROBLEM AREA /////
        printf("%s\n", word);
        for (int j = 0; j < (offset-1); j++) {
            if (i == 0) printf("first word: %s\n", word); //debugging
            printf("word[%d] = %c | ", j, word[j]);      //debugging
            abc[abc_count] = word[j];
            printf("abc[%d] = %c\n", abc_count, word[j]);  //debugging
            abc_count++;
        }
        if (i == 0) printf("first word: %s\n", word);
        abc[abc_count] = ' ';
        printf("abc[%d] = space\n", abc_count);
        abc_count++;
        if (i == 0) printf("first word: %s\n", word);
        printf("so far: %s\n\n", abc);
        if (i == 0) offset--; //undo correction for first word
        strr += offset; //stops infinite loop by moving pointer. This line is the reason "strr" exists instead of using "array type" str
        i = 1;
    } //while loop                       ///// PROBLEM AREA /////

    printf("Alphabetized: \n");
    for (i = 0; i < abc_count; i++) {
        printf("%c", abc[i]);  //I write directly from memory because somehow a null char is being added to "abc" after every word entered
    }
    printf("\n");

    return 0;
}

Что я нашел благодаря широкому использованию отладки «printf», так это то, что одна из моих строк (всегда первое введенное слово) перезаписывается, а также передняя частьстрока для вывода.Это очень странно.Вот пример вывода:

Please enter a sentence. Press enter to end.
Chocolate word cAT
You entered:
Chocolate word cAT
read a word of size 9
aCcehloot                //word is sorted as desired. No issues
first word: aCcehloot
word[0] = a | abc[0] = a
first word: aacehloot      //here you can see the strange overwrite behavior
word[1] = a | abc[1] = a
first word: aaaehloot
word[2] = a | abc[2] = a
first word: aaaahloot
word[3] = a | abc[3] = a
first word: aaaaaloot
word[4] = a | abc[4] = a
first word: aaaaaaoot
word[5] = a | abc[5] = a
first word: aaaaaaaot
word[6] = a | abc[6] = a
first word: aaaaaaaat
word[7] = a | abc[7] = a
first word: aaaaaaaaa    //first word is completely replaced by its first alphabetical letter
word[8] = a | abc[8] = a
first word: aaaaaaaaaa?n
abc[9] = space
first word: aaaaaaaaaa n
so far: aaaaaaaaa n

read a word of size 4
dorw
word[0] = d | abc[10] = d
word[1] = o | abc[11] = o
word[2] = r | abc[12] = r
word[3] = w | abc[13] = w
abc[14] = space
so far: orw

read a word of size 3
AcT
word[0] = A | abc[15] = A
word[1] = c | abc[16] = c
word[2] = T | abc[17] = T
abc[18] = space
so far: cT

Alphabetized:
cT  aaaaa dorw AcT
//the final result is shown as: everything but the first letter of the last word, 
//a cut-off remainder of the first overwritten word, the second word, then the third word

1 Ответ

0 голосов
/ 05 июня 2018

Эта часть выглядит странно:

char c;   
char* word = &c;  //will read each word from line

Здесь word - указатель на символ, указывающий на один символ (т. Е. c).

Тем не менее, вы делаете:

sscanf(strr, " %s%n", word, &offset)

, который будет считывать несколько символов в память, указанную word.Другими словами, вы пишете в память за пределами c, что является неопределенным поведением (UB).

Я бы ожидал, что word будет массивом символов (или указателем на массив символов), чтобы избежать выхода из него.ограниченный доступ (т.е. UB).

...