Запретить пользователю передавать отрицательные числа функции, принимающей unsigned int

Итак, вот код:

int create_mask(unsigned b, unsigned e)
{
  unsigned int mask=1;

  if(b<e || b<0 || e<0)
  {
    printf("Wrong values, starting bit can't be smaller than ending.\n");
    printf("Both got to be >= 0.\n");
    exit(EXIT_FAILURE);
  }
  while(b>0)
  {
    printf("%u\n", b);
    mask<<=1;
    if(b>e)
      mask|=1;
    b--;
  }

  return ~mask; /* negates mask for later purpose that is clearing corresponding bits */
}

Функция создает маску для некоторых битовых операций, но должна принимать два беззнаковых целых числа b и e, оба неотрицательные. Вопрос в том, как предотвратить ввод отрицательных чисел пользователем? Когда функция вызывается с (-1,0), она запускает цикл и должна выйти с ошибкой.


person zubergu    schedule 02.09.2013    source источник
comment
Но числа без знака никогда не бывают отрицательными, так что это значит?   -  person harold    schedule 02.09.2013
comment
Во-первых, не позволяйте функции вызывать с (-1, 0). Введите строку, затем преобразуйте ее в целое число без знака, используя strtoul(). Он сообщит об ошибке, если число было отрицательным.   -  person    schedule 02.09.2013
comment
@harold Происходит неявное преобразование из подписанного в беззнаковое, оно интерпретируется по модулю 1 << width, где width - количество бит в unsigned int.   -  person    schedule 02.09.2013
comment
знаковое int -1, когда оно интерпретируется как беззнаковое int, будет равно максимальному значению. Значит, это не меньше e или меньше 0.   -  person Taemyr    schedule 02.09.2013
comment
@Taemyr Не обязательно, только если машина использует представление с дополнением до 2 для отрицательных чисел.   -  person    schedule 02.09.2013
comment
@ H2CO3, поэтому я оборачиваю свою функцию другой функцией, которая проверяет, является ли первый символ '-', и действует соответственно, верно?   -  person zubergu    schedule 02.09.2013
comment
@ H2CO3 да .. и то уже не отрицательно. Задача решена.   -  person harold    schedule 02.09.2013
comment
И это так. Когда вы передаете b = -1 функции, вы должны запускать условие при запуске и exit со значением EXIT_FAILURE. Но проблема в том, что входные значения определены как unsigned int, что означает, что b будет равно максимальному значению, доступному для типа unsigned int в вашей системе. Может быть, проблема в чем-то другом?   -  person mesmerizingr    schedule 02.09.2013
comment
@zubergu в основном да.   -  person    schedule 02.09.2013


Ответы (3)


Вы можете просто ввести строку, проверить, содержит ли она символ '-', и выдать ошибку, если она есть. В противном случае вы преобразуете его в целое число без знака и продолжите. (В любом случае чтение в виде строки с последующим преобразованием с strtoul() предпочтительнее, чем с использованием scanf(), особенно если вы не знаете обо всех причудах scanf().)

char buf[LINE_MAX];
fgets(buf, sizeof buf, stdin);

if (strchr(buf, '-') != NULL) {
    fprintf(stderr, "input must be non-negative!\n");
    exit(-1);
}

unsigned int n = strtoul(buf, NULL, 0);
person Community    schedule 02.09.2013
comment
sscanf(buf, "%lu", &n); также выполнит преобразование, но я думаю, что strtoul лучше :) - person boleto; 02.09.2013
comment
@boleto Это не совпадение, которое я использовал (и предлагал) использовать strtoul() вместо scanf(). - person ; 02.09.2013
comment
Если вы собираетесь перепроектировать интерфейс, гораздо разумнее указать int вместо строки. Тогда вы можете просто утвердить или выбросить, если значение равно ‹= 0 или‹ 0. - person shawn1874; 21.01.2014

Изменить:

вы можете взять ввод long int, а затем проверить, что ввод находится между 0 to ending range of unsigned int. Если затем назначить свою переменную, иначе возникнет исключение для пользователя, вы должны вводить только числа без знака в качестве ввода.

long int input;
unsigned int valid_input;

scanf("%ld",&input);
if((0<= input) && (input <= 4294967295))
valid_input= (unsigned int)input  ;
else
printf("Unvalid input\n");

Как сказал H2CO3. чтение ввода в строку и проверка первого литерала, если он не минус, тогда преобразование в целое число без знака было бы предпочтительнее, чем метод ниже. потому что половина целых чисел без знака не покрыта.

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

person Gangadhar    schedule 02.09.2013
comment
Но это сделает невозможным использование некоторых высоких значений без знака (которые будут переполняться до отрицательной половины диапазона int, когда происходит преобразование из unsigned в signed. И целочисленное переполнение со знаком в любом случае является неопределенным поведением, поэтому это не может быть правильным решение.) - person ; 02.09.2013
comment
Собственно, в этом и проблема. - person zubergu; 02.09.2013
comment
@zubergu Опять же, смотри мой первый комментарий по твоему вопросу. - person ; 02.09.2013
comment
@zubergu Однако эти числа по определению равны отрицательным числам. - Итак, когда вы просите исключить отрицательные числа, вы в некотором смысле также просите исключить высокие беззнаковые целые числа. - person Taemyr; 02.09.2013
comment
@ H2CO3 был бы другой выбор. Проверка статуса возврата scanf. при сканировании, если вы не получили целое число без знака, вы можете получить ноль. как возвращаемое значение. - person Gangadhar; 02.09.2013
comment
@Gangadhar Ну, scanf() не выручит, если встретит отрицательное число. Он просканирует целое число со знаком, затем преобразует его в беззнаковое и сообщит об успешном выполнении. - person ; 02.09.2013

Взгляните на это: Неявное преобразование функции остановки

Мне удалось адаптировать его к вашей проблеме, и он не позволяет программе связываться. Информация в приведенном выше потоке кажется частично неверной, потому что пример скомпилирован нормально. Не удалось связать, потому что не была определена специализация шаблона. На самом деле я немного удивлен, что следующее сработало для int vs unsigned int.

template <class T>
void foo(const T& t);

template <>
void foo<unsigned int>(const unsigned int& t)
{

}

int main(){
  foo((unsigned int) 9); // will compile and link
  unsigned int value(5);
  foo(value);// will compile and link
  foo(9.0); // will not link
  foo(-9); // will not link
  return 0;
}

Я думаю, что вы, возможно, слишком долго об этом думаете. Неужели это проблема? Было бы лучше для начала сделать ваш тип идентификатора int? Есть ли минимальный / максимальный идентификатор, который позволяет избежать больших чисел, которые могут быть ошибочно приняты за комплимент для двоек? Это похоже на досадную проблему с языком, поскольку он не предоставляет простого способа остановить неявное приведение.

Я протестировал этот пример с Visual Studio 2010. Кроме того, у меня не было времени написать тестовый класс, поэтому, если он вас интересует, вам придется адаптировать пример к классу foo, чтобы увидеть, работает ли он с конструктором класс, или, если есть другой способ использовать шаблоны для этого. Основываясь на других ответах и ​​моем опыте, я не думаю, что вы найдете простой способ делать то, что хотите.

person shawn1874    schedule 20.01.2014
comment
Спасибо за ответ, но вопрос помечен тегом C, строго C. - person zubergu; 21.01.2014