Форум программистов, компьютерный форум CyberForum.ru

C для начинающих

Войти
Регистрация
Восстановить пароль
 
 
zss
Модератор
Эксперт С++
6321 / 5905 / 1913
Регистрация: 18.12.2011
Сообщений: 15,186
Завершенные тесты: 1
#1

Распространенные ошибки - C (СИ)

02.10.2014, 12:49. Просмотров 37797. Ответов 57
Метки нет (Все метки)

Оглавление

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


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


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

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

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

Ошибки, связанные с отклонением от стандарта языка
- Неверный тип функции main()
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
02.10.2014, 12:49     Распространенные ошибки
Посмотрите здесь:

Распространенные ошибки - C++
Оглавление Ошибки этапа компиляции Программа не компилируется или компилируется с предупреждениями. Попытка модифицировать...

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

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

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

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

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

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

После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
04.03.2015, 14:42     Распространенные ошибки #31
Цитата Сообщение от HighPredator Посмотреть сообщение
В контексте конкретно printf-а, скорее всего ожидается const char*
Хорошо, тогда есть подать char* или volatile char*, то будет неопределённое поведение?

В твоей цитате сказано "the argument shall be a pointer to the initial element of an array of character type". Под такую формулировку попадают и char*, и volatile char*, и char(*)[], а так же всякие int*, которые указывают на первый элемент массива char'ов
HighPredator
5476 / 1842 / 343
Регистрация: 10.12.2010
Сообщений: 5,433
Записей в блоге: 3
04.03.2015, 16:30     Распространенные ошибки #32
Цитата Сообщение от Evg Посмотреть сообщение
есть подать char*
Все будет ок, т.к. const-correctness позволяет T* -> const T*, но не наоборот.
Цитата Сообщение от Evg Посмотреть сообщение
volatile char*
Про volatile ничего не могу сказать. Думаю, лично мое видение данного вас не сильно интересует.

В сорцах glibc (2.21 -- /libc/stdio-common/printf-parse.h) нашел список форматов для парсера этой группы функций:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* The various kinds off arguments that can be passed to printf.  */
union printf_arg
  {
    wchar_t pa_wchar;
    int pa_int;
    long int pa_long_int;
    long long int pa_long_long_int;
    unsigned int pa_u_int;
    unsigned long int pa_u_long_int;
    unsigned long long int pa_u_long_long_int;
    double pa_double;
    long double pa_long_double;
    const char *pa_string;
    const wchar_t *pa_wstring;
    void *pa_pointer;
    void *pa_user;
  };
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
04.03.2015, 16:40     Распространенные ошибки #33
Цитата Сообщение от HighPredator Посмотреть сообщение
се будет ок, т.к. const-correctness позволяет T* -> const T*, но не наоборот
Т.е. здесь ты обратился к каким-то пунктам, которые описаны вне описания printf'а. Вот и я предлагал точно так же обратиться к прочим пунктам, описанным вне printf'а, в которых должно быть написано про то, что адрес на массив и адрес на первый элемент массива имеют одно и то же значение
HighPredator
5476 / 1842 / 343
Регистрация: 10.12.2010
Сообщений: 5,433
Записей в блоге: 3
04.03.2015, 16:55     Распространенные ошибки #34
Evg, не совсем понял ход вашей мысли. Пусть адреса совпадают. Что это нам дает в контексте обсуждения?
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
04.03.2015, 17:54     Распространенные ошибки #35
Цитата Сообщение от HighPredator Посмотреть сообщение
Пусть адреса совпадают. Что это нам дает в контексте обсуждения?
Откуда вытекает, что это UB? Когда вместо "const char*" подсовывается "char*", то у тебя нашёлся пункт стандарта, согласно которому такое поведение является корректным. Я предполагаю, что есть другой пункт стандарта, согласно которому подсовывание "char(*)[]" вместо "char*" является допустимым
HighPredator
5476 / 1842 / 343
Регистрация: 10.12.2010
Сообщений: 5,433
Записей в блоге: 3
04.03.2015, 19:04     Распространенные ошибки #36
Evg, теперь я вашу мысль понял. Если сможете найти такой, то я, да и огромная часть SO-коммьюнити будет в неоплатном долгу.
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
04.03.2015, 21:35     Распространенные ошибки #37
Цитата Сообщение от HighPredator Посмотреть сообщение
Если сможете найти такой
Я думал ты найдёшь Мне лениво, потому что не совсем понятно, что конкретно надо искать. Всё-таки я не читаю стандарт перед сном и кроме как поиском там фиг что могу найти
HighPredator
5476 / 1842 / 343
Регистрация: 10.12.2010
Сообщений: 5,433
Записей в блоге: 3
05.03.2015, 08:23     Распространенные ошибки #38
Evg, поищу конечно, но 50/50, т.к. если не найду, это не будет значить, что его там нет. Это будет лишь значить, что я не нашел и только. В таком контексте я вроде уже сразу на проигравшей стороне
Цитата Сообщение от Evg Посмотреть сообщение
Я предполагаю, что есть другой пункт стандарта, согласно которому подсовывание "char(*)[]" вместо "char*" является допустимым
Чисто из любопытства: почему вам кажется, что такое может быть? Это же напрочь разные типы указателей.
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
05.03.2015, 12:03     Распространенные ошибки #39
Цитата Сообщение от HighPredator Посмотреть сообщение
В таком контексте я вроде уже сразу на проигравшей стороне
Я не ставлю себе целью выиграть спор. Просто интересно, как обстоит дело с формальной точки зрения на самом деле. Постфактум-то и так понятно, что оно будет работать

Цитата Сообщение от HighPredator Посмотреть сообщение
Чисто из любопытства: почему вам кажется, что такое может быть?
Я уже выше писал, что в своё время что-то приходилось читать в стандарте на предмет того, что указатель на структуру должен преобразовываться к указателю на первое поле и обратно (что их адреса должны совпадать или что-то типа того). Т.е. там, где ожидается, грубо говоря, указатель на int, допустимо подсовывать указатель на всю структуру с соответствующим преобразованием, что гарантирует корректность работы кода. Правда в данном случае нету явного преобразования, так что начинаю подозревать, что даже если в стандарте найдётся что-то подобное для массивов, то это вовсе не будет означать формальную корректность подачи &array в printf/scanf
HighPredator
5476 / 1842 / 343
Регистрация: 10.12.2010
Сообщений: 5,433
Записей в блоге: 3
05.03.2015, 12:32     Распространенные ошибки #40
Цитата Сообщение от Evg Посмотреть сообщение
Правда в данном случае нету явного преобразования, так что начинаю подозревать, что даже если в стандарте найдётся что-то подобное для массивов, то это вовсе не будет означать формальную корректность подачи &array в printf/scanf
Собственно я того же мнения. Поэтому так уперся
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
05.03.2015, 17:41     Распространенные ошибки #41
HighPredator, пожалуй я соглашусь с тобой. Мы живём в мире процессоров, где адрес представляет собой целое число. Но в разные времена делались потуги создать защищённый процессор, в котором адрес представлял собой некоторую конструкцию, содержащую в себе базовый адрес блока, его размер, возможно, какую-то информацию о типе (с таким адресом аппаратура не даст, например, вылезти за границу массива). Так вот если в точке вызова printf/scanf подсунуть адрес, который имеет тип char(*)[], а потом пытаться трактовать его как char* (без каких-либо дополнительных операций приведения типа), то вполне возможно, что для такого процессора сие действо вызовет слом при исполнении. Так что тут и вправду получается UB, который для "нормальных" процессоров ничем плохим не грозит
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
24.03.2015, 15:57     Распространенные ошибки #42
Проблемы под windows при работе с файлами, описанными в виде полных путей

Код типа:

C
FILE *fp;
fp = fopen ("C:\a.txt", "w");
if (fp == NULL) 
{
  printf ("Error\n");
  exit(1);
}
не работают и всегда выдают ошибку.

Проблема в том, что в символьных и строковых литералах (буква в одинарных кавычках или текст в двойных кавычках) языков Си и Си++ символ обратного слэша является управляющим символом, а потому компилятор работает с ним не как с самостоятельным символом, а как с началом последовательности символов. Наиболее частыми такими последовательностями являются "\n" (перевод строки) и "\t" (табуляция), с которыми начинающие, как правило, сталкиваются уже с первых дней. Конкретно в данном примере у символа обратного слэша точно такая же трактовка, а потому последовательность символов "\a" распозналась как один управляющий символ, в итоге это привело к тому, что путь до файла в коде программы оказался вовсе не таким, каким ожидал программист. Чтобы в символьном и строковом литерале записать символ обратного слэша, надо его продублировать, т.е. написать "\\". Таким образом наша программа должна выглядеть как

C
fp = fopen ("C:\\a.txt", "w");
ПерС
371 / 287 / 89
Регистрация: 05.11.2013
Сообщений: 820
Записей в блоге: 5
Завершенные тесты: 1
26.03.2015, 16:16     Распространенные ошибки #43

Не по теме:

я лично больше парился вот с этим:


"Удвоение" последней строки файла
Возьмём небольшую программу, построчно читающую текстовый файл:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
 
int main (void){
 FILE *fp = fopen ("C:\\temp\\data.txt", "r");
 if (fp == NULL) {
  printf ("Can't open file!\n");
  fflush (stdin);
  getchar();
  exit(1);
 }
 const int MAXLEN = 128;
 char buf[MAXLEN+1];
 while (!feof(fp)) { //последняя строка файла может быть "удвоена"
  fgets (buf, MAXLEN, fp);
  puts (buf);
 }
 fclose (fp);
 system("pause");
 return 0;
} //Visual Studio
Сам файл C:\temp\data.txt состоит из пары строк и пустой строки в конце:
string 1
string 2


"Удивительным образом" наша программа "удвоит" string 2, которая будет напечатана дважды. Проблема в том, что после чтения string 2 конец файла ещё не достигнут, а ввод-вывод через stdio буферизован... последний шаг цикла прочитает пустую строку, а в буфере всё ещё будет string 2, которая и выведется повторно вместо пустой строки. Проверьте сами - если удалить из файла данных пустую строку в конце -
string 1
string 2
- никакого "удвоения" не будет. Встречается вот такое решение -
C
1
2
3
4
5
while (1) {
 fgets (buf, MAXLEN, fp);
 if (feof(fp)) break;
 puts (buf);
}
- но оно грозит, наоборот, "потерять" последнюю строку файла, если после неё нет пустой. Хотя при форматном чтении данных, скажем, целых чисел из файла, такой подход применим:
C
1
2
3
4
5
6
int a;
while (1) {
 fscanf (fp, "%d", &a);
 if (feof(fp)) break;
 printf ("%d ",a);
}
Что же до нашего исходного примера - всегда полезен дополнительный анализ прочитанных строк (например, исключение из вывода пустых) или хоть зануление буфера после того, как он использован:
C
1
2
3
4
5
while (!feof(fp)) {
 fgets (buf, MAXLEN, fp);
 puts (buf);
 buf[0]='\0';
}
"Лишние" пустые строки при построчном выводе данных

С программкой из этого примера связана ещё одна типовая проблема - прочитанные из файла строки при выводе почему-то содержат лишние пустые строки между строками данных. Файл данных:
string 1
string 2
Вывод:
string 1

string 2
Для продолжения нажмите любую клавишу . . .

Всё объясняется просто - fgets читает строку файла вместе с символом перевода строки (точней, под Windows - с парой символов \r\n, интерпретируемых как один), а puts добавляет к выводимой строке ещё один перевод строки. Так что выводите другим методом или удаляйте из прочитанной fgets'ом строки последний символ:
C
1
2
3
4
5
6
7
8
9
const int MAXLEN = 128;
char buf[MAXLEN+1];
while (!feof(fp)) {
 fgets (buf, MAXLEN, fp);
 int len = strlen(buf);
 if (buf[len-1]=='\n') buf[len-1]='\0';
 puts (buf);
 buf[0]='\0';
}
Evg
Эксперт CАвтор FAQ
17545 / 5783 / 370
Регистрация: 30.03.2009
Сообщений: 15,930
Записей в блоге: 26
08.12.2015, 16:45     Распространенные ошибки #44
Использование адресной арифметики вместо va_arg при работе с функцией с переменным числом параметров

В интернете видел много разных "обучающих" статей о том, как извлекать аргументы в функции с переменным числом аргументов. Они сводятся примерно к следующему (пример взят из одной из статей):

C++
#include <stdio.h>
 
/* Вычисление среднего арифметического аргументов (количество) */
double f(int n, ...)            /* количество элементов */
{  int *p = &n;
    p++;                        /* установка 'целого' на double */
    double *pp = (double *)p;   /* преобразование типа указателя */
    double sum = 0, count = n;
    for (;n--;pp++)             /* правильное увеличение на 8 */
       sum+=(*pp);
    return (sum/count);
}
 
int main(void)
{
  double s = f (4, 1.0, 2.0, 3.0, 4.0);
  printf ("%e\n", s);
}
Этот код НЕКОРРЕКТНЫЙ. Он будет работать, грубо говоря, только на архитектуре x86 в 32-битном режиме. Афторы подобных статей закладываются на то, что все параметры будут переданы через стек и лягут в памяти некоторым конкретным образом. В реальности передача параметров на каждой архитектуре происходит по разному, в соответствии с программными соглашениями (ABI) для каждой конкретной архитектуры/режима. Чтобы программисту не нужно было учитывать особенности передачи параметров под каждые архитектуры, придумали специальные интерфейсы va_start, va_arg, va_end. В итоге текст программы для всех архитектур будет выглядеть одинаково, но на каждой архитектуре интерфейс раскроется в правильное извлечение параметров

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

Правильно написанный код для данного примера должен выглядеть примерно так:

C
#include <stdio.h>
#include <stdarg.h>
 
/* Вычисление среднего арифметического аргументов (количество) */
double f (int n, ...)
{
    va_list va;
    double sum = 0, count = n;
 
    va_start (va, n);
    for (;n--;)
      sum += va_arg (va, double);
    va_end (va);
 
    return (sum/count);
}
 
int main(void)
{
    double s = f (4, 1.0, 2.0, 3.0, 4.0);
    printf ("%e\n", s);
}
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
27.07.2016, 19:49     Распространенные ошибки
Еще ссылки по теме:

Найти и вывести самые распространенные женские те мужские имена - Pascal ABC
О каждом студенте факультета доступна следующая информация: фамилия, имя, отчество, пол, возраст, курс. Найти и вывести самые...

Вывести самые распространенные мужское и женское имена среди студентов - Pascal
Во входном файле записана следующая информация о каждом из 20 студентов некоторого вуза: &lt;фамилия&gt;, &lt;имя&gt;, &lt;отчество&gt;, &lt;пол&gt;, &lt;возраст&gt;,...

Какие самые распространенные бесплатные спам-фильтры Вам известны? - Безопасность сайтов
Всем привет понимаю что не заслуживаю как новенький о помощи но все же попрошу помогите . Вопрос жизни . Какие самые распространенные...

Тема: составить программу Справочная экспертная система «Словарь» (содержит наиболее распространенные терми - Программирование
Тема Помогите написать программу Справочная экспертная система «Словарь» (содержит наиболее распространенные термины и понятия по...

Найти ошибки в данной программе (известно что есть только 2 ошибки) - Assembler
У меня в модуле с програмирования задание: Найти ошибки в данной программе (известно что есть только 2 ошибки) .model small .data ...


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

Или воспользуйтесь поиском по форуму:
emerael
0 / 0 / 0
Регистрация: 27.07.2016
Сообщений: 1
27.07.2016, 19:49     Распространенные ошибки #45
Ошибки в сравнении (использование = вместо ==, очень частотная вещь среди пересевших на си с паскалеподобных языков)
C++
1
2
3
4
if( var = 0 ) {/*
some code here
*/
}
скомпилится, но в иф никогда не зайдет и затрет данные из переменной на ноль.
Yandex
Объявления
27.07.2016, 19:49     Распространенные ошибки
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru