У меня проблемы с указателем и функциональными частями. Меня просят написать функцию: char *mytoupper(char *p)
, которая преобразует строку, на которую указывает p
, в верхний регистр, а затем в returns
p
.
Обратите внимание, что я не могу использовать функции <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;
}
Пожалуйста, помогите мне это проверить. Я занимаюсь кодированием всего месяц. Я ценю всю помощь.
Выглядит правильно, за исключением того, что вы должны возвращать char*
(который указывает на начало того места, на которое указал p
). Также избегайте «магических чисел»: вместо 32
вы должны использовать 'a' - 'A'
для получения самодокументированного кода.
Просто укажите, тип возврата в определение и декларация в mytoupper()
разные. В вашем случае mytoupper()
не нужно ничего возвращать, оставьте тип возврата как void
.
Этот код не должен даже компилироваться, поскольку определение вашей функции не соответствует объявлению функции. Вы вообще игнорируете ошибки и запускаете программу, которая была скомпилирована некоторое время назад, до того, как вы допустили эту ошибку?
Это работает? Если да, то так вы это делаете. Если нет, объясните как.
Функция char mytoupper(char *p) {
должна быть char * mytoupper(char *p) {
, как в прототипе. char result = * p должен быть char * result = p
.
if ((*p >= 'a') && (*p <= 'z'))
Это предположение, которое не поддерживается по стандарту C. Только цифры символов '0'
-'9'
гарантированно будут представлены последовательно в наборе символов выполнения.
Имейте в виду, что существует несколько (редко используемых) наборов символов, не имеющих соседних буквенных символов (например, EBCDIC), для них это не сработает. Большинство наборов символов совместимы с ASCII, но содержат дополнительные буквы (с тремой, ударениями, тильдой, седилем, запятой (румынский), ...). Они не покрываются, как и нелатинские алфавиты (например, кириллица).
Новички @AndrewHenle могут предположить, что строки закодированы в ASCII, поскольку другие кодировки с непоследовательными буквами в настоящее время почти не используются (практически существует только один - EBCDIC [и его советские варианты], и вероятность того, что OP программируется с использованием мэйнфреймов IBM, составляет довольно низкий). Все популярные стандарты кодирования букв ASCII, UTF-8, UTF16, UTF32 имеют последовательные коды.
Хм, наверное не в смысле решаемой задачи, но toupper
определяется не в string.h
, а в ctype.h
. Так что если взять задачу дословно, можно было бы ее использовать ...;)
В вашем объявлении функции
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
.
Вы должны рассмотреть, чтобы проверить также сторону "z"!
Эта часть очень зависит от платформы:
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
, и не требуется приведение (особенно к символу без знака), и следует избегать преобразований со знаком <=> без знака
У вас нет необходимости иметь несколько return
, которые делают код менее читаемым и подверженным ошибкам. Безопаснее использовать goto
для выхода из функции (и IMO это единственный случай, когда goto
приемлем), как это делается, например, в ядре Linux.
Вопрос гласит, что нельзя использовать <string.h>
, и я уважал это - toupper()
находится в <ctype.h>
, что не запрещено. И нет, goto
здесь не подходит.
вашего многократного возврата нет. Достаточно изменить условие, чтобы код стал намного лучше
Это необходимо, потому что char
может быть подписанным типом, но функции <ctype.h>
принимают int
с примечанием Если значение ch
не может быть представлено как unsigned char
и не равно EOF
, поведение не определено.. Поэтому нам нужно убедиться, что отрицательные значения приводятся к unsigned char
перед повышением до int
.
пожалуйста ... Это заклинание не имеет никакого эффекта, и вы совершенно неправильно поняли это замечание. godbolt.org/g/mdMJDV
@P__J__, другой вариант (который я сейчас выбрал) - последовать примеру семейства <string.h>
и настоять на том, чтобы вызывающая сторона обязана не передавать нулевые указатели. Поскольку вызывающий объект никогда не может передать NULL, здесь это безопасно. :-)
Все рекомендации по кодированию, с которыми я встречался до сих пор, скорее принимали бы несколько возвратов, чем goto ... В данном случае было бы намного проще инвертировать условие if и поместить цикл в тело if ...
@P__J__, аргумент toupper
необходимо преобразовать в unsigned char
, чтобы избежать риска неопределенного поведения. Или вы предполагаете, что ответ также неверен и никогда не был «исправлен»?
Это приведение не имеет никакого эффекта не защищает от UB, так как не влияет на сгенерированный код.
Я не уверен, что вы имеете в виду под «не влияет на сгенерированный код». Вы имеете в виду код, который генерирует ваш текущий компилятор? Вполне разумно, чтобы он работал для вас без требуемого приведения (Undefined Behavior - неопределенный, и одна из возможностей - делать то, что вы хотите). Если вы не докажете то же самое для компиляторов все (включая все будущие компиляторы), ваше утверждение безосновательно. Пожалуйста, перечитайте ответ Кита Томпсона, который я связал в предыдущем комментарии, в частности, в предпоследнем абзаце.
@P__J__ Кстати, ваша ссылка Godbolt показывает два разных объектных кода (один с movsx
и один с movzx
, как видно из этого более простого сравнения: godbolt.org/g/qd686a (GCC 8.2 на x86-64). Я должен уйти сейчас; вернемся в понедельник.
Убедитесь, что прототип функции соответствует определению.