LINUX.ORG.RU

Просьба проверить учебную программу на языке Си на наличие ошибок

 , ,


0

1

Исходный текст программы.

Указанная по ссылке заметка предназначена для людей, которые начинают изучать язык Си с использованием ОС GNU/Linux.
В случае обнаружения ошибок, недочетов в программе просьба сообщить.

Спасибо.

Deleted

    puts("Данные не указаны! Повторите ввод.");
    free(buf);
    return NULL;

Какой повторите, если у тебя цикла нет?

ziemin ★★
()
Ответ на: комментарий от Deleted

Не исключается возможность отсутствия символа '\n' в окончании строки.

Как бы исключается на маках, вроде. Там же '\r'.

ziemin ★★
()
Ответ на: комментарий от ziemin

ziemin

Как бы исключается на маках, вроде. Там же '\r'.

Разве стандартная библиотека языка Си не заменяет символ '\r' символом '\n' в автоматическом режиме?

P.S. Макинтош отсутствует, поэтому проверить не смогу.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 2)
    /* Если в строке обнаружен печатный символ ASCII,
     перейти к проверке следующего по порядку символа. */

    if (hi >= 32 && hi <= 126) {
      i++;
      continue;
    }

А если это русский?

    if (i+1 < read)
    {
      lo = (unsigned char) buf[i+1];

      // Учитывать символы: А-Я, а-п, Ё.
      if ( hi == 208 && ((lo >= 144 && lo <= 191) || lo == 129) ) {
        i += 2;
        continue;
      }

      // Учитывать символы: р-я, ё.
      if ( hi == 209 && ((lo >= 128 && lo <= 143) || lo == 145) ) {
        i += 2;
        continue;
      }
    }

А если это не хрюникод?

Неужто сложно нормальные уже готовые макросы, использующие твою локаль, юзать? А?

Вот тебе пример:

cat 1.c 
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>

int main(int argc, char **argv){
	int i;
	char *s, c;
	if(argc != 2) return 1;
	setlocale(LC_ALL, "");
	s = argv[1];
	for(i = strlen(s); i > 0; i--,s++){
		c = *s;
		if(!isprint(c)){
			printf("non printable symbol\n");
			continue;
		}
		if(isalpha(c)){
			if(islower(c)) printf("lowercase");
			else printf("uppercase");
		printf(" letter");
		}else if(isdigit(c)) printf("digit");
		else if(ispunct(c)) printf("punctuation");
		else if(isspace(c)) printf("space");
		else if(isblank(c)) printf("blank");
		printf(" %c\n", c);
	}
	return 0;
}

gcc 1.c && ./a.out "Hello, 12345! Русский Язык."
uppercase letter H
lowercase letter e
lowercase letter l
lowercase letter l
lowercase letter o
punctuation ,
space  
digit 1
digit 2
digit 3
digit 4
digit 5
punctuation !
space  
uppercase letter Р
lowercase letter у
lowercase letter с
lowercase letter с
lowercase letter к
lowercase letter и
lowercase letter й
space  
uppercase letter Я
lowercase letter з
lowercase letter ы
lowercase letter к
punctuation .
Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Deleted

Разве стандартная библиотека языка Си не заменяет символ '\r' символом '\n' в автоматическом режиме?

нет. Вроде где-то есть константа с текущим истемным line break-ом, но тут точно не скажу.

arkhnchul ★★
()
Последнее исправление: arkhnchul (всего исправлений: 1)
Ответ на: комментарий от ziemin

Вон, я ниже привел пример, который везде будет работать. А не велосипед ТСа, который в случае не-хрюникода сдохнет.

Eddy_Em ☆☆☆☆☆
()

зачем делать так?

puts(«Укажите информацию о себе.\n»);

если puts автоматом ставит перенос строки

Deleted
()

Чисто стилистически объявление переменной hi перенёс бы в for.

backbone ★★★★★
()
Ответ на: комментарий от Eddy_Em

Eddy_Em, благодарю за подробное разъяснение в случае использования кодировки koi8-r в консоли.

Eddy_Em

А если это русский?
А если это не хрюникод?

ziemin

Твой случай не предусмотрели :)

В консоли GNU/Linux по умолчанию используется Юникод.

Deleted
()

И чем отличается return 1 от return 2 и return 3?

LongLiveUbuntu ★★★★★
()
Ответ на: комментарий от Deleted

Fermion

зачем делать так?
puts(«Укажите информацию о себе.\n»);
если puts автоматом ставит перенос строки

Для того, чтобы добавить пустую строку после текста «Укажите информацию о себе.»

Deleted
()
Ответ на: комментарий от Deleted

В консоли GNU/Linux по умолчанию используется Юникод.

если побуквоедствовать, то «по-умолчанию» ака минимальный обязательный стандарт - локаль C и первые 127 символов ASCII. Текущая распространенность юникода - только сложившееся положение.

arkhnchul ★★
()

Указанная по ссылке заметка предназначена для людей, которые начинают изучать язык Си с использованием ОС GNU/Linux.

Прога - полное гавно. Так уже не пишут лет пятнадцать. См. С++

типа std::string ans << cin.

corjc
()
Ответ на: комментарий от Eddy_Em
#!/bin/bash -x

wget "http://www.linux.org.ru/forum/development/10424734?lastmod=1398516552491#comment-10424865" -q -O- |
sed -n '/<code>cat 1.c/,/<\/code>/p' |
sed '1d;$d' |
sed 's/&lt;/</g' |
sed 's/&gt;/>/g' |
sed 's/&quot;/"/g' |
gcc -x c - -o a.out
./a.out "Hello, 12345! Русский Язык."
locale
$ ./run.sh
+ sed '1d;$d'
+ sed -n '/<code>cat 1.c/,/<\/code>/p'
+ wget 'http://www.linux.org.ru/forum/development/10424734?lastmod=1398516552491#comment-10424865' -q -O-
+ sed 's/&lt;/</g'
+ sed 's/&gt;/>/g'
+ sed 's/&quot;/"/g'
+ gcc -x c - -o a.out
+ ./a.out 'Hello, 12345! Русский Язык.'
uppercase letter H
lowercase letter e
lowercase letter l
lowercase letter l
lowercase letter o
punctuation ,
space  
digit 1
digit 2
digit 3
digit 4
digit 5
punctuation !
space  
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
space  
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
non printable symbol
punctuation .
+ locale
LANG=ru_RU.UTF-8
LANGUAGE=ru:en_US:en
LC_CTYPE="ru_RU.UTF-8"
LC_NUMERIC=ru_RU.UTF-8
LC_TIME=ru_RU.UTF-8
LC_COLLATE="ru_RU.UTF-8"
LC_MONETARY=ru_RU.UTF-8
LC_MESSAGES="ru_RU.UTF-8"
LC_PAPER=ru_RU.UTF-8
LC_NAME=ru_RU.UTF-8
LC_ADDRESS=ru_RU.UTF-8
LC_TELEPHONE=ru_RU.UTF-8
LC_MEASUREMENT=ru_RU.UTF-8
LC_IDENTIFICATION=ru_RU.UTF-8
LC_ALL=
anonymous
()
Ответ на: комментарий от anonymous

Хм, а и правда. Блин, я и забыл, что с хрюникодом стандартные функции не работают — надо всякие wprintf использовать, т.к. кодировка не однобайтная, а говно.

Ну, можно сделать соответствующую замену для получения универсального случая. Правда, нафиг это нужно, не знаю. Хрюникод в консоли не нужен.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

а если ты китаец русского происхождения по одному из родителей и отправляешь письмо через консоль на историческую родину второго родителя, в семью где знают только родной язык?

anonymous
()
Ответ на: комментарий от arkhnchul

И что ты мне хочешь показать? Ядро и драйверы пишут на С. А задачи типа ввода с консоли лучше писать на С++ это по сути тот же С, но гораздо лучше.

corjc
()
Ответ на: комментарий от corjc

И что ты мне хочешь показать?

что вполне себе пишут и на C прям щас, а не 15 лет назад. Ядро - просто первое, что в голову пришло, есть еще гора всяческих coreutils с башами итд.

С++ это по сути тот же С, но гораздо лучше.

спорно. Очень.

arkhnchul ★★
()
Ответ на: комментарий от ziemin

А в чём проблема-то? Вроде такая запись распространена.

ну, например, у нас cp1251 тоже распространена. А кое-где - koi8-r. Вот в том и проблема - в нехилой возможности получить что-то больше 127-го символа.

arkhnchul ★★
()
Ответ на: комментарий от anonymous

В этом случае просто открывается еще одна консолька, там устанавливаются кетайские шрефты с хрюникодом, пишется письмецо и отправляется. Но то — проблема китайцев. Давно бы уже могли на латинницу перейти, наркоманы чертовы!

Eddy_Em ☆☆☆☆☆
()
if (dest == NULL) {
    free(name);
    return 2;
  }

  work = prompt("Каков род вашей деятельности?");
  if (work == NULL) {
    free(name);
    free(dest);
    return 3;
  }

  printf("\n%s, вы живете в %s. Род вашей деятельности: %s.\n",
    name, dest, work);

  free(name);
  free(dest);
  free(work);

Это ужасно.

  int exit_code = 0;
  ...
  name = prompt("Как вас зовут?");
  if (name == NULL) {
    exit_code = 1;
    goto fail_name;
  }

  dest = prompt("Как называется населенный пункт, в котором вы живете?");
  if (dest == NULL) {
    exit_code = 2;
    goto fail_dest;
  }

  work = prompt("Каков род вашей деятельности?");
  if (work == NULL) {
    exit_code = 3;
    goto fail_work;
  }

  printf("\n%s, вы живете в %s. Род вашей деятельности: %s.\n", 
    name, dest, work);

  free(work);
fail_work:
  free(dest);
fail_dest:
  free(name);
fail_name:
  
  return exit_code;
anonymous
()
Ответ на: комментарий от anonymous

может лучше так?

int func(char**name, char**dest, char**work)
{
  *name = prompt("Как вас зовут?");
  if (*name == NULL) return 1;

  *dest = prompt("Как называется населенный пункт, в котором вы живете?");
  if (*dest == NULL) return 2;

  *work = prompt("Каков род вашей деятельности?");
  if (*work == NULL) return 3;

  return 0;
}
int main(void)
{
  char
    *name = NULL, // Имя пользователя.
    *dest = NULL, // Место его проживания.
    *work = NULL; // Род деятельности.

  puts("Укажите информацию о себе.\n");
  int exitcode = func(&name, &dest, &work);
  if(exitcode==0)
    printf("\n%s, вы живете в %s. Род вашей деятельности: %s.\n",
      name, dest, work);

  free(name);
  free(dest);
  free(work);

  return 0;
}

anonymous
()
Ответ на: комментарий от anonymous

А если у тебя вместо free код, подтирающий за кем-то, причем подтирать кроме него нужно за теми и только за теми участками, которые выполнялись до него?

И зачем там функция?

anonymous
()
Ответ на: комментарий от anonymous

то вернемся к подобию goto

switch(func()){
case 0: printf("\n%s, вы живете в %s. Род вашей деятельности: %s.\n", name, dest, work);
        free(work);
case 3: free(dest)
case 2: free(name);
case 1:
}
но в конкретном то случае, зачем оно?

anonymous
()
Ответ на: комментарий от anonymous

Что за паническая боязнь goto?

goto — очень полезная операция. А в случае подобных функций, где куча чего выделяется, она прямо-таки необходима. Хотя, конечно, можно сделать внутри функции do{...}while(0); и в случае косяков делать внутри нее break, а free в конце делать для всего (но чтобы не было косяков, инициализировать до malloc указатели NULL'ом). Вот только это не решит проблему с необходимостью делать free на выделяемую память, которую нужно вернуть из этой функции.

Поэтому таки goto удобней всего.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Eddy_Em

может не боязнь, а отсутствие явной необходимости в данном случае? код с goto просто больше получается в данном случае.

anonymous
()
Ответ на: комментарий от anonymous

Да я ж говорю: в конкретно этом случае действительно можно было бы упростить:

char *a = NULL, *b = NULL, *c = NULL;
...
do{
  if(!(a = prompt(texta))) break;
  if(!(b = prompt(textb))) break;
  if(!(c = prompt(textc))) break;
  ...
}while(0);
free(a); 
free(b);
free(c);

Вот, правда, дальнейшее упрощение (с массивами и циклами) приведет опять к необходимости goto.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

Несколько ошибочных меток только помогают запутать код и программиста.

  dest = NULL;
  name = NULL;

  free(work);
  return 0;
fail:
  if (dest)
    free(dest);
  if (name)
    free(name);
  return exitcode;
tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Несколько ошибочных меток только помогают запутать код и программиста.

Почему? Они находятся в одном месте, всё норм. А если бы эта была не память, пришлось бы создавать флаги work_failed, name_failed и т.д.?

Алсо, между второй и четвёртой строчками должен идти весь остальной код? Тогда

free(work);
return 0;

return 0; лишний, не? Кто будет остальное освобождать?

anonymous
()
Ответ на: комментарий от anonymous

По лишним сущностям.

Следующее сообщение может быть записано не менее чем через 30 секунд после предыдущего

Ну лол.

anonymous
()
Ответ на: комментарий от Eddy_Em
for(i = strlen(s); i > 0; i--,s++){
		c = *s;
		if(!isprint(c)){
			printf("non printable symbol\n");
			continue;
		}
		if(isalpha(c)){
			if(islower(c)) printf("lowercase");
			else printf("uppercase");
		printf(" letter");
		}else if(isdigit(c)) printf("digit");
		else if(ispunct(c)) printf("punctuation");
		else if(isspace(c)) printf("space");
		else if(isblank(c)) printf("blank");
		printf(" %c\n", c);
	}

Что же ты делаешь! Одумайся!

Solace ★★
()
Ответ на: комментарий от Solace

УМВР ☺

Про универсальность для работы в юникоде я выше писал. Можно сделать.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

Они находятся в одном месте

Нет. Три метки - это не «одно место».

Кто будет остальное освобождать?

Это я неправильно понял код - подумал, что в случае удачного завершения name и dest не освобождаются.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

А у меня вот такие удобные макросы есть:

#define ALLOC(type, var, size)  type * var = ((type *)my_alloc(size, sizeof(type)))
#define MALLOC(type, size) ((type *)my_alloc(size, sizeof(type)))
#define FREE(ptr)           do{free(ptr); ptr = NULL;}while(0)
Сама функция простая:
void *my_alloc(size_t N, size_t S){
    void *p = calloc(N, S);
    if(!p) ERR("malloc");
    return p;
}

Eddy_Em ☆☆☆☆☆
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.