Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.63/421: Рейтинг темы: голосов - 421, средняя оценка - 4.63
zss
Модератор
Эксперт С++
7098 / 6629 / 4196
Регистрация: 18.12.2011
Сообщений: 17,486
Завершенные тесты: 1
#1

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

21.09.2014, 17:33. Просмотров 75990. Ответов 63
Метки нет (Все метки)

Оглавление

Ошибки этапа компиляции
Программа не компилируется или компилируется с предупреждениями.
  1. Попытка модифицировать константу через указатель
  2. Неправильное понятие приведения
  3. Лишняя точка с запятой
  4. Отсутствие точки с запятой после определения классового типа
  5. Отсутствие возврата значения из функции
  6. Использование комментариев в #define
  7. Компилятор не находит iostream.h
  8. Внутри switch ошибка Case bypasses initialization of a local variable
  9. Передача двумерных массивов и указателей в функцию
  10. Не удается открыть файл stdafx.h
  11. Ошибка "unresolved external symbol _WinMain@16"
  12. Ошибка "This function or variable may be unsafe"
Ошибки этапа выполнения
Программа прекращает работу с сообщением об ошибке
  1. Выделение памяти без дальнейшего освобождения или неверное освобождение
  2. Возврат ссылки/указателя на локальную переменную
  3. Использование неинциализированной переменной
  4. Выход за пределы массива
  5. Ошибки при использовании функции scanf()
  6. Работа с локальной копией объекта, вместо работы с самим объектом
  7. Интерпретация одиночного char символа как символьной строки
  8. Ошибка преобразования: type ** в const type **
Неправильное поведение программы на этапе исполнения
Программа исполняется, но не так, как хотелось.
  1. Неожиданное закрытие окна
  2. "Неожиданное" целочисленное деление в арифметических выражениях
  3. Ошибки в логических выражениях
  4. Лишняя точка с запятой
  5. switch без break
  6. Сравнение вещественных чисел при вычислениях
  7. Сравнение char массивов
  8. Использование чисел, записанных в других системах счисления
  9. Проверки на принадлежность значения определенному интервалу
  10. Неверный аргумент тригонометрических функций
  11. Сравнение знаковой переменной с беззнаковой
  12. Использование запятой для отделения дробной части
  13. Забытое выделение тела цикла for, while и операторов if else
  14. Определение размера массива, переданного в качестве аргумента функции
  15. Порядок вычисления аргументов при вызове функции
  16. Некорректное использование логических переменных
  17. Вычисленное значение косинуса не совпадает с ожидаемым
  18. Локальная переменная экранирует переменную с таким же именем из вышестоящей области видимости
  19. Неправильное использование memset
Алгоритмические ошибки
Ошибки, допущенные при разработке алгоритма
  1. Двойная перестановка строк или элементов массива
  2. Использование символа цифры вместо числа
Ошибки ввода-вывода
  1. Оставление символа '\n' в потоке ввода
  2. Ошибки при использовании функции scanf()
  3. При работе с fgetc чтение файла обрывается при достижении буквы 'я'
  4. При считывании из файла последний элемент читается дважды
Ошибки, связанные с отклонением от стандарта языка
  1. Неверный тип функции main()

Ошибки проектирования АТД (классов).
  1. Вызов виртуальной функции из конструктора
  2. Отсутствие точки с запятой после определения класса/структуры
  3. Неверный вызов конструктора базового класса из конструктора производного
  4. Неверный порядок при инициализации
  5. Нарушение правила ТРЕХ.
  6. Отсутствие виртуального деструктора в базовом классе
  7. Неправильное обращение к конструктору по умолчанию
  8. Не очевидные моменты с вызовом конструктора базового класса
  9. Неявно объявленный конструктор по умолчанию
  10. Перегрузка оператора >>
Ошибки при использовании STL контейнеров
  1. Невалидные ссылки/указатели, при перемещении объектов
  2. Ошибки связанные с итераторами (кэширование размера контейнера)
  3. Ошибки связанные с итераторами (удаление элементов по итератору в циклах)
  4. Ошибки связанные с итераторами (префикс-постфиксные инкременты при удалении элементов в цикле)
27
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
21.09.2014, 17:33
Ответы с готовыми решениями:

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

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

Найти ошибки в коде и исправить эти ошибки (Наследование)
Вот в общем 3 файла, изучаю наследование на примере односвязного и двусвязного...

с2079 и с2228 ошибки при компиляции ниже участок кода, с которым праблема и скрины ошибки
// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ bool game = true; // КЛАССЫ class Me; class...

Найти ошибки в коде и исправить эти ошибки
Есть у меня вот такой код: #include <iostream> using namespace std; ...

63
BumerangSP
4287 / 1409 / 463
Регистрация: 16.12.2010
Сообщений: 2,941
Записей в блоге: 3
24.09.2014, 12:43 #21
Неверный аргумент тригонометрических функций.
Очень часто бывают ошибки вроде таких:
C++
1
x = sin( 90 ); // x = 0.893997. Имелось ввиду 90 градусов, а функция sin принимает в радианах.
Решение: перевести в градусы:
C++
1
x = sin( 90 * M_PI / 180 ); // x = 1
3
BumerangSP
4287 / 1409 / 463
Регистрация: 16.12.2010
Сообщений: 2,941
Записей в блоге: 3
24.09.2014, 12:43 #22
Неверный вызов конструктора базового класса из конструктора производного.
Встречаются подобные варианты:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class A {
public:
    int x;
    A() {
        x = 11;
    }
 
    A( int v ) {
        x = v;
    }
};
 
class B : public A {
public:
    B() {}
    B( int v ) {
        A::A( v );  // такой вызов 
        A();        // или такие
        A::A(); 
    }
};
 
int main()
{
    B b( 1 );
    std::cout << b.x;   // x по прежнему = 11.
}
Решение: использовать списки инициализации.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A {
public:
    int x;
    A(): x( 11 ) {}
    A( int v ) : x( v ) {}
};
 
class B : public A {
public:
    B() {}
    B( int v ) : A( v ) {}
};
 
int main() {
    B b( 1 );
    std::cout << b.x;   // x = 1
}
5
BumerangSP
4287 / 1409 / 463
Регистрация: 16.12.2010
Сообщений: 2,941
Записей в блоге: 3
24.09.2014, 12:43 #23
Сравнение знаковой переменной с беззнаковой.
C++
1
2
3
4
    int x = -5;
    unsigned int y = 100;
    if ( x < y ) // Результат false 
    ...
Знаковое x приводится к беззнаковому, отчего возрастает до UINT_MAX - 4 (UINT_MAX равно (unsigned int)-1).
Решение - не сравнивать такие типы, заранее приводить к одному.
0
zss
Модератор
Эксперт С++
7098 / 6629 / 4196
Регистрация: 18.12.2011
Сообщений: 17,486
Завершенные тесты: 1
24.09.2014, 12:58  [ТС] #24
Вызов виртуальной функции из конструктора
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class B
{
    int value;
    virtual int f() const { return 1; } 
 
public:
    B() { value = f(); }
    int getValue() const { return value; }
};
 
class D : public B
{
    virtual int f() const { return 2; } 
};
 
int main()
{
    D d; 
    cout << d.getValue() << endl; // Выводит 1, а не 2
}
Суть в том, что при создании объекта D, сначала создается базовая часть (класс B), а в конструкторе базового класса ничего о классе D еще не известно, т.к. он еще не создан (и в том числе не заполнена таблица виртуальных функций).
1
MrGluck
Модератор
Эксперт CЭксперт С++
8078 / 4931 / 1431
Регистрация: 29.11.2010
Сообщений: 13,366
24.09.2014, 16:21 #25
Использование запятой для отделения дробной части
C++
1
2
double x;
x = 3,2; // ожидаем, что присвоится  3.2, а на самом деле 3
Правильно использовать для таких целей .
C++
1
x = 3.2;
Ошибка является весьма коварной и её сложно заметить при использовании длинных выражений. Максимум, что выдаст компилятор - предупреждение о неиспользуемой переменной при включённом флаге -Wunused-value. Так что старайтесь держать сборку идеально чистой и используйте флаги предупреждений, чтобы суметь поймать данную ошибку.
3
sourcerer
Модератор
Эксперт CЭксперт С++
4834 / 2025 / 316
Регистрация: 20.02.2013
Сообщений: 5,450
Записей в блоге: 24
Завершенные тесты: 1
24.09.2014, 16:59 #26
Забытое выделение тела цикла for, while и операторов if else
В циклах часто используют запись тела цикла без фигурных скобок, при условии, что в качестве тела цикла используется один оператор:
C++
1
2
3
4
5
6
7
8
9
// допустимо и так:
for ( int i = 0; i < n; ++i )
    arr[i] = i * i;
 
// и так:
for ( int i = 0; i < n; ++i )
{
    arr[i] = i * i;
}
Это вводит в заблуждение новичков и они пытаются "запихнуть" в цикл несколько операторов, ограничившись отступами:
C++
1
2
3
for ( int i = 0; i < n; ++i )
    arr[i] = i * i;    // будет выполняться на каждом витке цикла
    std:: cout << "Value of " << i + 1 << " element is " << arr[i];    // выполнится после цикла
Если операторов несколько, то в цикле выполнится только первый, остальные же - только по окончании всех итераций (всех витков в цикле). Поэтому, чтобы добиться задуманного новичок должен был бы написать свой код так:
C++
1
2
3
4
5
for ( int i = 0; i < n; ++i )
{   // оба оператора будут выполняться на каждом витке цикла:
    arr[i] = i * i;
    std:: cout << "Value of " << i+1 << " element is " << arr[i];
}
Во избежание подобных ошибок рекомендуется ставить скобки и в случае одного оператора в теле цикла.
Аналогичная ошибка присутствует и в таком примере (Evg):
C++
1
2
3
4
5
if ( x == 0 )
    if ( y == 2 )
        printf ( "a\n" );
else
    printf ( "b\n" );
Из-за отсутствия скобок, оператор else относится не к первому if, а ко второму.
Скобки устраняют эту ошибку:
C++
1
2
3
4
5
6
7
if ( x == 0 )
{
    if ( y == 2 )
        printf( "a\n" );
} else {
    printf( "b\n" );
}

Не по теме:

gru74ik, с Вашего позволения перенёс объявление переменной i в цикл.

1
BumerangSP
4287 / 1409 / 463
Регистрация: 16.12.2010
Сообщений: 2,941
Записей в блоге: 3
24.09.2014, 20:31 #27
Определение размера массива, переданного в качестве аргумента функции.
C++
1
2
3
4
5
6
7
8
9
int f( int * a ) {
    return sizeof a / sizeof *a;
}
 
int main() {
    int a[] = { 1, 2, 3 };
    std::cout << sizeof a / sizeof *a << '\n';  // 3
    std::cout << f( a );                    // 1
}
Решение: передавать размер массива как параметр функции:
C++
1
2
3
4
5
6
7
int f( int * a, int size ) {
    return size;
}
int main() {
    int a[] = { 1, 2, 3 };
    std::cout << f( a, sizeof a / sizeof *a );      // 3
}
Размер массива можно задать при использовании шаблона
( (предложил ISergey):
C++
1
2
3
4
5
6
7
8
9
#include <iostream>
template <typename T, std::size_t size>
std::size_t arr_size( T(&)[size] ) { return size; }
int  main()
{
    int arr[] = { 1, 2, 3, 4 };
    std::cout << arr_size( arr ) << std::endl;
    return 0;
}
Если массив статический, то размер можно узнать, если передать ссылку на массив
(автор Ilot http://www.cyberforum.ru/post6650671.html):
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
template <typename T>
int f( T & a ) {
    return sizeof( a ) / sizeof( a[0] );
}
 
int main() 
{
    const int N = 5;
    const int M = 15;
    char a[N];
    std::cout << f( a ) << std::endl;
    char b[M];
    std::cout << f( b ) << std::endl;
    return 0;
}
0
Croessmah
++Ͻ
14540 / 8300 / 1559
Регистрация: 27.09.2012
Сообщений: 20,368
Записей в блоге: 3
Завершенные тесты: 1
26.09.2014, 22:19 #28
Неверный порядок при инициализации
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream> 
 
struct Test
{
    int x;
    int y;
    Test ( int y_ ) : y( y_ ) , x( y * 2 ) {}
};
     
int main() {
    Test t( 10 );
    std::cout << t.x << std::endl;
    std::cout << t.y << std::endl;
}
Неверный порядок при инициализации может стать источником ошибок. В списке инициализации конструктора, инициализация происходит в порядке объявления переменных, а не в порядке, указанном в списке инициализации.
В данном случае, сначала будет инициализирована переменная x, а только потом переменная y.
8
daslex
1291 / 535 / 177
Регистрация: 02.08.2011
Сообщений: 2,756
29.09.2014, 21:27 #29
Работа с локальной копией объекта, вместо работы с самим объектом


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void foo_create( int * Arr ) {
    Arr = new int [10];
    for ( int i = 0; i < 10; i++ ) Arr[i] = i;
}
 
void foo_delete( int * Arr ) {
    delete [] Arr;
    Arr = NULL;
}
 
int main() {
    int * Ptr;
    foo_create( Ptr );
    for ( int i = 0; i < 10; i++ ) cout << Ptr[i] << "  ";
    foo_delete( Ptr );
    return 0;
}
Здесь ошибка в том, что при вызове функции foo_create в нее передается копия указателя Ptr
(который еще не инициализирован). При выделении памяти, ее адрес помещается в эту копию,
т.е. значение переменной Ptr не изменится.
В результате, после возврата из функции, адрес потеряется и, соответственно, потеряется выделенная память.
__________________________________
Варианты решения проблемы:
1. Передавать в функцию адрес указателя
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
void foo_create( int ** ptr_to_ptr )
{
    *ptr_to_ptr = new int [10];
    for ( int i = 0; i < 10; i++ ) (*ptr_to_ptr)[i] = i;
}
 
void foo_delete( int ** ptr_to_ptr )
{
    delete [] *ptr_to_ptr;
    *ptr_to_ptr = nullptr;
}
 
int main()
{
    int * Ptr;
    foo_create( &Ptr );
    for ( int i = 0; i < 10; i++ ) std::cout << Ptr[i] << "  ";
    foo_delete( &Ptr );
    std::cout  << Ptr <<  std::endl;
}
2. Передавать в функцию сылку на указатель
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 <iostream>
 
void foo_create( int * & Arr )
{
    Arr = new int [10];
    for ( int i = 0; i < 10; i++ ) Arr[i] = i;
}
 
void foo_delete( int * & Arr )
{
    delete [] Arr;
    Arr = nullptr;
}
 
int main()
{
    int * Ptr;
    foo_create( Ptr );
    for ( int i = 0; i < 10; i++ ) std::cout << Ptr[i] << "  ";
    std::cout <<  std::endl;
    foo_delete( Ptr );
    std::cout <<  Ptr << std::endl;
}
6
zss
Модератор
Эксперт С++
7098 / 6629 / 4196
Регистрация: 18.12.2011
Сообщений: 17,486
Завершенные тесты: 1
04.10.2014, 13:25  [ТС] #30
Нарушение правила ТРЕХ.
Правило трёх (также известное как «Закон Большой Тройки» или «Большая Тройка») — правило, гласящее, что если класс или структура определяет один из следующих методов, то они должны явным образом определить все три метода:
1. Деструктор
2. Конструктор копирования
3. Оператор присваивания

Эти три метода являются особыми членами-функциями.
Они автоматически создаются компилятором в случае отсутствия их явного объявления.
Если один из них должен быть определен программистом, то это означает, что версия, сгенерированная компилятором, не удовлетворит потребности класса и для двух других.
Пример:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class A
{
private:
    int * x; // указатель на массив
    int n;
 
public:
    A() : x( 0 ), n( 0 ) {}
    A( int N ) : n( N )
    {
        x = new int [N]; // под массив выделяется память - нужен деструктор для ее удаления
        for ( int i = 0; i < N; i++ )
            x[i] = i;
    }
 
    ~A() // Создаем деструктор
    {
        delete [] x;
    }
 
    A( const A & a ) : n( a.n ) //  Обязательный копиконструктор (раз есть деструктор)
    {
        x = new int [a.n];
        for ( int i = 0; i < a.n; i++ )
            x[i] = a.x[i];
    }
 
    A & operator = ( const A & a ) // Обязательный оператор присвоения (раз есть деструктор)
    {
        if ( this == &a ) 
            return *this; // присвоение самому себе, ничего делать не надо
 
        delete [] x;
 
        x = new int [a.n];
        for ( int i = 0; i < a.n; i++ )
            x[i] = a.x[i];
 
        return *this;
    }
 
    A operator + ( A & a ) // слияние массивов 
    {
        A c;
        c.n = this->n + a.n;
        c.x = new int [c.n];
 
        int i = 0;
        for ( ; i < this->n; i++ )
            c.x[i] = this->x[i];
 
        int j = 0;
        for ( ; i < c.n; i++, j++ )
            c.x[i] = a.x[j];
 
        return c; // для передачи по значению используется копиконструктор
    }
};
 
int main()
{
    A a1( 2 ), a2( 3 );
    A a3 = a1 + a2; // используется оператор присвоения
    return 0;
}
Примечание.
Не забывайте и о том, что и конструктор по умолчанию тоже в этом случае не создается.
Правда, при необходимости его вызова компилятор выдаст предупреждающее сообщение:
error C2512: A: нет подходящего конструктора по умолчанию
5
daslex
1291 / 535 / 177
Регистрация: 02.08.2011
Сообщений: 2,756
04.10.2014, 14:16 #31
Интерпретация одиночного char символа как символьной строки

Начинающие программисты иногда воспринимают отдельный тестовый символ как строку из одного значения.
char c; - отдельный символ, занимающий 1 байт в памяти.
char str[256] - строка символов длиной не более 255, которая всегда должна заканчиваться признаком окончания строки - числом ноль.

Из-за этого различия вполне реально столкнутся с проблемой преобразования при работе со строками.
Простейший пример
C++
1
2
3
4
5
char ch = 'z';
char S[255] = "TestString__";
 
strcat( S, (char *)ch ); // попытка объединить строку с символом
std::cout << S << "\n";
Здесь используется явное приведение типа char к char *,
т.к. написавший подразумевает символ строки как строку в 1 символ., но в этом-то и ошибка.
______________________

Для подобного преобразования строки нужно выполнить явное присвоение нужного символа и добавление терминального нуля:
C++
1
2
3
4
5
6
char ch = 'z';
char S[255] = "TestString__";
int L = strlen( S );
S[L] = ch;
S[L + 1] = 0;
std::cout << S << "\n";

Вариант решения - создать массив в 2 символа. Принудительно забить сам символ и признак окончания в этот массив, после чего использовать этот массив вместо изначального символа.

C++
1
2
3
4
5
6
7
8
char ch = 'z';
char S[255] = "TestString__";
char temp[2] = "";
temp[0] = ch;
temp[1] = 0; // Обязательно
 
strcat( S, temp );
std::cout << S << "\n";
Итак всегда помните, что строка символов заканчивается нулем!
0
MrGluck
Модератор
Эксперт CЭксперт С++
8078 / 4931 / 1431
Регистрация: 29.11.2010
Сообщений: 13,366
06.10.2014, 16:38 #32
Отсутствие виртуального деструктора в базовом классе.
Класс по умолчанию генерирует деструктор, но при этом он не является виртуальным. Отличие виртуального деструктора в том, что он предполагает освобождение ресурсов не только базового класса, но и всех производных. Ниже пример, явно демонстрирующий проблему:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
 
class A
{
public:
    A() { std::cout << "A()"; }
    ~A() { std::cout << "~A()"; }
};
 
class B : public A
{
public:
    B() { std::cout << "B()"; }
    ~B() { std::cout << "~B()"; }
};
 
int main()
{
    A * p = new B;
    delete p;
    // выведет A()B()~A()
}
При размещении объекта производного класса на стеке, вызов деструкторов будет правильным, а вот при динамическом (как выше), произойдёт неполное освобождение ресурсов т.к. деструктор не виртуальный. Таким образом, виртуальный деструктор нужен для правильного полиморфного удаления объектов.

Для исправления ошибки в примере выше достаточно сделать деструктор класса A виртуальным,
строку 7 поменять на
C++
1
virtual ~A() { std::cout << "~A()"; }
Во избежание проблем всегда следует как минимум:
1. При наличии хотя бы одного виртуального метода объявлять виртуальным и деструктор.
2. Также его надо явно объявлять виртуальным, если класс предполагается в будущем сделать базовым.

Майерс советует делать виртуальным деструктор всегда, кроме тех случаев, когда есть веские причины этого не делать.
В некоторых компиляторах (mingw) существует флаг -Weffc++, который выдаёт предупреждение по данной ошибке.

P.S. в "правиле трёх" надо бы также описать данный случай как исключение из правил т.к. при необходимости явного объявления виртуального деструктора это вовсе не значит, что нужно описывать конструктор копий или оператор присваивания. ЕМНИМ, в оригинале говорится, что если в классе идёт работа с памятью, то необходимо явно объявить три вещи...
2
XZentus
201 / 199 / 65
Регистрация: 06.10.2013
Сообщений: 552
31.10.2014, 23:02 #33
Порядок вычисления аргументов при вызове функции.

Недавно ловил ошибку в собственном коде.

C
1
2
3
4
5
6
7
8
9
10
11
12
/* Глобальная переменная, единственное место хранения количества хэшей.
Т.е. если бы была ошибка где-то в алгоритме, здесь, скорее всего, было бы левое значение */
int nhash = 0; 
 
// ...
 
int main( int argn, char * argv[] ) {
    // ...
    // nkeyhash в своих недрах активно использует nhash, при чем только увеличивая значение
    printf( "\n%lli\t%d\n", nkeyshash( argv[1], argv[2] ), nhash );
    return 0;
}
Глупая ошибка, но час потратил, пока не понял, почему nhash всегда показывал 0.
Хотя, если бы там действительно был ноль на момент завершения работы, программа выдавала бы неверное значение nkeyshash(...)

P.S. Кстати, результат не зависел от ключей оптимизации.

Т.о. имейте ввиду, что нельзя рассчитывать на то, что вычисление значений аргументов в списке параметров функции выполняется слева-направо.
1
MrGluck
Модератор
Эксперт CЭксперт С++
8078 / 4931 / 1431
Регистрация: 29.11.2010
Сообщений: 13,366
31.10.2014, 23:45 #34
Сегодня наткнулся на подобное (код понятное дело упрощён и добавлены действия, эмулирующие работу):
C++
1
2
3
4
5
6
7
8
9
10
11
bool GetImageSize( const char * fileName, int & width, int & height )
{
    width = height = 42;
    return true;
}
 
int main ()
{
    int width = 0, height = 0;
    std::cout << GetImageSize( "tmp.jpg", width, height ) << " " << width << " " << height;
}
Проблема в том, что до ; значения остаются в рамках одной инструкции, т.е. не меняют своё значение. Возможно, UB, надо вырезку из стандарта прикрепить, а времени сейчас нет.

Добавлено через 52 секунды
Это в продолжение к проблеме XZentus, просто код, на мой взгляд, более реален.
0
XZentus
201 / 199 / 65
Регистрация: 06.10.2013
Сообщений: 552
01.11.2014, 08:51 #35
Цитата Сообщение от MrGluck Посмотреть сообщение
Возможно, UB, надо вырезку из стандарта прикрепить, а времени сейчас нет.
Неуточняемое поведение.

Кликните здесь для просмотра всего текста
6.5.2.2 Function calls
Constraints
1 The expression that denotes the called function 92) shall have type pointer to function
returning void or returning a complete object type other than an array type.
2 If the expression that denotes the called function has a type that includes a prototype, the
number of arguments shall agree with the number of parameters. Each argument shall
have a type such that its value may be assigned to an object with the unqualified version
of the type of its corresponding parameter.
Semantics
3 A postfix expression followed by parentheses () containing a possibly empty, comma-
separated list of expressions is a function call. The postfix expression denotes the called
function. The list of expressions specifies the arguments to the function.
4 An argument may be an expression of any complete object type. In preparing for the call
to a function, the arguments are evaluated, and each parameter is assigned the value of the
corresponding argument. 93)
5 If the expression that denotes the called function has type pointer to function returning an
object type, the function call expression has the same type as that object type, and has the
value determined as specified in 6.8.6.4. Otherwise, the function call has type void.
6 If the expression that denotes the called function has a type that does not include a
prototype, the integer promotions are performed on each argument, and arguments that
have type float are promoted to double. These are called the default argument
promotions. If the number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that includes a prototype, and
either the prototype ends with an ellipsis (, ...) or the types of the arguments after
promotion are not compatible with the types of the parameters, the behavior is undefined.
If the function is defined with a type that does not include a prototype, and the types of
the arguments after promotion are not compatible with those of the parameters after
promotion, the behavior is undefined, except for the following cases:
— one promoted type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in both types;
— both types are pointers to qualified or unqualified versions of a character type or
void.
7 If the expression that denotes the called function has a type that does include a prototype,
the arguments are implicitly converted, as if by assignment, to the types of the
corresponding parameters, taking the type of each parameter to be the unqualified version
of its declared type. The ellipsis notation in a function prototype declarator causes
argument type conversion to stop after the last declared parameter. The default argument
promotions are performed on trailing arguments.
8 No other conversions are performed implicitly; in particular, the number and types of
arguments are not compared with those of the parameters in a function definition that
does not include a function prototype declarator.
9 If the function is defined with a type that is not compatible with the type (of the
expression) pointed to by the expression that denotes the called function, the behavior is
undefined.
10 There is a sequence point after the evaluations of the function designator and the actual
arguments but before the actual call. Every evaluation in the calling function (including
other function calls) that is not otherwise specifically sequenced before or after the
execution of the body of the called function is indeterminately sequenced with respect to
the execution of the called function. 94)
11 Recursive function calls shall be permitted, both directly and indirectly through any chain
of other functions.
12 EXAMPLE
In the function call
C
1
(*pf[f1()]) (f2(), f3() + f4())
the functions f1, f2, f3, and f4 may be called in any order. All side effects have to be completed before
the function pointed to by pf[f1()] is called.
0
Somebody
2799 / 1610 / 251
Регистрация: 03.12.2007
Сообщений: 4,213
Завершенные тесты: 3
01.11.2014, 10:37 #36
Sequence points :-( Во времена C++14...
Тут уже не просто аргументы вычисляются "in any order", а вообще "Value computations and side effects associated with different argument expressions are unsequenced".
C++14 1.9, абзац 10
15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. —end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.
[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
—end example ]
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. —end note ] Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.9 Several contexts in C++ cause evaluation of a function call, even though no corresponding function call syntax appears in the translation unit. [ Example: Evaluation of a new expression invokes one or more allocation and constructor functions; see 5.3.4. For another example, invocation of a conversion function (12.3.2) can arise in contexts in which no function call syntax appears. —end example ] The sequencing constraints on the execution of the called function (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.
9) In other words, function executions do not interleave with each other.
0
ValeryS
Модератор
7223 / 5485 / 683
Регистрация: 14.02.2011
Сообщений: 18,554
09.12.2014, 11:18 #37
Использование комментариев в #define

Комментарии в #define
C++
1
2
#define VAR  5 // так делать не рекомендуется 
#define VAR  5 /* так можно */
Почему?

Потому что предкомпилятор видя VAR подставляет все что за ней, и вот такое выражение:
C++
1
int x = VAR;
превратит его в первом случае в
C++
1
int x = 5 // так делать не рекомендуется ;
потерялась точка с запятой, ушла в комментарии.

Во втором случае:
C++
1
int x = 5  /* так можно */;
это уже не страшно.
2
Tulosba
:)
Эксперт С++
4747 / 3241 / 497
Регистрация: 19.02.2013
Сообщений: 9,046
09.12.2014, 11:33 #38
Цитата Сообщение от ValeryS Посмотреть сообщение
превратит его в первом случае в
Ну, а проверить хотя бы? Комменты в препроцессор не попадают, на то они и комменты.
http://ideone.com/cKjv9L

Добавлено через 34 секунды
Цитата Сообщение от ValeryS Посмотреть сообщение
сам только что нарвался
Хотелось бы узнать: где?
1
ValeryS
Модератор
7223 / 5485 / 683
Регистрация: 14.02.2011
Сообщений: 18,554
09.12.2014, 11:49 #39
Цитата Сообщение от Tulosba Посмотреть сообщение
Ну, а проверить хотя бы?
разумеется пример утрированный, чтобы показать проблему
в таком примере отработает и то и другое
Цитата Сообщение от Tulosba Посмотреть сообщение
Хотелось бы узнать: где?
Keil библитеки STM, макросы вложенные один в другой и все вместе в третий в четвертый
решил прокомментировать (//) и поперли ошибки

но вот тебе пример, тоже утрированный, никто в здравом уме такое писать не будет
многострочный макрос

C++
1
2
#define VAR /*так можно прокоментировать*/  \
   /*и так можно прокомментировать */5
а как прокомментировать двумя слэшами?
0
Tulosba
:)
Эксперт С++
4747 / 3241 / 497
Регистрация: 19.02.2013
Сообщений: 9,046
09.12.2014, 12:20 #40
Цитата Сообщение от ValeryS Посмотреть сообщение
в таком примере отработает и то и другое
Значит, такой пример не состоятельный. И нужен другой для подтверждения проблемы.
Цитата Сообщение от ValeryS Посмотреть сообщение
а как прокомментировать двумя слэшами?
Максос по своей природе должен быть однострочным, для этого и добавляют "\" в конце каждой псевдостроки, когда он слишком длинный и хочется тем не менее избежать горизонтальной прокрутки. А учитывая то, что комментарий в виде двойных прямых слешей комментирует всё оставшееся до конца строки, то и располагать его надо в конце макроса, т.е. в твоем случае за пятеркой:
C++
1
2
#define VAR /*так можно прокоментировать*/  \
   /*и так можно прокомментировать */5 // comment
1
09.12.2014, 12:20
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
09.12.2014, 12:20

Найти ошибки в коде и исправить эти ошибки - C++
Судя по вываливающейся ошибки, идет двойное освобождение памяти. У самого не...

Распространенные ошибки
Оглавление Ошибки этапа компиляции (В процессе компиляции выдается либо...

безопасность и распространенные ошибки
Тут наткнулся на очень интересные тексты: ...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Опции темы

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