Модератор
Эксперт С++
13702 / 10905 / 6472
Регистрация: 18.12.2011
Сообщений: 29,112
1

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

21.09.2014, 17:33. Показов 394687. Ответов 73
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Скачать pdf:
Часто встречающиеся Ошибки.pdf
Оглавление

Ошибки этапа компиляции
Программа не компилируется или компилируется с предупреждениями.
  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"
  13. Компилятор не находит шаблонов описанных в других файлах проекта
Ошибки этапа выполнения
Программа прекращает работу с сообщением об ошибке
  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. Неправильное использование memset
  19. Ошибка при использовании счётчика цикла вне цикла
Алгоритмические ошибки
Ошибки, допущенные при разработке алгоритма
  1. Двойная перестановка строк или элементов массива
  2. Использование символа цифры вместо числа
Ошибки ввода-вывода
  1. Оставление символа '\n' в потоке ввода
  2. Ошибки при использовании функции scanf()
  3. При работе с fgetc чтение файла обрывается при достижении буквы 'я'
  4. При считывании из файла последний элемент читается дважды
  5. Запись сложных объектов в бинарный файл
Ошибки, связанные с отклонением от стандарта языка
  1. Неверный тип функции main()

Ошибки проектирования АТД (классов).
  1. Вызов виртуальной функции из конструктора
  2. Отсутствие точки с запятой после определения класса/структуры
  3. Неверный вызов конструктора базового класса из конструктора производного
  4. Неверный порядок при инициализации
  5. Нарушение правила ТРЕХ.
  6. Отсутствие виртуального деструктора в базовом классе
  7. Неправильное обращение к конструктору по умолчанию
  8. Не очевидные моменты с вызовом конструктора базового класса
  9. Неявно объявленный конструктор по умолчанию
  10. Перегрузка оператора >>
  11. Невозможно обратиться к protected члену, объявленному в базовом классе
Ошибки при использовании STL контейнеров
  1. Невалидные ссылки/указатели, при перемещении объектов
  2. Ошибки связанные с итераторами (кэширование размера контейнера)
  3. Ошибки связанные с итераторами (удаление элементов по итератору в циклах)
  4. Ошибки связанные с итераторами (префикс-постфиксные инкременты при удалении элементов в цикле)
36
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
21.09.2014, 17:33
Ответы с готовыми решениями:

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

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

Ошибки после компиляции на Visual Express 2012.Ошибки в теме
Добрый вечер ребят помогите пожалуйста.Программа написана на Visual Express 2012.Обясните что...

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

73
4311 / 1422 / 463
Регистрация: 16.12.2010
Сообщений: 2,939
Записей в блоге: 3
24.09.2014, 12:43 21
Author24 — интернет-сервис помощи студентам
Неверный аргумент тригонометрических функций.
Очень часто бывают ошибки вроде таких:
C++
1
x = sin( 90 ); // x = 0.893997. Имелось ввиду 90 градусов, а функция sin принимает в радианах.
Решение: перевести в градусы:
C++
1
x = sin( 90 * M_PI / 180 ); // x = 1
3
4311 / 1422 / 463
Регистрация: 16.12.2010
Сообщений: 2,939
Записей в блоге: 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
}
6
4311 / 1422 / 463
Регистрация: 16.12.2010
Сообщений: 2,939
Записей в блоге: 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
Модератор
Эксперт С++
13702 / 10905 / 6472
Регистрация: 18.12.2011
Сообщений: 29,112
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
Модератор
Эксперт CЭксперт С++
5286 / 2373 / 342
Регистрация: 20.02.2013
Сообщений: 5,773
Записей в блоге: 20
24.09.2014, 16:59 25
Забытое выделение тела цикла 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" );
}
1
4311 / 1422 / 463
Регистрация: 16.12.2010
Сообщений: 2,939
Записей в блоге: 3
24.09.2014, 20:31 26
Определение размера массива, переданного в качестве аргумента функции.
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 https://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
Неэпический
18105 / 10692 / 2062
Регистрация: 27.09.2012
Сообщений: 26,918
Записей в блоге: 1
26.09.2014, 22:19 27
Неверный порядок при инициализации
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.
9
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
29.09.2014, 21:27 28
Работа с локальной копией объекта, вместо работы с самим объектом


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;
}
10
Модератор
Эксперт С++
13702 / 10905 / 6472
Регистрация: 18.12.2011
Сообщений: 29,112
04.10.2014, 13:25  [ТС] 29
Нарушение правила ТРЕХ.
Правило трёх (также известное как «Закон Большой Тройки» или «Большая Тройка») — правило, гласящее, что если класс или структура определяет один из следующих методов, то они должны явным образом определить все три метода:
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
66
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;
        n=a.n;
        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;
        a3 = a1 + a2; // используется оператор присвоения
    return 0;
}
Примечание.
Не забывайте и о том, что и конструктор по умолчанию тоже в этом случае не создается.
Правда, при необходимости его вызова компилятор выдаст предупреждающее сообщение:
error C2512: A: нет подходящего конструктора по умолчанию
9
1373 / 596 / 199
Регистрация: 02.08.2011
Сообщений: 2,886
04.10.2014, 14:16 30
Интерпретация одиночного 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
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
06.10.2014, 16:38 31
Отсутствие виртуального деструктора в базовом классе.
Класс по умолчанию генерирует деструктор, но при этом он не является виртуальным. Отличие виртуального деструктора в том, что он предполагает освобождение ресурсов не только базового класса, но и всех производных. Ниже пример, явно демонстрирующий проблему:
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. в "правиле трёх" надо бы также описать данный случай как исключение из правил т.к. при необходимости явного объявления виртуального деструктора это вовсе не значит, что нужно описывать конструктор копий или оператор присваивания. ЕМНИМ, в оригинале говорится, что если в классе идёт работа с памятью, то необходимо явно объявить три вещи...
4
202 / 200 / 65
Регистрация: 06.10.2013
Сообщений: 552
31.10.2014, 23:02 32
Порядок вычисления аргументов при вызове функции.

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

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
Форумчанин
Эксперт CЭксперт С++
8216 / 5046 / 1437
Регистрация: 29.11.2010
Сообщений: 13,453
31.10.2014, 23:45 33
Сегодня наткнулся на подобное (код понятное дело упрощён и добавлены действия, эмулирующие работу):
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
202 / 200 / 65
Регистрация: 06.10.2013
Сообщений: 552
01.11.2014, 08:51 34
Цитата Сообщение от 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
2836 / 1645 / 254
Регистрация: 03.12.2007
Сообщений: 4,222
01.11.2014, 10:37 35
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
Модератор
Эксперт по электронике
8950 / 6716 / 921
Регистрация: 14.02.2011
Сообщений: 23,696
09.12.2014, 11:18 36
Использование комментариев в #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
:)
Эксперт С++
4773 / 3267 / 497
Регистрация: 19.02.2013
Сообщений: 9,046
09.12.2014, 11:33 37
Цитата Сообщение от ValeryS Посмотреть сообщение
превратит его в первом случае в
Ну, а проверить хотя бы? Комменты в препроцессор не попадают, на то они и комменты.
http://ideone.com/cKjv9L

Добавлено через 34 секунды
Цитата Сообщение от ValeryS Посмотреть сообщение
сам только что нарвался
Хотелось бы узнать: где?
1
Модератор
Эксперт по электронике
8950 / 6716 / 921
Регистрация: 14.02.2011
Сообщений: 23,696
09.12.2014, 11:49 38
Цитата Сообщение от Tulosba Посмотреть сообщение
Ну, а проверить хотя бы?
разумеется пример утрированный, чтобы показать проблему
в таком примере отработает и то и другое
Цитата Сообщение от Tulosba Посмотреть сообщение
Хотелось бы узнать: где?
Keil библитеки STM, макросы вложенные один в другой и все вместе в третий в четвертый
решил прокомментировать (//) и поперли ошибки

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

C++
1
2
#define VAR /*так можно прокоментировать*/  \
   /*и так можно прокомментировать */5
а как прокомментировать двумя слэшами?
0
:)
Эксперт С++
4773 / 3267 / 497
Регистрация: 19.02.2013
Сообщений: 9,046
09.12.2014, 12:20 39
Цитата Сообщение от ValeryS Посмотреть сообщение
в таком примере отработает и то и другое
Значит, такой пример не состоятельный. И нужен другой для подтверждения проблемы.
Цитата Сообщение от ValeryS Посмотреть сообщение
а как прокомментировать двумя слэшами?
Максос по своей природе должен быть однострочным, для этого и добавляют "\" в конце каждой псевдостроки, когда он слишком длинный и хочется тем не менее избежать горизонтальной прокрутки. А учитывая то, что комментарий в виде двойных прямых слешей комментирует всё оставшееся до конца строки, то и располагать его надо в конце макроса, т.е. в твоем случае за пятеркой:
C++
1
2
#define VAR /*так можно прокоментировать*/  \
   /*и так можно прокомментировать */5 // comment
1
Evg
Эксперт CАвтор FAQ
21280 / 8304 / 637
Регистрация: 30.03.2009
Сообщений: 22,660
Записей в блоге: 30
09.12.2014, 12:23 40
Что в каком порядке делается. Комментарии заменяются на пробелы до того, как выполняется запоминание макросов

Код
ISO/IEC 9899:1999 (E)
 
5.1.1.2 Translation phases
 
...
2. Each instance of a backslash character (\) immediately followed by a new-line
character is deleted, splicing physical source lines to form logical source lines.
Only the last backslash on any physical source line shall be eligible for being part
of such a splice. A source file that is not empty shall end in a new-line character,
which shall not be immediately preceded by a backslash character before any such
splicing takes place.

3. The source file is decomposed into preprocessing tokens 6) and sequences of
white-space characters (including comments). A source file shall not end in a
partial preprocessing token or in a partial comment. Each comment is replaced by
one space character. New-line characters are retained. Whether each nonempty
sequence of white-space characters other than new-line is retained or replaced by
one space character is implementation-defined.

4. Preprocessing directives are executed, macro invocations are expanded, and
_Pragma unary operator expressions are executed. If a character sequence that
matches the syntax of a universal character name is produced by token
concatenation (6.10.3.3), the behavior is undefined. A #include preprocessing
directive causes the named header or source file to be processed from phase 1
through phase 4, recursively. All preprocessing directives are then deleted.
4
09.12.2014, 12:23
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.12.2014, 12:23
Помогаю со студенческими работами здесь

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

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

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

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

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

Распространённые схемы мошейничества с вайбером
Использовался старый кнопочный телефон. Схема: - в одном из телеграмм чатов предложили...

Самые распространенные мужское и женское имена
Здрасти всем. Помогите справиться с решением задачи. Во входном файле записана следующая...


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

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

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru