Как мне написать заостренную функцию?

У меня проблемы с указателем и функциональными частями. Меня просят написать функцию: char *mytoupper(char *p), которая преобразует строку, на которую указывает p, в верхний регистр, а затем в returnsp.

Обратите внимание, что я не могу использовать функции <string.h>. Вот как я это делаю?

#include <stdio.h>

char *mytoupper(char *p);

int main(int argc, char const *argv[]) {
    char str[100];
    printf("Please enter a string: ");
    scanf("%s", str);

    mytoupper(str);
    printf("The upercased string is: %s\n", str);
}

char mytoupper(char *p) {
    char result = *p;
    while (*p != '\0') {
        if ((*p >= 'a') && (*p <= 'z')) {
            *p = *p - 32;
        }
        p++;
    }
    return result;
}

Пожалуйста, помогите мне это проверить. Я занимаюсь кодированием всего месяц. Я ценю всю помощь.

Убедитесь, что прототип функции соответствует определению.

dbush 10.08.2018 15:05

Выглядит правильно, за исключением того, что вы должны возвращать char* (который указывает на начало того места, на которое указал p). Также избегайте «магических чисел»: вместо 32 вы должны использовать 'a' - 'A' для получения самодокументированного кода.

Lundin 10.08.2018 15:06

Просто укажите, тип возврата в определение и декларация в mytoupper() разные. В вашем случае mytoupper() не нужно ничего возвращать, оставьте тип возврата как void.

Achal 10.08.2018 15:08

Этот код не должен даже компилироваться, поскольку определение вашей функции не соответствует объявлению функции. Вы вообще игнорируете ошибки и запускаете программу, которая была скомпилирована некоторое время назад, до того, как вы допустили эту ошибку?

0___________ 10.08.2018 15:09

Это работает? Если да, то так вы это делаете. Если нет, объясните как.

klutt 10.08.2018 15:09

Функция char mytoupper(char *p) { должна быть char * mytoupper(char *p) {, как в прототипе. char result = * p должен быть char * result = p.

Sir Jo Black 10.08.2018 15:37
if ((*p >= 'a') && (*p <= 'z')) Это предположение, которое не поддерживается по стандарту C. Только цифры символов '0'-'9' гарантированно будут представлены последовательно в наборе символов выполнения.
Andrew Henle 10.08.2018 16:17

Имейте в виду, что существует несколько (редко используемых) наборов символов, не имеющих соседних буквенных символов (например, EBCDIC), для них это не сработает. Большинство наборов символов совместимы с ASCII, но содержат дополнительные буквы (с тремой, ударениями, тильдой, седилем, запятой (румынский), ...). Они не покрываются, как и нелатинские алфавиты (например, кириллица).

Aconcagua 10.08.2018 16:29

Новички @AndrewHenle могут предположить, что строки закодированы в ASCII, поскольку другие кодировки с непоследовательными буквами в настоящее время почти не используются (практически существует только один - EBCDIC [и его советские варианты], и вероятность того, что OP программируется с использованием мэйнфреймов IBM, составляет довольно низкий). Все популярные стандарты кодирования букв ASCII, UTF-8, UTF16, UTF32 имеют последовательные коды.

0___________ 10.08.2018 16:35

Хм, наверное не в смысле решаемой задачи, но toupper определяется не в string.h, а в ctype.h. Так что если взять задачу дословно, можно было бы ее использовать ...;)

Aconcagua 10.08.2018 16:35
0
10
178
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

В вашем объявлении функции

char *mytoupper(char *p);

Вы говорите, что он возвращает char * (указатель на char)

В вашем определении вы говорите, что он возвращает только char

char mytoupper(char *p)

Два определения должны совпадать, поэтому измените его на

char *mytoupper(char *p)

а затем внутри функции не сохранять первый символ; сохранить указатель на первый символ и вернуть его.

char *result = p;
....
return result;

НЕ ПРОПУСТИТЕ ОШИБКИ И ПРЕДУПРЕЖДЕНИЯ

Ваш код не будет компилироваться, и вы получите сообщения об ошибках:

main.c:14:6: error: conflicting types for 'mytoupper'
 char mytoupper(char *p) {
      ^
main.c:3:7: note: previous declaration of 'mytoupper' was here
 char *mytoupper(char *p);
       ^
main.c: In function 'main':
main.c:8:5: warning: ignoring return value of 'scanf', declared with attribute warn_unused_result [-Wunused-result]
     scanf("%s", str);
     ^

указывая, где именно находится проблема.

Есть и другие проблемы, поэтому см. Исправленный (но работает только в том случае, если символы закодированы в ASCII)

char *mytoupper(char *p) 
{
    char *savedptr = p;
    if(p) 
    {
        while (*p) 
        {
            if ((*p >= 'a') && (*p <= 'z')) 
            {
                *p -= ('a' - 'A');
            }
            p++;
        }
    }
    return savedptr;
}

Если вы хотите преобразовать все символы в верхний регистр, взгляните на таблицу ASCII. Как вы, возможно, знаете, каждый символ имеет целочисленное значение. Например, A имеет значение 65. Поэтому выполните итерацию по всем элементам и проверьте, не превышает ли значение (int) 'a'. Если это так, тогда вычтите ((int) 'a' - (int) 'A') из символа.

Пример

int counter = 0;

while (p[counter] != '\0')
{
     if ((int) p[counter] >= (int) 'a')
     {
          p[counter] -= (int) 'a' - (int) 'A';
     }

    counter++;
}

Вам не нужно преобразовывать эти значения в целые числа. Проще, если все будет на char.

Tim Randall 10.08.2018 15:33

Вы должны рассмотреть, чтобы проверить также сторону "z"!

Sir Jo Black 10.08.2018 15:34
'A' уже является int: «Целочисленная символьная константа имеет тип int. ...»
Andrew Henle 10.08.2018 16:21
Ответ принят как подходящий

Эта часть очень зависит от платформы:

while (*p != '\0') {
    if ((*p >= 'a') && (*p <= 'z')) {
        *p = *p - 32;  
    }
    p++;
}

Хороший (переносимый) код не предполагает, что все буквы идут подряд, что нет букв вне a..z или что все строчные буквы на 32 меньше, чем прописные. Все три из этих предположений неверны, если вы не ограничиваете себя американским вариантом ASCII (только для пары примеров, заглавные буквы EBCDIC на 64 позиции выше, чем строчные, а ASCII в неамериканских регионах имеет буквы, такие как å и ø вне диапазона от a до z).

Вместо этого мы можем использовать возможности, предоставляемые в <ctype.h>, в частности здесь toupper():

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

char *mytoupper(char *p);

/* use (void) when we ignore arguments */
int main(void)
{
    char str[100];
    printf("Please enter a string: ");
    /* scanf("%s") is dangerous! Always */
    /* limit input length and check result */
    if (scanf("%99s", str) != 1) {
        fputs("Failed to read input!\n", stderr);
        return 1;
    }

    mytoupper(str);
    /* spelling fixed */
    printf("The uppercased string is: %s\n", str);
}

/* Convert the string pointed to by s into uppercase.
   s must not be a null pointer */
char *mytoupper(char *const s)
{
    for (char *p = s;  *p;  ++p) {
        *p = toupper((unsigned char)*p);
    }

    return s;
}

Обратите внимание, что это также устраняет некоторые проблемы в main() - см. Комментарии.

Он не хотел, чтобы вы использовали функции библиотеки. *p = toupper((unsigned char)*p) ошибочен, поскольку эти функции принимают параметр int, и не требуется приведение (особенно к символу без знака), и следует избегать преобразований со знаком <=> без знака

0___________ 10.08.2018 16:19

У вас нет необходимости иметь несколько return, которые делают код менее читаемым и подверженным ошибкам. Безопаснее использовать goto для выхода из функции (и IMO это единственный случай, когда goto приемлем), как это делается, например, в ядре Linux.

0___________ 10.08.2018 16:23

Вопрос гласит, что нельзя использовать <string.h>, и я уважал это - toupper() находится в <ctype.h>, что не запрещено. И нет, goto здесь не подходит.

Toby Speight 10.08.2018 16:25

вашего многократного возврата нет. Достаточно изменить условие, чтобы код стал намного лучше

0___________ 10.08.2018 16:29

Это необходимо, потому что char может быть подписанным типом, но функции <ctype.h> принимают int с примечанием Если значение ch не может быть представлено как unsigned char и не равно EOF, поведение не определено.. Поэтому нам нужно убедиться, что отрицательные значения приводятся к unsigned char перед повышением до int.

Toby Speight 10.08.2018 16:41

пожалуйста ... Это заклинание не имеет никакого эффекта, и вы совершенно неправильно поняли это замечание. godbolt.org/g/mdMJDV

0___________ 10.08.2018 16:48

@P__J__, другой вариант (который я сейчас выбрал) - последовать примеру семейства <string.h> и настоять на том, чтобы вызывающая сторона обязана не передавать нулевые указатели. Поскольку вызывающий объект никогда не может передать NULL, здесь это безопасно. :-)

Toby Speight 10.08.2018 16:49

Все рекомендации по кодированию, с которыми я встречался до сих пор, скорее принимали бы несколько возвратов, чем goto ... В данном случае было бы намного проще инвертировать условие if и поместить цикл в тело if ...

Aconcagua 10.08.2018 16:49

@P__J__, аргумент toupper необходимо преобразовать в unsigned char, чтобы избежать риска неопределенного поведения. Или вы предполагаете, что ответ также неверен и никогда не был «исправлен»?

Toby Speight 10.08.2018 16:51

Это приведение не имеет никакого эффекта не защищает от UB, так как не влияет на сгенерированный код.

0___________ 10.08.2018 17:00

Я не уверен, что вы имеете в виду под «не влияет на сгенерированный код». Вы имеете в виду код, который генерирует ваш текущий компилятор? Вполне разумно, чтобы он работал для вас без требуемого приведения (Undefined Behavior - неопределенный, и одна из возможностей - делать то, что вы хотите). Если вы не докажете то же самое для компиляторов все (включая все будущие компиляторы), ваше утверждение безосновательно. Пожалуйста, перечитайте ответ Кита Томпсона, который я связал в предыдущем комментарии, в частности, в предпоследнем абзаце.

Toby Speight 10.08.2018 17:14

@P__J__ Кстати, ваша ссылка Godbolt показывает два разных объектных кода (один с movsx и один с movzx, как видно из этого более простого сравнения: godbolt.org/g/qd686a (GCC 8.2 на x86-64). Я должен уйти сейчас; вернемся в понедельник.

Toby Speight 10.08.2018 17:31

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