Форум программистов, компьютерный форум, киберфорум
C для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.97/796: Рейтинг темы: голосов - 796, средняя оценка - 4.97
Модератор
Эксперт С++
 Аватар для zss
13789 / 10981 / 6492
Регистрация: 18.12.2011
Сообщений: 29,282

Распространенные ошибки

02.10.2014, 12:49. Показов 161482. Ответов 72
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Оглавление

Ошибки этапа компиляции
(В процессе компиляции выдается либо сообщение об ошибке, либо предупреждение)
- Попытка модифицировать константу через указатель
- Лишняя точка с запятой
- Отсутствие возврата значения из функции
- Приведение типа в стиле С++
- Использование = вместо ==


Ошибки этапа исполнения программы
(Во время исполнения программа прерывается с сообщением об ошибке)
- Возврат из функции локальной строки
- Выделение памяти без дальнейшего освобождения
- Использование неинциализированной переменной
- Использование функции strncpy как функции "безопасного" копирования строк
- Использование функций atoi/atof или sscanf для перевода строки в число
- Возврат ссылки/указателя на локальную переменную
- Выход за пределы массива
- Сравнение символьных массивов
- Использование чисел, записанных в других системах счисления.
- Работа с локальной копией объекта, вместо работы с самим объектом


Неправильное поведение программы на этапе исполнения
(Программа работает, но не так, как запланировано)
- Использование типов char, short и float при работе с va_arg
- Определение размера массива, переданного в качестве аргумента функции.
- "Неожиданное" закрытие окна.
- "Неожиданное" целочисленное деление в арифметических выражениях.
- Ошибки в логических выражениях.
- Использование символа цифры вместо числа
- Лишняя точка с запятой
- switch без break
- Сравнение вещественных чисел при вычислениях
- Проверки на принадлежность значения определенному интервалу.
- Неверный аргумент тригонометрических функций.
- Сравнение знаковой переменной с беззнаковой.
- Использование запятой для отделения дробной части
- Забытое выделение тела цикла for, while и операторов if else
- Локальная переменная экранирует переменную с таким же именем из вышестоящей области видимости
- Неправильное использование memset
- Сравнение char массивов

Алгоритмические ошибки
(Неправильно составлен алгоритм программы)
- Двойная перестановка строк или элементов массива.

Ошибки ввода-вывода
- Ошибки при использовании функции scanf()!
- При работе с fgetc под Windows чтение файла обрывается при достижении буквы 'я'
- Оставление символа '\n' в потоке ввода
- scanf() - ввод текстовых строк

Ошибки, связанные с отклонением от стандарта языка
- Неверный тип функции main()
3
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
02.10.2014, 12:49
Ответы с готовыми решениями:

Распространенные ошибки
Скачать pdf: Оглавление Ошибки этапа компиляции Программа не компилируется или компилируется с предупреждениями. Попытка...

безопасность и распространенные ошибки
Тут наткнулся на очень интересные тексты: http://werad.narod.ru/articles/programm6.html http://werad.narod.ru/articles/programm9.html...

Распространенные ошибки SEO и ASP.NET 2.0
Здравствуйте, существуют несколько СЕО проблем при использовании ASP.NET, ниже я опишу эти проблемы, нужно найти простое и эффективное...

72
6 / 6 / 1
Регистрация: 28.09.2018
Сообщений: 396
24.02.2019, 21:25
Студворк — интернет-сервис помощи студентам
TheCalligrapher,

Цитата Сообщение от TheCalligrapher Посмотреть сообщение
12. scanf() - ввод текстовых строк
Сообщение от zss
C++Выделить код
1
2
3
char mass[N];
scanf("%s",mass); // так правильно
scanf("%s",&mass); // почему-то тоже срабатывает
А в чем проблема?
Возможно, кому-то понравится:

https://www.geeksforgeeks.org/... nt-array5/
0
6 / 6 / 1
Регистрация: 28.09.2018
Сообщений: 396
11.03.2019, 06:09
К слову о генерации случайных чисел, вот такой код:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <time.h>
 
 
int main (void) {
    
    int a, sum = 0, tmp; char str[50];
    srand(time(NULL));
    
    a = rand()%( 32760 - 100 ) + 100;
    
        do  {
            printf("%d ", a /= 10);
        } while ( a / 10 );
    
    
    getch();
    return 0;
        
    
}
даёт выходные данные:

Code
1
2478 247 24 2
Code
1
2501 250 25 2
-- хах, вроде исправилось)

но если компилировать + запускать ( как я привык делать; клавишу, знаете ли запомнил ),

не выходя из IDE, то

Code
1
2514
Code
1
2516
Code
1
2518
Хотя я понял: оно же берёт системное время --> скорее всего, связано с тем, что я некоторое время возился тут, набирая что-то.

Всё это я к тому, что алгоритм не самый хороший. Возможно, надо делать, как stake-k26, деля на два, но непонятно, что и почему это даёт.

Спасибо.
0
Evg
Эксперт CАвтор FAQ
 Аватар для Evg
21285 / 8310 / 637
Регистрация: 30.03.2009
Сообщений: 22,671
Записей в блоге: 30
10.09.2019, 21:38
В продолжение Вычитание беззнаковых чисел и Ошибка переполнения малоразрядных беззнаковых переменных

C
#include <stdio.h>
 
int main (void)
{
  unsigned char a, b, c;
  a = 0;
  b = 254;
 
  /* Пример N1 */
  if ((a - b) > 5u)
    printf ("1: yes\n");
  else
    printf ("1: no\n");
 
  /* Пример N2 */
  c = a - b;
  if (c > 5u)
    printf ("2: yes\n");
  else
    printf ("2: no\n");
 
  return 0;
}
Code
$ gcc t.c
 
$ ./a.out
1: yes
2: no
С виду оба примера выглядят одинаково. Разница между ними только в том, что в примере N2 результат вычитания записывается в промежуточную переменную. Однако работают они по разному

Виною всему integer promotion. По правилам языка Си при построении арифметических операций для аргументов короткого целочисленного типа (char, unsigned char, short, unsigned short) выполняется неявное преобразование к типу int. В итоге пример N1 выглядит как

C
  if ( (unsigned)((int)a - (int)b) > 5u )
а пример N2, если выкинуть из него промежуточную переменную "c", добавив при этом truncation (операцию приведения к unsigned char, как это имеет место быть при записи в переменную "c"), выглядит как

C
  if ( (unsigned)(unsigned char)((int)a - (int)b) > 5u )
отличие между ними в том, что в примере N2 появляется дополнительный truncate до usigned char'а. Значение выражения "(int)a - (int)b" в обоих случаях будет равно -254 (0xffffff02). В первом случае при беззнаковом сравнении это число окажется больше, чем 5. В вот дополнительный truncate во втором случае приведёт к тому, что это значение станет равным 0x02, и уже окажется меньше, чем 5
4
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
12.09.2019, 08:51
Evg, спс. Я "так" ни умею .. но именно так и надо для начинающих.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13212 / 6845 / 1825
Регистрация: 18.10.2014
Сообщений: 17,323
29.07.2020, 08:14
Ошибочное убеждение, что изменение значений переменных в С имеет магическую "обратную силу"

Это ошибка встречается в разных проявлениях. Наиболее часто она выглядит примерно так

C
1
2
3
4
5
6
int n;        /* Неинициализированная переменная */
int array[n]; /* Определение массива (VLA) */
...
n = 10;
/* Здесь автор кода ожидает, что теперь`array` -
   это массив размера 10 */
или

C
1
2
3
4
5
6
int n = 3;    /* Инициализированная переменная */
int array[n]; /* Определение массива (VLA) размера 3 */
...
n = 10;
/* Здесь автор кода ожидает, что теперь`array` -
   это массив размера 10 */
после чего автор кода начинает работать с массивом array так, как будто он имеет размер 10, то есть как будто этот массив каким-то магическим образом узнал о том, что переменная n получила новое значение, и автоматически подстроил свой размер.

В реальности же ничего подобного в С не происходит. С - процедурный, пошаговый язык. Размер массива array в вышеприведенных примерах задается (и фиксируется) в тот момент, когда процесс выполнения программы проходит по его определению (и в первом варианте поведение вообще не определено, ибо переменная n не инициализирована). После этого никакие изменения значения n уже не могут повлиять на размер массива.

Эта же ошибка встречается в варианте с динамическим выделением памяти.

---

Другие, более редкие вариации, того же заблуждения встречаются в виде

C
1
2
3
4
5
6
int a = 5;
int b = a + 5; /* b = 10 */
...
a = 10;
/* Здесь автор кода ожидает, что значение `b` 
   само по себе магически изменилось на 15 */
и даже однажды встретившийся здесь экзотический вариант

C
1
2
3
4
int a = 10, b = 15, x;
b = a + x;
/* Здесь автор кода ожидает, что язык С автоматически 
   "решит уравнение" и значение `x` станет равно 5 */
Резюме:

1. Программа на языке С - это не электронная таблица вроде Excel. Изменения переменных в С программах не имеют неявной [обратной] силы. Если какое-то изменение должно влечь за собой череду связанных изменений, то вам придется сделать/инициировать их все явно, вручную, самостоятельно.

2. В языке С нет массивов изменяющегося на лету размера. Изменить размер массива в С можно только путем уничтожения старого массива и создания нового. Опять же, делать это придется явно, вручную, самостоятельно.
4
из племени тумба-юбма
 Аватар для мама Стифлера
2523 / 1819 / 419
Регистрация: 29.11.2015
Сообщений: 8,857
Записей в блоге: 15
29.07.2020, 08:54
Два последних варианта мне точно не встречались, иначе бы вынесло мозг ))
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13212 / 6845 / 1825
Регистрация: 18.10.2014
Сообщений: 17,323
29.07.2020, 10:37
Наивная замена "опасных" функций на "безопасные"

Если компилятор Microsoft Visual Studio советует вам заменить вызовы функции scanf на вызовы функции scanf_s и вы решили последовать его совету, то это совсем не означает, что для этого достаточно просто огульно исправить имена функций в вызовах со scanf на scanf_s.

Функция scanf_s - это другая функция, обладающая несколько другим интерфейсом. Поэтому при переходе с использования scanf на использование scanf_s в общем случае придется также внести исправления в список аргументов в вызове функции.

А именно: три спецификатора формата - %s, и %[] - в функции scanf_s ведут себя не так, как ведут себя аналогичные спецификаторы в функции scanf. В функции scanf_s эти спецификаторы формата требуют двух аргументов, а не одного. Первый аргумент - это, как и в scanf, указатель на переменную-приемник (на буфер-приемник). А вот непосредственно вслед за ним должен следовать еще один аргумент - значение типа unsigned, описывающее размер этого приемника.

Все остальные спецификаторы формата scanf_s ведут себя так же как и в scanf, т.е. не требуют никаких дополнительных аргументов.

Например, если вы хотите перейти на использование функции scanf_s в следующем искусственном примере

C
1
2
3
4
5
int a, b;
char s[100];
char c;
 
scanf("%d%s%d%c", &a, s, &b, &c);
то, по причинам указанным выше, следующий вариант будет является некорректным

C
1
2
scanf_s("%d%s%d%c", &a, s, &b, &c);
/* Неправильный набор аргументов для данного формата! */
Правильно же

C
1
2
3
scanf_s("%d%s%d%c", &a, s, 100u, &b, &c, 1u);
// или 
scanf_s("%d%s%d%c", &a, s, (unsigned) sizeof s, &b, &c, 1u);
В случае, когда формат в вызове scanf не содержит спецификаторов %s, или %[], достаточно просто изменить имя функции в вызове на scanf_s. Но как только в формате появляется хотя бы один из этих спецификаторов, при переходе на scanf_s придется обязательно вносить исправления и в набор передаваемых аргументов.

---

Похожее замечание справедливо и для функции gets_s. Эта функция в языке С требует два аргумента, а не один, как это когда-то было в gets. Например

C
1
2
char *buffer = malloc(buffer_size);
gets_s(buffer, buffer_size);
Аналогичные соображения имеют место практически для всех "безопасных" функций группы _s.
4
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
30.07.2020, 08:43
"Си - процедурный пошаговый язык" .. вот именно из-за этого он и является самым простым для изучения и самым мощным языком программирования, что доказывает его столь долгая "живучесть" и популярность.

Единственное чего мне в нем не хватало - это прямая работа с флагами регистра(ов) состояния процессора. В частности, не получится реализовать какую-нибудь "свою" 24-х битную арифметику на 8-и битном процессоре. Флаг переноса недоступен, увы.



Всё, что есть в иных "продвинутых" языках не более чем "синтаксический сахар", в т.ч. и реалтайм.

P.S. просто ИМХО, не холивара для. Тему стоит пополнять такими вот примерами, особенно для тех кто "движется наоборот" от разных Питонов в Си .. у них много "непонимания" этого рода. Тоже сталкивался уже.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13212 / 6845 / 1825
Регистрация: 18.10.2014
Сообщений: 17,323
01.08.2020, 06:54
Бездумное определение локальных объектов большого размера

При определении локальных (автоматических) объектов следует иметь в ввиду, что даже в современных "полноразмерных" архитектурах размер стека (в котором и размещаются локальные переменные) обычно ограничен, и обычно ограничен относительно небольшим значением. Да, размеры объектов, которые допустимо объявлять локально, существенно выросли по сравнению с тем, какими они были двадцать лет назад, но тем не менее бездумное объявление локальных переменных огромного размера запросто может привести к переполнению стека и сегодня.

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

C
1
2
3
4
5
6
7
8
9
10
#include <stdlib.h>
 
#define N 10000000u
 
int main(void)
{
  int a[N] = { 0 };
  a[rand() % N] = rand();
  return a[rand() % N];
}
Code
1
Segmentation fault (core dumped)
Практически по всех реализациях проблему зачастую можно сиюминутно "исправить" путем увеличения размера стека через настройки компилятора/сборщика, но в большинстве случаев это будет лишь временной мерой. Правильный подход: просто не объявлять объекты большого размера как локальные (автоматические) переменные. Если в программе возникает необходимость в локальном объекте большого размера, то его следует разместить в динамической, а не в автоматической памяти.

Какой размер объекта является "уже большим", а какой - "еще маленьким", сильно зависит от особенностей платформы. Как правило опыт, позволяющий проводить разумный водораздел между первым и вторым, вырабатывается быстро.
3
56 / 20 / 2
Регистрация: 18.06.2018
Сообщений: 199
01.08.2020, 14:35
На моей практике (но это было давно уже) локальные переменные (с параметрами вкл.) в функции по-хорошему должны попадать в регистровое поле архитектуры назначения. Если оно туда "не лезет", то скорострельность функций как правило падает значительно. А учитывая глубокую вложенность вызовов в современных подходах .. всё это ведет (и частенько) к возмущению "а чего оно так тупит-то?"
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13212 / 6845 / 1825
Регистрация: 18.10.2014
Сообщений: 17,323
05.08.2020, 04:44
Передача "массива" в функцию (в дополнение к Определение размера массива, переданного в качестве аргумента функции)

Все объявления параметров типа "массив T [N]" в списке параметров функции автоматически трансформируются в параметры типа "указатель T *". Наличие или отсутствие явно указанного размера в объявлении массива не имеет никакого значения - этот размер просто игнорируется.

Например, объявления

C
1
2
void foo(int a[20]);
void bar(int a[]);
эквивалентны объявлениям

C
1
2
void foo(int *a);
void bar(int *a);
То есть вышеприведенные функции на самом деле получают параметры типа "указатель".

По этой причине внутри функции бесполезно пытаться применять к такому "массиву" трюк с sizeof для определения количества элементов в массиве. Выражение sizeof a / sizeof *a не сможет вычислить фактическое количество элементов массива, переданного в качестве аргумента, уже хотя бы по той причине, что sizeof a в данном контексте - это размер указателя, а не размер массива.

Уточнение: вышесказанное относится именно к параметрам типа "массив". Параметры типа "указатель на массив" (T (*)[N]) не подвержены таким трансформациям.
3
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13212 / 6845 / 1825
Регистрация: 18.10.2014
Сообщений: 17,323
05.10.2020, 22:47
Цитата Сообщение от мама Стифлера Посмотреть сообщение
Два последних варианта мне точно не встречались, иначе бы вынесло мозг ))
Только за последние пару дней:

При вычислении результата получается "-nan"
Проблема с rand() в цикле
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13212 / 6845 / 1825
Регистрация: 18.10.2014
Сообщений: 17,323
21.01.2021, 12:03
Переключение текущей локали воздействует на формат представления чисел

Переключение со стандартной "С"-локали на некоторую интернациональную локаль, вроде

C
1
setlocale(LC_ALL, "ru_RU.UTF-8");
кроме прочего воздействует на формат строкового представления чисел. Это отражается как на преобразованиях из внутреннего представления в текстовое, так и на преобразованиях из текстового представления во внутреннее.

В частности, в текстовой записи десятичных дробей в качестве разделителя дробной части теперь будет выступать запятая, а не точка. Это окажет воздействие на поведение всех стандартных функций, выполняющих преобразование из строкового формата в числовой. В частности, такие функции как strtod, atof, scanf и т.п. будут воспринимать символ '.' в записи десятичной дроби как некорректный и прекращать преобразование на этом символе. Внешне это будет выглядеть так, как будто эти функции округляют плавающие значения до целого. На самом же деле преобразование просто останавливается на символе '.' и рассмотрения дробной части не происходит вообще.

C
1
2
3
4
5
6
7
8
9
10
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
 
int main()
{
  setlocale(LC_ALL, "ru_RU.UTF-8");
  double d = atof("123.456");
  printf("%lf\n", d);
}
Code
1
123,000000
Если вам необходимо выполнить перевод чисел, записанных по правилам "C"-локали, следует предварительно переключиться на "C"-локаль хотя бы для форматов группы LC_NUMERIC

C
1
2
3
setlocale(LC_ALL, "C");
/* или */
setlocale(LC_NUMERIC, "C");
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
 
int main()
{
  setlocale(LC_ALL, "ru_RU.UTF-8");
  
  setlocale(LC_NUMERIC, "C");
  double d = atof("123.456");
  setlocale(LC_NUMERIC, "ru_RU.UTF-8");
  
  printf("%lf\n", d);
}
Code
1
123,456000
5
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
21.01.2021, 12:03

Самые распространенные строки
type Mytype = record name:string; surname:string; end; var Students:Mytype; MyFile:file of Mytype; ...

Самые распространённые фамилии
Здравствуйте! Для реализации автоподстановки фамилии нужно как-то выявить, например, 1 (10, 15, 20, 100...) тыс самых...

Вывести самые распространенные женские и мужские имена
Помогите решить задачу пожалуйста! Написать программу, которая формирует файл записей данной структуры Type Student=Record ...

Вывести самые распространенные мужские и женские имена
Имеется массив записей о студентах, каждая из которых включает поля: фамилия, имя, отчество, пол, возраст, курс. Разработать программу,...

Вирус блокирует выход на сайт вк и другие распространенные сайты
я чайник поэтому если что то не загрузилось..напишите пожалуйста, и помогите... второй день вожусь с этим. Hosts чистый, drweb выявил один...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
73
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru