Этот код неверен:
int key = (int) argv[1];
argv
- это массив string
, который в CS50 - не более чем запутанный указатель char *
.
За 5.1.2.2.1 Запуск программы стандарта C :
Функция, вызываемая при запуске программы, называется основной. Реализация не объявляет прототип для этой функции. Он должен быть определен с типом возврата int и без параметров:
int main(void) { /* ... */ }
или с двумя параметрами (именуемыми здесь как argc и argv, хотя могут использоваться любые имена, поскольку они являются локальными для функции, в которой они объявлены):
int main(int argc, char *argv[]) { /* ... */ }
или эквивалентный; ...
Итак, argv[1]
- это значение указателя char *
, которое вы затем присваиваете значению int
. Это берет адрес некоторой памяти (скажем, значение в argv[1]
равно 0xFF0403220020480C
) и пытается вставить его в вероятные 4 байта переменной int
key
(которой в этом случае будет присвоено усеченное значение 0x0020480C
.)
Это не то, что вы пытаетесь сделать.
(IMO, ваша проблема здесь является прекрасным примером того, почему обфускация CS50 char *
с типом string
является чрезвычайно плохой идеей. Вы просто не можете понять C без понимания указателей и NUL
-определенного char
строк доступны через указатель char *
, и запутывание, которое делает string
делает это сложнее.)
Если вы хотите преобразовать строку в числовое значение , вы, вероятно, захотите что-то вроде strtol()
( никогда не используйте atoi()
, так как в нем нет проверки ошибок и его использование может вызывать неопределенное поведение ):
char firstCharNotConverted;
// set errno to zero as strtol()
errno = 0;
long key = strtol( argv[ 1 ], &firstCharNotConverted, 0 );
// if errno is now non-zero, the call to strtol() failed (per Linux man page)
// need to examine key and the contents of firstCharNotConverted
// to figure out why
if ( errno != 0 )
{
...
}
Правильные заголовки опущены в качестве упражнения для тех, кто пытается использовать этот код; -)
Обратите внимание, что я использовал long
для key
, поскольку вы не можете выполнить полную и правильную проверку ошибок на strtol()
, если приведете возвращаемое значение к int
.
Проверка ошибок strtol()
может быть несколько сложной, поскольку возвращаемое значение (и присвоенное key
в вышеприведенном коде) может быть любым значением, и нет возможных значений, которые не являются допустимыми long
значениями, которые strtol()
может возвращаться для обозначения ошибки, поэтому для правильной проверки ошибок необходимо проверить значения как errno
, так и firstCharNotConverted
, чтобы правильно определить, произошла ли ошибка. Справочная страница Linux сообщает:
Поскольку strtol () может законно возвращать 0, LONG_MAX или LONG_MIN
(LLONG_MAX или LLONG_MIN для strtoll ()) в случае успеха и неудачи,
вызывающая программа должна установить errno на 0 до вызова, а затем
определить, произошла ли ошибка, проверив, имеет ли errno
ненулевое значение после звонка.
После этого вызова strtol()
вам нужно проверить, является ли key
равным LONG_MIN
или LONG_MAX
с errno
равным ERANGE
для недостаточного или переполнения, или key
равно 0
вам нужно проверить содержимое firstCharNotConverted
, чтобы определить причину сбоя преобразования. Обратите внимание, что если key
равно нулю и firstCharNotConverted
не равно argv[ 1 ]
, то входная строка была правильно преобразована из нуля.
Ваша реализация шифра Ceaser также неверна:
for (int i = 0, n = strlen(ptext); i < n; i++)
{
printf("%c", (( ptext[i] + key ) % 26);
}
printf("\n");
будет просто выводить символы со значениями от 0
до 25
- которые не являются буквами в наборе символов ASCII.
Здесь уже размещены многочисленные вопросы о шифрах Ceaser, поэтому я не собираюсь писать код. См. Код шифра Цезаря для одного примера вопроса.