2015-12-23 2 views
0

Я пытаюсь написать функцию, которая хранит данные в текстовом файле, используя fopen("filename","w"), где «filename» - это строка, введенная пользователем. Я решил сделать это с помощью функции getchar(), и мне нужна переменная счетчика, которая увеличивается с каждым нажатием клавиши. Здесь все становится запутанным и запутанным.Нечетное поведение функции getchar()

char *p; 
    int count = 0; 
    p = (char*)malloc(32*sizeof(char)); 
    do 
    { 
     *p = getchar(); 
     count++; 
    } 
    while(getchar() != '\n'); 

вплоть до 3-х символов, введенного ему нужно всего лишь 1 нажатие клавиши ввода и счетчик является точным до 2 знака после 2 не вытекает нечетный шаблон.

--------------------Input------------------------Count-------------------- 
-------------------- t -------------------------- 1 -------------------- 
-------------------- te ------------------------- 2 -------------------- 
-------------------- tes ----------------------- 2 -------------------- 
-------------------- test ------------------------ 3 --------------------- 
-------------------- test1 ----------------------- 3 --------------------- 
-------------------- test12 ---------------------- 4 --------------------- 
-------------------- test123 --------------------- 4 --------------------- 
-------------------- test1234 -------------------- 5 --------------------- 

В принципе, для каждых 2 дополнительных символов количество увеличивается на единицу.

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

+0

Не набрасывайте результат 'malloc' & friends in C! И 'sizeof (char)' определен для получения '1'. Это лишнее. – Olaf

ответ

0

Dave Schwartz прав насчет вызова getchar() дважды. В фрагменте кода есть один или два грубых края; давайте поговорим об этом, пока я немного расширю ответ Дэйва.

Язык C может быть ОЧЕНЬ плотным; в одной строке кода может происходить несколько вещей. Для кого-то, кто только начинает, я бы посоветовал вам писать длинную форму, даже если вы не думаете, что вам нужно ... затем консолидироваться, так как ваша ментальная модель того, что делает C, становится богаче.

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

int count = 0; 
p = (char*)malloc(32*sizeof(char)); 
char c = getchar(); /* first call to getchar() */ 
while(c != '\n') { 
    p[count] = c; /* easier to read than *p=getchar() */ 
    count++; /* could be combined... see below for a more C-like version. */ 
    /* question: what would happen if we increment count BEFORE we store in p's memory? */ 

    /* Also... your initial code was this: */ 
    /* *p = getchar(); */ 
    /* which is always assigning getchar to p[0]. */ 
    /* see below for more "idiomatic" way to do that. */ 
    /* see below for more "idiomatic" way to do that. */ 

    /* Get the next char, then let while() condition decide if */ 
    /* we come back into the loop body or proceed after it. */ 
    /* It is a common tactic to put an input value in a scratch variable */ 
    /* like 'c' and use it at different points in your loop. */ 

    c = getchar(); /* all additional calls to getchar. */ 
    /* note that we already declared c above the while-loop, so ok to re-use. */ 
} 
/* dont want to call getchar() again here... this is the problem dave called out. */ 
/* while(getchar() != '\n'); */ 
/* ...do some stuff here */ 
free(p); /* your logic does free the malloc at some point, yes? :-) */ 

Теперь вы могли попробовать свернуть как можно больше кода в одном выражении, как это возможно. Но , пожалуйста, не делайте этого - по крайней мере, пока вы не сможете с комфортом написать код, который 1) легко читается, и 2) легко предсказать, что он будет делать.

В конечном итоге вы найдете письмо код не является твердой частью.

Это чтение код, который был написан 2 недели (или 2 года назад), что сложно.

Давайте поговорим о следующем: while(), for() и do/while() для работы. Функция while() и for() дает вам возможность пропустить тело цикла ... do/while() будет всегда выполнять тело цикла, что может быть не таким, каким вы хотите в своей ситуации, так как пользователь мог просто нажать введите, указав первый символ как '\ n'.

для версии петли Рассмотрим, как логика может выглядеть usuing а, для() цикла:

int count = 0; 
p = (char*)malloc(32*sizeof(char)); 
for(char c = getchar(); c != '\n'; c = getchar()) { 
    p[count] = c; 
    count++; 
} 

сделать/в то время как версия Я нахожу его более трудным, чтобы написать это с делать/время(): Не так сложно написать код, но мне трудно сделать код читаемым, так что намерение ясно.

int count = 0; 
p = (char*)malloc(32*sizeof(char)); 
char c; 
do { 
    if(count >= 1 ) { 
     /* ugly... check count >= 1 so we don't save an uninitialized 'c' */ 
     p[count] = c; 
    } 
    count++; 
} while((c = getchar()); 
count--; /* adjust because we counted our '\n'. */ 

снова с некоторыми дополнительными C идиомы Это один трудно читать, потому что, чтобы понять это, вы должны быть в состоянии распутать в то время как() выражение, которое делает следующие вещи:

1) вызывает функцию GetChar() и присваивает результат нашей темп вар «с»

2) сравнивает результат этого присвоения «\ п»

3),() оценивает результат сравнения и только входит в тело цикла, если у нас есть что-то помимо '\ n' в temp char var 'c'.

Это много, чтобы обернуть вокруг вас голову, прежде чем вы сможете спросить: «Это правда?». FYI - это единственный пример, я на самом деле пытался бежать ...

#include <stdio.h> 
#include <malloc.h> 

void main(char **argv) { 
    printf("Please type some stuff:\n"); 

    int count = 0; 
    char *p = (char*)malloc(32*sizeof(char)); 
    char c; 
    while((c = getchar()) != '\n') { 
     p[count++] = c; 
     /* So... could we just saying the following instead? 
     *  *p++ = c; 
     */ 
    } 
    p[count] = '\0'; /* this would turn our malloced buffer into a C-style string */ 
    printf("found %d chars = '%s'\n", count, p); 
    free(p); 
} 

За один последний пункт, чтобы обдумать, рассмотреть следующие вопросы:

1) Есть ли ограничение на количество символов нашего пользователя можете напечатать, прежде чем они нажмут?

2) Предполагая, что № 1 имеет лимит, что это такое и почему?

3) Что вы можете сделать, чтобы добавить чек, чтобы убедиться, что мы не позволяем нашему пользователю набирать слишком много символов?

+1

'char c;' должен быть 'int c;', и вы также должны проверить, что это не 'EOF' –

+0

wow man, я ошеломлен вашим ответом. Спасибо, я ценю ваше время и усилия. Похоже, у этого сайта отличное сообщество, я его люблю. Спасибо вам, что вы были очень полезны, и я желаю вам счастливых праздников :). – Paragon

+0

@ M.M согласился ... лучше использовать 'int c' против 'char c'. Также лучше проверить, что getchar() возвращает -1, чтобы указать eof. Также лучше проверить наличие сбоя в malloc. :-) После того, как Paragon продолжит обучение, они могут прочитать об этом: https://stackoverflow.com/questions/3676796/what-does-getchar-exactly-do – jgreve

6

Вы звоните getchar два раза каждый раз в цикле. Вы нажимаете enter (newline) в конце символов. И вы натыкаетесь на счет до второй getchar. Таким образом, счет будет (n + 1)/2 округленный, где n - количество символов.

Плюс один для новой строки. Разделение на два, потому что для каждого приращения считываются два символа. И округляется, потому что приращение происходит до второго чтения.