02.10.2014, 12:49 | |
Ответы с готовыми решениями:
72
Распространенные ошибки безопасность и распространенные ошибки Распространенные ошибки SEO и ASP.NET 2.0 Самые распространенные строки |
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
|||||||||||
02.10.2014, 12:55 [ТС] | 2 | ||||||||||
1. Ошибки при использовании функции scanf().
Рассмотрим ошибки.
scanf_s. Можете послушаться и заменить, а можете отключить это предупреждение добавив #pragma warning(disable:4996) или изменить уровень ошибок в свойствах проекта (VS 2008 2010): Проект->Свойства->Свойства конфигурации->С/С++->Общие->Уровень предупреждений-> Уровень 2(W2). 2. Передано значение переменной, а не адрес. Если ранее переменной значение не присваивалось, то компилятор может выдать предупреждение о том, что используется неинициализированная переменная. 3. Вводится целое число, но в форматной строке указано иное (lf - используется double). 4. Лишние знаки форматирования в командной строке (после ввода числа будет ожидаться еще один Enter). 5. Написано правильно. Однако, тут есть нюанс (который отсутствует в iostrem). Если локаль языка установлена русская ( setlocale(LC_ALL,"Rus") ; ), то дробная часть отделяется от целой запятой, а не точкой. 6. По формату %s водится не строка целиком, а только очередное слово до пробельного символа. Для ввода всей строки используйте gets(buf); 7. scanf_s требует передачи размера массива в дополнительном параметре:
2
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
|
02.10.2014, 12:57 [ТС] | 3 |
2. При работе с fgetc чтение файла обрывается при достижении буквы 'я'
(Автор Evg) C FILE *fp; char c; fp = fopen ("a.txt", "r"); while ((c = fgetc(fp)) != EOF) { ... } Проблема кроется не в конкретной букве 'я', а в байте со значением 255. Потому что именно это значение, рассмотренное, как char (0xff) и приведённое к типу int (0xffffffff) совпадёт со значением EOF (которое равно -1). При работе под windows с файлами в кодировке win-1251 код номер 255 имеет буква 'я'. В других кодировках этому коду может соответствовать другая буква. Вот ссылки с пояснениями: "я" это EOF?! Существует ли EOF или это миф? 10 вопросов по С. part1
0
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
|
02.10.2014, 17:43 [ТС] | 4 |
3. Использование типов char, short и float при работе с va_arg
(Автор Evg) C #include <stdio.h> #include <stdarg.h> void func (int x, ...) { va_list va; char c1 = 0x11; char c2 = 0x22; short s1 = 0x3333; short s2 = 0x4444; float f1 = 1.25; float f2 = 5.75; va_start (va, x); c1 = va_arg (va, char); c2 = va_arg (va, char); s1 = va_arg (va, short); s2 = va_arg (va, short); f1 = va_arg (va, float); f2 = va_arg (va, float); printf ("c1=%hhx\n", c1); printf ("c2=%hhx\n", c2); printf ("s1=%hx\n", s1); printf ("s2=%hx\n", s2); printf ("f1=%f\n", f1); printf ("f2=%f\n", f2); va_end (va); } int main (void) { char c1 = 0x11; char c2 = 0x22; short s1 = 0x3333; short s2 = 0x4444; float f1 = 1.25; float f2 = 5.75; func (0, c1, c2, s1, s2, f1, f2); return 0; } Работа с va_arg Правильное написание данного примера: C #include <stdio.h> #include <stdarg.h> void func (int x, ...) { va_list va; char c1 = 0x11; char c2 = 0x22; short s1 = 0x3333; short s2 = 0x4444; float f1 = 1.25; float f2 = 5.75; va_start (va, x); c1 = (char) va_arg (va, int); c2 = (char) va_arg (va, int); s1 = (short) va_arg (va, int); s2 = (short) va_arg (va, int); f1 = (float) va_arg (va, double); f2 = (float) va_arg (va, double); printf ("c1=%hhx\n", c1); printf ("c2=%hhx\n", c2); printf ("s1=%hx\n", s1); printf ("s2=%hx\n", s2); printf ("f1=%f\n", f1); printf ("f2=%f\n", f2); va_end (va); } int main (void) { char c1 = 0x11; char c2 = 0x22; short s1 = 0x3333; short s2 = 0x4444; float f1 = 1.25; float f2 = 5.75; func (0, c1, c2, s1, s2, f1, f2); return 0; }
0
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
|||||||||||
02.10.2014, 17:49 [ТС] | 5 | ||||||||||
4. Определение размера массива, переданного в качестве аргумента функции.
(Автор BumerangSP):
1
|
02.10.2014, 21:33 | 6 |
7. Возврат из функции локальной строки
Что-то похожее есть тут, но для языка Си мы имеем отдельный часто встречаемый случай при работе со строками C char* gen_string (void) { char str[100]; strcpy (str, "hello world"); return str; } void func (void) { p = gen_string (); printf ("%s\n", p); } Как это дело лечить? 1. Самый честный способ - снаружи в функцию передавать буфер, куда копировать строку, и размер. По возможности при всех операциях с буфером контролировать возможный выход за границу буфера, особенно когда работаем с заранее неизвестными размерами C char* gen_string (char *str, int len) { strncpy (str, "hello world", len); return str; } void func (void) { char str[100], *p; p = gen_string (str, sizeof (str)); printf ("%s\n", p); } C char* gen_string (void) { static char str[100]; strcpy (str, "hello world"); return str; } void func (void) { p = gen_string (); printf ("%s\n", p); } C char* gen_string (void) { char *str; str = malloc (100); strcpy (str, "hello world"); return str; } void func (void) { p = gen_string (); printf ("%s\n", p); free (p); }
2
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
||||||||||||||||
03.10.2014, 20:52 [ТС] | 7 | |||||||||||||||
5. "Неожиданное" закрытие окна под Windows.
Когда консольное приложение запускается непосредственно из среды программирования, то после выполнения последнего оператора программы ( return 0; ) окно закрывается. Вставляйте оператор, ожидающий ввода символа с клавиатуры перед return:
(на *NIX системах она не используется):
1
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
||||||||||||||||
04.10.2014, 10:24 [ТС] | 8 | |||||||||||||||
6. Оставление символа '\n' в потоке ввода:
2
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
||||||
04.10.2014, 10:30 [ТС] | 9 | |||||
8. Выделение памяти без дальнейшего освобождения
При выделении памяти обязательно освобождайте её перед выходом из функции, во избежание утечки памяти:
0
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
|||||||||||
04.10.2014, 10:57 [ТС] | 10 | ||||||||||
9. Использование неинциализированной переменной
В Visual Studio это делается через команду меню Проект->Свойства->Свойства конфигурации->С/С++->Общие-> Уровень предупреждений->Уровень 4 (/W4).
0
|
12.10.2014, 14:06 | 11 | |||||||||||||||
10. "Неожиданное" целочисленное деление в арифметических выражениях.
Следует помнить, что результат деления целого на целое - тоже целое:
Все константы, которые явно не должны быть целыми, делайте действительными (записывайте с точкой, первое время можете даже после точки ставить нуль 1.0 ):
Пояснения, почему так происходит. Во всех двухаргументных арифметических операциях (кроме операций сдвига) оба аргумента должны иметь один и тот же тип. Если аргументы имеют разный тип, то компилятор строит неявное приведение типа для одного их аргументов, чтобы в конечном итоге выполнить арифметическую операцию над значениями одного и того же типа. Чтобы выяснить, который из двух аргументов нужно приводить к типу другого, в стандарте языка существуют правила, которые в отдельных тонких моментах даже различаются для Си и Си++. В общем виде они довольно сложные для восприятия начинающими, а потому сделаю лишь краткое пояснение. В случае различных типов один из типов является более "широким", чем другое. А потому в двухаргументной операции всегда аргумент более "узкого" типа приводится к более "широкому" типу. Проверка идёт примерно в таком порядке:
В итоге код C float f; int i; unsigned u; long long ll; x + f; u + f; i + u; ll + u; C int i; unsigned u; float f; ((float)x) + f; /* тип результата - float */ ((float)u) + f; /* тип результата - float */ ((unsigned)i) + u; /* тип результата - unsigned */ ll + ((long long)u); /* тип результата - long long */ C int a, b; float f; f = a / b; C int a, b, tmp; float f; /* сначала выполняем целочисленное деление * (при котором отбрасывается остаток) */ tmp = a / b; /* и только потом полученный результат (с утерянной * дробной частью) преобразуется в вещественный тип */ f = (float) tmp; C int a, b; float f; f = ((float)a) / b; C int a, b; float f; f = a / ((float)b); C int a, b; float f; f = ((float)a) / ((float)b);
9
|
Вездепух
12771 / 6653 / 1791
Регистрация: 18.10.2014
Сообщений: 16,821
|
|||||||||||
24.10.2014, 11:30 | 12 | ||||||||||
11.Использование функции strncpy как функции "безопасного" копирования строк
Функция strncpy появилась в стандартной библиотеке языка С для решения одной узкоспециализированной задачи - перевода классических нуль-терминированных строк в так называемые строки фиксированной ширины. Строки фиксированной ширины - это особый способ представления строк, который использовался в файловой системе Unix во времена, когда язык С был еще очень молодым. В формате фиксированной ширины строка хранится в соответствии со следующими правилами: 1. Все строки хранятся в буфере заранее заданной фиксированной ширины N 2. Если строка имеет длину меньше N, то все лишние позиции в строке заполняются нулевыми символами 3. Если строка имеет длину ровно N, то она полностью заполняет буфер и не заканчивается нулевым символом В частности, файловая система Unix System V использовала такие строки длины 14 для представления имен файлов. Такой формат представления строк был выбран с одной-единственной целью - сэкономить один байт при представлении имен максимальной длины. При использовании классических нуль-терминированных строк имя длины 14 потребовало бы 15 байт, в то время как в соответствии с соглашениями формата фиксированной ширины такая строка умещается в 14 байт. Для того, чтобы сравнивать строки фиксированной ширины между собой применялась функция memcmp на всю фиксированную ширину буфера. Требование заполнения всего неиспользуемого хвоста строки нулями как раз и обеспечивало работоспособность и корректность такого сравнения. Так вот, функция strncpy предназначалась именно для инициализации таких буферов. Например, приведенные ниже вызовы функции strncpy формируют правильно проинициализированные строки фиксированной ширины 14 в своих целевых буферах
Однако времена поменялись, необходимость экономить один байт из пятнадцати практически отпала и функция strncpy стала почти бесполезной... Но тут случилось страшное. Кто-то малознакомый и с конкретной спецификацией функции strncpy и с изначальным предназначением вдруг пришел к выводу, что эта функция предназначена (и пригодна) для "безопасного" копирования обыкновенных нуль-терминированых строк. Такого человека можно понять, ибо функция strncpy названа довольно неудачно: ее название как-будто намекает на то, что эта функция предназначена именно для ограниченного по длине копирования строк. А документация... Кто ж читает документацию, когда и из названия функции все понятно? Разумеется, пользователи функции быстро заметили, что она не выполняет нуль-терминацию целевой строки, когда при копировании был достигнут предел длины целевого буфера. В результате родилась следующая неприглядная идиома
Понятно, что strncpy в такой роли решает поставленную задачу, но никогда не работает хорошо. Если исходная строка короче целевого буфера, то strncpy будет выполнят дурную и ненужную работу: заполнять всю хвостовую часть буфера нулями. Если же строка длиннее целевого буфера, то strncpy наоборот забудет выполнить нужную и важную работу: выполнить нуль-терминацию целевой строки. Такое применение strncpy чем-то сродни забиванию шурупов молотком: понятно, что неправильно, но вроде держится. Другими словами, если функию strncpy еще можно как-то заставить работать для безопасного копирования строк - при помощи ручной нуль-терминации целевой строки (как в примере выше), то использование strncpy без такой нуль-терминации всегда является ошибкой. Однако в реальном коде часто можно встретить именно такое использование strncpy с полным игнорированием необходимости обеспечить нуль-терминацию. К сожалению, стандартная библиотека языка С не предоставляет хорошей функции, выполняющей именно ограниченное по длине копирование обычных С-строк (это еще одна причина популярности strncpy в этой роли). Поэтому реализации стандартной библиотеки в системах Unix традиционно предоставляют свою де-факто стандартную функцию - strlcpy - предназначенную именно для этой цели. strlcpy - это именно функция безопасного (т.е. ограниченного по длине) копирования обыкновенный нуль-терминированных строк. Если вы работаете на платформе, не предоставляющей вам функции strlcpy, то лучше написать эту функцию самостоятельно, чем замусоривать ваш код некачественным применением strncpy. Но, если уж вам все таки хочется пользоваться strncpy, то помните - эта функция не гарантирует того, что ваша строка будет правильно нуль-терминирована. Не забываете обеспечить корректную нуль-терминацию самостоятельно.
13
|
24.10.2014, 16:38 | 13 |
Причина вполне стандартная, как и для многих других ошибок - неправильно рассказывали в школе/институте из-за того, что преподаватель сам плохо разбирается в вопросе
Добавлено через 1 час 33 минуты Тут есть ещё дополнительная засада. Выдержка из linux'ового man'а: C #include <stdio.h> #include <errno.h> int main (void) { int x; sscanf ("99999999999999999999999", "%d", &x); printf ("x=%d, errno=%d\n", x, errno); return 0; } Код
$ gcc t.c $ ./a.out x=2147483647, errno=34 Код
$ gcc t.c $ ./a.out x=-159383553, errno=0
0
|
Вездепух
12771 / 6653 / 1791
Регистрация: 18.10.2014
Сообщений: 16,821
|
|
24.10.2014, 18:25 | 14 |
Да, конечно, если заглянуть в реализацию стандартной библиотеки, то обнаружится, что 'atoi' "почти всегда" просто напросто вызывает 'strtol'. Таким образом хорошее поведение 'strtol' тут же наследуется и 'atoi'. Я не удивлюсь, если увижу аналогично реализованную 'sscanf'.
Но спецификация языка продолжает настаивать на том, что 'atoi' ведет себя (или может вести себя) плохо: Аналогичное утверждение сделано о функциях группы 'scanf'. Если заглянуть в такой документ, как Rationale for International Standard-Programming Languages-C, то там открыто пишут, что уже в времена первого стандарта языка С (C89/90) опасность функция группы 'ato...' была всем понятна. Именно по этой причине, в частности, первое дополнение к стандарту, вышедшее в в 1995 году и добавившее, кроме прочего, в стандартную библиотеку функции работы с wide character строками, не ввела "широких" версий для функций этой группы. Там же сказано, что функции группы 'ato...' считаются "поглощенными" (subsumed) функциями группы 'strto...', но не были исключены из языка полностью по причине их широкого использования в старом коде (см. 7.20.1.1/25). Другими словами, мне не совсем понятно, почему комитет по стандартизации не хочет "спасти" функции группы 'ato...'. Ведь на первый взгляд не составит никакого труда просто уточнить их спецификацию и сделать их безопасными аналогами функций группы 'strto...'. Но этого не происходит. Да, с одной стороны, как сказано в rationale, эти функции считаются "ненужными". Но с другой, группа продолжает поддерживаться: 'atoll' была добавлена относительно "недавно".
0
|
24.10.2014, 18:50 | 15 |
Корень подобных проблем ты уже озвучивал выше - стандарты никто не читает, все пытаются делать выводы на основании того, что на их собственной системе программирования всё работает. Я тут просто наглядно продемонстрировал к чему может привести такой неправильный подход. Для начинающих само понятие другой платформы уже тяжело к восприятию, так что может быть тут уже будет перебором засовывать всё это в ошибки для начинающих, т.к. это уже по сути следующий уровень
0
|
Вездепух
12771 / 6653 / 1791
Регистрация: 18.10.2014
Сообщений: 16,821
|
||||||
24.10.2014, 22:57 | 16 | |||||
12.Использование функций atoi/atof или sscanf для перевода строки в число
Функции atoi/atof (и другие функции группы ato...), несмотря на свою исключительную популярность, практически не пригодны для надёжного решения задачи преобразования десятичной записи числа в его внутреннее представление. Эти функции страдают от двух серьёзных проблем: они не формируют осмысленного отчёта об ошибочных ситуациях и они вызывают неопределённое поведение при переполнении. Во-первых, функция atoi, как известно, возвращает нулевое значение, если входная строка не содержит корректного десятичного представления. Понятно, что получив нулевой результат, вызывающий код не может немедленно ответить на вопрос о том, произошла ли какая-то ошибка или входная строка действительно содержала ноль. В практическом коде, основанном на использовании atoi, зачастую можно встретить попытки дополнительного анализа входной строки в ситуациях, когда atoi вернула ноль. Т.е. код пытается выполнить пост-разбор входной строки чтобы выяснить, содержится ли там ноль или какая-то абракадабра. Несмотря на то, что для функций целочисленного преобразования такой разбор выполнить несложно, он зачастую бывает реализован с ошибками (не говоря уже о более сложной с точки зрения входного формата функции atof). В любом случае, решение это в корне порочно, ибо вся прелесть использования готовых стандартных функций преобразования строк в числа во многом и заключается в том, что разбор не надо делать вручную. В других вариантах кода, использующего atoi, можно встретить попытки пред-разбора входной строки с целью выяснения её корректности. Такой код пытается убедиться, что со входной строкой "все хорошо", ещё до того как он передает ее в atoi. Понятно, что такие варианты обладают все тем же вышеописанными недостатками и ещё более склонны к ошибкам. Но это ещё не всё. Во-вторых, функция atoi приводит к неопределённому поведению в случае возникновения переполнения в процессе преобразования. Это существенно более серьёзная проблема, у которой, к сожалению, нет приемлемого решения. Например, вот такой код вызывает неопределённое поведение
Ситуация с функцией sscanf, которая тоже может использоваться для преобразования строк в числа, лишь немногим лучше. Через свое возвращаемое значение эта функция умеет сообщать в вызывающий код об неправильном формате входных данных. Но, к сожалению, эта функция тоже приводит к неопределённому поведению в случае возникновения переполнения в процессе преобразования. Так как же правильно и безопасно преобразовать строковую запись числа во внутреннее представление? Для этой цели в стандартной библиотеке языка С с начала стандартизованных времен присутствуют функции группы strto...: strtol, strtoul, strtod и т.д. Эти функции немножко сложнее в использовании, чем функции группы ato..., но зато они умеют сообщать в вызывающий код о неправильном формате входных данных и, что особенно важно, устойчивы к переполнению. В случае возникновения переполнения функции группы strto... не вызывают неопределённого поведения, а сообщают об этом через стандартную переменную errno. Ещё в девяностых годах прошлого столетия функции группы strto... были признаны замещающими функции группы ato.... Последние были оставлены в стандартной библиотеке в первую очередь для совместимости со старым кодом. Другими словами, функциям группы ato... не место в серьёзном коде. Эти функции применимы лишь в одноразовом экспериментальном коде, написанном "на салфетке". В серьёзном коде для выполнения рассматриваемого преобразования следует использовать функции группы strto....
0
|
Вездепух
12771 / 6653 / 1791
Регистрация: 18.10.2014
Сообщений: 16,821
|
|
28.10.2014, 17:11 | 18 |
0
|
Модератор
13695 / 10899 / 6471
Регистрация: 18.12.2011
Сообщений: 29,092
|
||||||
28.10.2014, 18:41 [ТС] | 19 | |||||
0
|
28.10.2014, 19:01 | 20 | |||||
"mass" семантически есть &(mass[0]), т.е. указатель на char
"&mass" семантически хз как написать, но по смыслу это есть указатель на конструкцию, которая есть массив из N char'ов. Т.е. тип указателя другой, но значение то же самое
Код
$ gcc t.c -Wall -c t.c: In function 'foo': t.c:8: warning: assignment from incompatible pointer type
0
|
28.10.2014, 19:01 | |
28.10.2014, 19:01 | |
Помогаю со студенческими работами здесь
20
Самые распространённые фамилии Вывести самые распространенные женские и мужские имена Вывести самые распространенные мужские и женские имена Вирус блокирует выход на сайт вк и другие распространенные сайты Найти и вывести самые распространенные женские те мужские имена Вывести самые распространенные мужское и женское имена среди студентов Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |