2017-01-03 3 views
-2

Я создал программу. Он работает так, как я хотел, чтобы он работал, но я не понимаю, почему.Почему цикл работает так?

Это функция.

void LiteraMajuscula(char *str) 
{ 
    int i; 

    i = 0; 
    while (str[i] != '\0') 
    { 
     if (i == 0 && str[i] >= 'a' && str[i] <= 'z') 
      str[i] -= 32; 
     if (str[i-1] == ' ' && str[i] >= 'a' && str[i] <= 'z') 
      str[i] -= 32; 
     i++; 
    } 
} 

Предполагается сделать символ нижнего регистра в верхний регистр один каждый раз, когда есть пространство, и только первый один, и если первый символ из строки в нижнем регистре, делает его верхний регистр.

Единственное, что я не понимаю, это str[i-1]. Я пробовал str[i], но ничего не меняет, а str[i-2] изменяет вторую букву в верхний регистр и верхний регистр вместо первого. Почему это?

+6

'ул [I-1]' не определено поведение (потому что вы начинаете с 'i = 0' – bolov

+2

' str [i-1] == '' 'является UB, если' i == 0' – BLUEPIXY

+2

Да, это должно было быть 'else if', так как первые утверждения if случай i = 0. – Lundin

ответ

0

Тело цикла является неправильное

while (str[i] != '\0') 
{ 
    if (i == 0 && str[i] >= 'a' && str[i] <= 'z') 
     str[i] -= 32; 
    if (str[i-1] == ' ' && str[i] >= 'a' && str[i] <= 'z') 
     str[i] -= 32; 
    i++; 
} 

Когда i равно ф 0, то в этом состоянии

if (str[i-1] == ' ' && str[i] >= 'a' && str[i] <= 'z') 
     ^^^^^^^^ 

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

while (str[i] != '\0') 
{ 
    if ((i == 0 || str[i-1] == ' ') && str[i] >= 'a' && str[i] <= 'z') 
     str[i] -= 32; 
    i++; 
} 

Это проверяет состояние является ли первый символ строки является альфа-символ или, если это не первый символ строки ли предыдущий символ пространства и текущий символ является альфа-символом.

Учтите, что функция не будет работать с символами EBCDIC.Лучше использовать стандартную функцию C toupper, указанную в заголовке <string.h>. Например,

while (str[i] != '\0') 
{ 
    if (i == 0 || str[i-1] == ' ') 
    { 
     if (isalpha((unsigned char)s[i])) s[i] = toupper((unsigned char)s[i]); 
    } 
    i++; 
} 

Я думаю, что желательно также проверить, является ли предыдущий символ символом табуляции. Также такие строковые функции обычно возвращают целевую строку.

Я бы записать функцию следующим образом

#include <stdio.h> 
#include <ctype.h> 

char * LiteraMajuscula(char *s) 
{ 
    for (size_t i = 0; 
     s[i += strspn(s + i, " \t")] != '\0'; 
     i += strcspn(s + i, " \t")) 
    { 
     if (isalpha((unsigned char)s[i])) s[i] = toupper((unsigned char)s[i]); 
    } 

    return s; 
} 

int main(void) 
{ 
    char s[] = "hello,\tworld!"; 

    puts(s); 
    puts(LiteraMajuscula(s)); 

    return 0; 
} 

Его выход

hello, world! 
Hello, World! 
+0

«Учтите, что функция не будет работать с символами EBCDIC». Как это плохо? – Lundin

+0

@Lundin Это зависит от вашей квалификации. Либо у вас достаточно квалификации, чтобы понять, что функция должна быть более общей, или у вас недостаточно квалификации, и для каждого конкретного случая написать отдельную функцию. –

3

str[i] -= 32; будет конвертировать символ по индексу i. Причина, по которой второй оператор if использует в своей проверке str[i-1] == ' ', поэтому он знает, находится ли текущий символ (на i) сразу после пробела (на i - 1). Причина, по которой преобразовала второй характер слов, когда вы изменили его str[i-2] == ' ', что вы изменили его, чтобы он преобразовал характер, если он (в i) был два символа после пробела (в i-2).

Как было отмечено в комментариях, код там неопределенное поведение, потому что нет ничего предотвратить str[i-1] == ' ' чек, когда i является 0, так str[i-1] будет доступ характер до того где str указывает.

Отдельно, так как большинство из условий и логики дублироваться между двумя if с, это, где вы будете использовать || (логическое ИЛИ):

while (str[i] != '\0') 
{ 
    if ((i == 0 || str[i-1] == ' ') && str[i] >= 'a' && str[i] <= 'z') 
     str[i] -= 32; 
    i++; 
} 

(Обратите внимание на () вокруг ||.)

|| короткого замыкания, поэтому, когда i является 0 и первый операнд верно, то второй операнд (str[i-1] == ' ') никогда не оценивали и поэтому вы избегаете неопределенного поведения.

0

Другие ответы Отправленные объяснили, что было не так с кодом.

Вот альтернативный способ, что функция может быть переписано, чтобы быть более эффективным и более портативным, с использованием функций из ctype.h:

void do_some_incorrect_capitalization (char str[]) 
{ 
    if(*str == '\0') 
    { 
    return ; 
    } 

    *str = toupper(*str); // special case, first letter in the string 
    str++; 

    while(*str != '\0') 
    { 
    if(isspace(*(str-1))) 
    { 
     *str = toupper(*str); 
    } 
    str++; 
    }  
} 

 Смежные вопросы

  • Нет связанных вопросов^_^