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

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

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 240, средняя оценка - 4.65
zss
Модератор
Эксперт С++
 Аватар для zss
6056 / 5659 / 1829
Регистрация: 18.12.2011
Сообщений: 14,455
Завершенные тесты: 1
02.10.2014, 12:49     Распространенные ошибки #1
Оглавление

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


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


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

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

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

Ошибки, связанные с отклонением от стандарта языка
- Неверный тип функции main()
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16944 / 5349 / 329
Регистрация: 30.03.2009
Сообщений: 14,384
Записей в блоге: 26
05.03.2015, 17:41     Распространенные ошибки #41
HighPredator, пожалуй я соглашусь с тобой. Мы живём в мире процессоров, где адрес представляет собой целое число. Но в разные времена делались потуги создать защищённый процессор, в котором адрес представлял собой некоторую конструкцию, содержащую в себе базовый адрес блока, его размер, возможно, какую-то информацию о типе (с таким адресом аппаратура не даст, например, вылезти за границу массива). Так вот если в точке вызова printf/scanf подсунуть адрес, который имеет тип char(*)[], а потом пытаться трактовать его как char* (без каких-либо дополнительных операций приведения типа), то вполне возможно, что для такого процессора сие действо вызовет слом при исполнении. Так что тут и вправду получается UB, который для "нормальных" процессоров ничем плохим не грозит
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16944 / 5349 / 329
Регистрация: 30.03.2009
Сообщений: 14,384
Записей в блоге: 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");
ПерС
366 / 282 / 84
Регистрация: 05.11.2013
Сообщений: 806
Записей в блоге: 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
Эксперт С++Автор FAQ
 Аватар для Evg
16944 / 5349 / 329
Регистрация: 30.03.2009
Сообщений: 14,384
Записей в блоге: 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);
}
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
*/
}
скомпилится, но в иф никогда не зайдет и затрет данные из переменной на ноль.
Tulosba
:)
Эксперт С++
4382 / 3225 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
27.07.2016, 22:03     Распространенные ошибки #46
Цитата Сообщение от emerael Посмотреть сообщение
использование = вместо ==
Любой современный компилятор ругается на это предупреждением. Хотя, справедливости ради, стоит заметить, что VC++ делает это только начиная с 4го уровня ворнингов.
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16944 / 5349 / 329
Регистрация: 30.03.2009
Сообщений: 14,384
Записей в блоге: 26
28.07.2016, 09:44     Распространенные ошибки #47
Цитата Сообщение от Tulosba Посмотреть сообщение
Любой современный компилятор ругается на это предупреждением
Это никакой роли не играет, т.к. среди начинающих почти никто не умеет читать предупреждения

В любом случае этот пункт уже есть - "Ошибки в логических выражениях". Правда ссылка ведёт на параллельную тему в разделе C++
gng
609 / 455 / 122
Регистрация: 08.09.2013
Сообщений: 1,168
28.07.2016, 12:50     Распространенные ошибки #48
Цитата Сообщение от Tulosba Посмотреть сообщение
Любой современный компилятор ругается на это предупреждением.
А на это уже нет
C
1
2
3
if ((a=0) || (b=0)) {
  ...
}
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16944 / 5349 / 329
Регистрация: 30.03.2009
Сообщений: 14,384
Записей в блоге: 26
28.07.2016, 13:00     Распространенные ошибки #49
Цитата Сообщение от gng Посмотреть сообщение
А на это уже нет
А это негласная договорённость. Чтобы оставить программисту возможность писать присваивание, когда ему нужно именно присваивание, а не сравнение
gng
609 / 455 / 122
Регистрация: 08.09.2013
Сообщений: 1,168
28.07.2016, 13:08     Распространенные ошибки #50
Цитата Сообщение от Evg Посмотреть сообщение
А это негласная договорённость.
Поэтому для меня, често говоря, стало новостью, чо без двойных скобок ругается на
C
1
if (a=0)
Не приходилось как-то получать таких предупреждений.
linuxdesk
3 / 3 / 2
Регистрация: 25.03.2015
Сообщений: 22
02.08.2016, 17:30     Распространенные ошибки #51
Цитата Сообщение от Evg Посмотреть сообщение
А это негласная договорённость.
Подскажите, пожалуйста, как изменить следующую строчку, чтобы варнинга не было?
C
1
while ((vqty = get_vcqty(Seq, os, qty)) && (TableOrder[qS].prc <= prc))
upd: присваивание мне тут нужно
Evg
Эксперт С++Автор FAQ
 Аватар для Evg
16944 / 5349 / 329
Регистрация: 30.03.2009
Сообщений: 14,384
Записей в блоге: 26
02.08.2016, 20:30     Распространенные ошибки #52
А какой у тебя компилятор? Наверняка не все компиляторы этого придерживаются

Добавлено через 31 минуту
На крайний случай всегда можно написать явное сравнение (лично на мой взгляд такой код почти всегда более читаемый). Т.е. вместо

C
if ((x = y))
писать

C
if ((x = y) != 0)
Тут уже warning'а не будет
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
03.08.2016, 03:27     Распространенные ошибки
Еще ссылки по теме:

C (СИ) Ошибки IntelliSense
C (СИ) Ошибки в терминале
C (СИ) Ошибки компиляции
C (СИ) Поиск ошибки

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

Или воспользуйтесь поиском по форуму:
linuxdesk
3 / 3 / 2
Регистрация: 25.03.2015
Сообщений: 22
03.08.2016, 03:27     Распространенные ошибки #53
Цитата Сообщение от Evg Посмотреть сообщение
А какой у тебя компилятор?
VS2015.
Цитата Сообщение от Evg Посмотреть сообщение
if ((x = y) != 0)
Спасибо
Yandex
Объявления
03.08.2016, 03:27     Распространенные ошибки
Ответ Создать тему
Опции темы

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