Проблема с указателем в C - что я здесь не так делаю? - PullRequest
2 голосов
/ 05 марта 2010

Для справки, я пишу приложение для чтения счетчиков на C для небольшого 16-разрядного портативного компьютера, на котором установлена ​​проприетарная версия DOS.

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

/* ...
 * beginning of switch block to check for keystrokes
 * ...
 */
case KEY_ENTER: {
    /* show what has been entered */
    if(needNew == 0) {
        /* calculate usage for new reading */
        double usg = 0;
        int ret = CalculateNewUsage(vlr, buf, &usg);
        VerifyReadScreen(vlr, ret, buf, &usg);
        needRedraw = TRUE;
    }
    break;
}
/* .... end switch statement */

vlr - указатель на структуру, которая содержит всю информацию об учетной записи / счетчике, buf имеет тип char[21], используемый для хранения числовых нажатий клавиш для чтения, которое обрабатывается над этим блоком. Все мои переменные содержат действительные данные, когда я проверяю их как до, так и после вызова CalculateNewUsage.

Однако, когда я снова проверяю переменные данные после ввода VerifyReadScreen, newread указывает куда-то случайно в памяти и возвращает то, что похоже на уведомление об авторских правах. Интересно, что независимо от того, в какую учетную запись или какое чтение я вхожу - на экране выводятся те же недействительные данные для newread в VerifyReadScreen. Я передаю адрес на VerifyReadScreen так же, как CalculateNewUsage, но каким-то образом у меня получилось что-то другое.

Вот VerifyReadScreen:

BYTE VerifyReadScreen(const VLRREC * vlr,
                        const int status,
                        const char * newread,
                        const double * usage) {

    /* snip a whole bunch of irrelevant formatting code */

    printf("%s", (*newread)); /* prints funky copyright text */

    /* snip more irrelevant formatting code */
    return TRUE;
}   

Спасибо Джефроми за то, что он указал, что код, на котором я на самом деле печатаю newread в VerifyReadScreen, должен действительно читать:

printf("%s", newread); /* yay! */

потому что мне не нужно разыменовывать newread, потому что printf делает это для меня. По сути, я передавал указатель на указатель, который был произвольным местом в памяти.

Ответы [ 2 ]

7 голосов
/ 05 марта 2010

Я думаю, что я достаточно уверен, чтобы опубликовать это как ответ:

BYTE VerifyReadScreen(const VLRREC * vlr, const int status, const char * newread, const double * usage) {
...
    LCD_set_cursor_pos(19 - strlen(newread), 3);
    printf("%s", (*newread)); /* prints funky copyright text */
...
}

У вас есть строка (char*) newread, но в этом printf вы разыменовываете ее, что дает вам первый символ строки. Затем вы используете его в качестве аргумента для %s для printf, поэтому он пытается перейти к адресу памяти, указанному этим символом, и распечатать найденное там.

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

2 голосов
/ 05 марта 2010

Я не знаю, с какой проблемой вы столкнулись, но buffer, используемый в VerifyReadScreen длиной 21 символ, скорее всего переполнится:

if(strlen((*vlr).ServAdd) >= 20) {
    sprintf(buffer, "%20s", (*vlr).ServAdd);
}

Спецификатор формата %20s не мешает sprintf писать более 20 символов. Он просто дополняет строку пробелами, если она короче 20 символов (или вы хотите <= 20 в условии if?).

else {
    memset(buffer, 0x20, (int)(strlen((*vlr).ServAdd) / 2) + 1);
    strcat(buffer, (*vlr).ServAdd);
}

Здесь некоторые отступы выполняются в зависимости от длины строк, но я не вижу, как это могло бы гарантировать, что результат будет длиннее 20 символов.

...