Ошибка control reaches end of non void function

I’ve been getting strange compiler errors on this binary search algorithm. I get a warning that control reaches end of non-void function. What does this mean?

int binary(int val, int sorted[], int low, int high) {
    int mid = (low+high)/2;

    if(high < low)
        return -1;

    if(val < sorted[mid])
        return binary(val, sorted, low, mid-1);

    else if(val > sorted[mid])
        return binary(val, sorted, mid+1, high);

    else if(val == sorted[mid])
        return mid;
}

detly's user avatar

detly

29.2k18 gold badges92 silver badges149 bronze badges

asked May 30, 2011 at 1:14

tekknolagi's user avatar

tekknolagitekknolagi

10.6k24 gold badges74 silver badges119 bronze badges

The compiler cannot tell from that code if the function will ever reach the end and still return something. To make that clear, replace the last else if(...) with just else.

answered May 30, 2011 at 1:16

rid's user avatar

4

The compiler isn’t smart enough to know that <, >, and == are a «complete set». You can let it know that by removing the condition «if(val == sorted[mid])» — it’s redundant. Jut say «else return mid;«

answered May 30, 2011 at 1:17

Ernest Friedman-Hill's user avatar

4

Error image

If the function is non-void,it means it has to return something before reaching the end of function block[ _} ].So, when we give only if and else-if statements the compiler cannot tell from that code,that any of these statements will be evaluated to true and return something.Means, if all of the condition evaluates to false,then the control will reach the end of function,without returning something,which is wrong.

answered Mar 30, 2021 at 12:38

Srijoy_paul's user avatar

Always build with at least minimal optimization. With -O0, all analysis that the compiler could use to determine that execution cannot reach the end of the function has been disabled. This is why you’re seeing the warning. The only time you should ever use -O0 is for step-by-line debugging, which is usually not a good debugging approach anyway, but it’s what most people who got started with MSVC learned on…

answered May 30, 2011 at 2:26

R.. GitHub STOP HELPING ICE's user avatar

3

I had the same problem. My code below didn’t work, but when I replaced the last «if» with «else», it works. The error was: may reach end of non-void function.

int shifted(char key_letter)
  {
        if(isupper(key_letter))
        {
            return key_letter - 'A'; 
        }

        if(islower(key_letter)   //<----------- doesn't work, replace with else

        {                                            


            return key_letter - 'a'; 
        }

  }

answered Jan 6, 2014 at 5:23

Thuy's user avatar

ThuyThuy

1,4851 gold badge11 silver badges11 bronze badges

Make sure that your code is returning a value of given return-type irrespective of conditional statements

This code snippet was showing the same error

int search(char arr[], int start, int end, char value)
{
    int i;
    for(i=start; i<=end; i++)
    {
        if(arr[i] == value)
            return i;
    }
}

This is the working code after little changes

int search(char arr[], int start, int end, char value)
{
    int i;
    int index=-1;
    for(i=start; i<=end; i++)
    {
        if(arr[i] == value)
            index=i;
    }
    return index;
}

answered Oct 24, 2020 at 13:11

Suraj Shende's user avatar

It means it’s searching for a function that needs to be completed.

else if(val == sorted[mid]) return mid;

so, remove the if() part and modify the code or add an else() at the end which returns an int.

answered Nov 7, 2020 at 10:22

Jayanth sattineni's user avatar

Compiler by itself would not know that the conditions you have given are optimum .. meaning of this is you have covered all cases ..
So therefore it always wants a return statement … So either you can change last else if with else or just write return 0 after last else if ;`int binary(int val, int sorted[], int low, int high) {
int mid = (low+high)/2;

if(high < low)
    return -1;

if(val < sorted[mid])
    return binary(val, sorted, low, mid-1);

else if(val > sorted[mid])
    return binary(val, sorted, mid+1, high);

else if(val == sorted[mid])
    return mid;
return 0; }`

answered Feb 4, 2021 at 12:39

Pankaj Ramola's user avatar

If it’s «main» function just make sure to return 0 or change it from
int main()
to
void main()

answered Apr 26, 2022 at 8:55

Niv's user avatar

NivNiv

8301 gold badge7 silver badges21 bronze badges

Add the binary function to the end of the code:

int binary(int val, int sorted[], int low, int high)
{ /* ... */ return 1;}

If not one of the conditions is not met, the int function should return int.

user16217248's user avatar

user16217248

2,84717 gold badges16 silver badges35 bronze badges

answered May 13 at 7:23

Vladimir's user avatar

add to your code:

"#include < stdlib.h>"

return EXIT_SUCCESS;

at the end of main()

Ram Sharma's user avatar

Ram Sharma

8,6567 gold badges43 silver badges56 bronze badges

answered Jan 9, 2015 at 11:59

Donitsky's user avatar

2

Исследование одного неопределённого поведения

Время на прочтение
18 мин

Количество просмотров 6.7K

В статье исследуются возможные проявления неопределённого поведения, возникающего в c++ при завершении не-void функции без вызова return с подходящим значением. Статья носит больше научно-развлекательный характер, чем практический.

Кому не нравится весело скакать по граблям — проходим мимо, не задерживаемся.

Введение

Всем известно, что при разработке c++-кода следует не допускать неопределённого поведения.
Однако:

  • неопределённое поведение может казаться не достаточно опасным из-за абстрактности возможных последствий;
  • не всегда понятно, где грань.

Попробуем конкретизировать возможные проявления неопределённого поведения, возникающего в одном довольно простом случае — в не-void функции отсутствует return.

Для этого рассмотрим код, генерируемый наиболее популярными компиляторами в разных режимах оптимизации.

Исследования под Linux будут проводиться с помощью Compiler Explorer. Исследования под Windows и macOs X — на непосредственно доступном мне железе.

Все сборки будут делаться для x86-x64.

Никаких мер для усиления либо подавления предупреждений/ошибок компиляторов предприниматься не будет.

Будет много дизассемблированного кода. Его оформление, к сожалению, разношёрстное, т.к. приходится использовать несколько разных инструментов (хорошо хоть удалось добиться везде синтаксиса Intel). К дизассемблированному коду я буду давать в меру подробные комментарии, которые, однако, не избавляют от необходимости знания регистров процессора и принципов работы стека.

Читаем Стандарт

C++11 final draft n3797, C++14 final draft N3936:

6.6.3 The return statement

Flowing off the end of a function is equivalent to a return with no value; this results in undefined
behavior in a value-returning function.

Достижение конца функции эквивалентно выражению return без возвращаемого значения; для функции, у которой возвращаемое значение предусмотрено, это приводит к неопределённому поведению.

C++17 draft n4713

9.6.3 The return statement

Flowing off the end of a constructor, a destructor, or a function with a cv void return type is equivalent to a return with no operand. Otherwise, flowing off the end of a function other than main (6.8.3.1) results in undefined behavior.

Достижение конца конструктора, деструктора или функции с возвращаемым значением void (возможно, с квалификаторами const и volatile) эквивалентно выражению return без возвращаемого значения. Для всех других функций это приводит к неопределённому поведению (кроме функции main).

Что это значит на практике?

Если сигнатура функции предусматривает возвращаемое значение:

  • её выполнение должно завершаться выражением return с экземпляром подходящего типа;
  • иначе — неопределённое поведение;
  • неопределённое поведение начинается не с момента вызова такой функции и не с момента использования возвращённого значение, а с момента ненадлежащего завершения такой функции;
  • если функция содержит как корректные, так и некорректные пути выполнения — неопределённое поведение будет возникать только на некорректных путях;
  • рассматриваемое неопределённое поведение не затрагивает выполнение инструкций, содержащихся в теле функции.

Фраза про функцию main не является новшеством c++17 — в предыдущих версиях Стандарта аналогичное исключение было описано в разделе 3.6.1 Main function.

Пример 1 — bool

В c++ нет ни одного типа с состоянием более простым, чем bool. Вот с него и начнём.

#include <iostream>

bool bad() {};

int main()
{
    std::cout << bad();

    return 0;
}

MSVC выдаёт на такой пример ошибку компиляции C4716, поэтому для MSVC код придётся слегка усложнить, предоставив хотя бы один корректный путь выполнения:

#include <iostream>
#include <stdlib.h>

bool bad()
{
    if (rand() == 0) {
        return true;
    }
}

int main()
{
    std::cout << bad();

    return 0;
}

Компиляция:

Результаты выполнения:

Даже в этом простейшем примере четыре компилятора продемонстрировали как минимум три варианта проявления неопределённого поведения.

Идём разбираться, что же там эти компиляторы накомпилировали.

Linux x86-x64 Clang 10.0.0, -O0

image

Последняя инструкция в функции bad() — ud2.

Описание инструкции из Intel 64 and IA-32 Architectures Software Developer’s Manual:

UD2—Undefined Instruction
Generates an invalid opcode exception. This instruction is provided for software testing to explicitly generate an invalid opcode exception. The opcode for this instruction is reserved for this purpose.
Other than raising the invalid opcode exception, this instruction has no effect on processor state or memory.

Even though it is the execution of the UD2 instruction that causes the invalid opcode exception, the instruction pointer saved by delivery of the exception references the UD2 instruction (and not the following instruction).

This instruction’s operation is the same in non-64-bit modes and 64-bit mode.

Если кратко — это специальная инструкция для генерации исключения.

Надо обернуть вызов bad() в блок try… catch !?

Как бы не так. Это не c++-исключение.

Можно ли отловить ud2 в рантайме?
Под Windows для этого следует использовать __try, под Linux и macOs X — обработчик сигнала SIGILL.

Linux x86-x64 Clang 10.0.0, -O1, -O2

image

В результате оптимизации компилятор просто взял и выбросил как тело функции bad(), так и её вызов.

Linux x86-x64 gcc 9.3, -O0

image

Пояснения (в обратном порядке, т.к. в данном случае цепочку проще разбирать с конца):

5. Вызывается оператор вывода в stream для bool (строка 14);

4. В регистр edi помещается адрес std::cout — это первый аргумент оператора вывода в stream (строка 13);

3. В регистр esi помещается содержимое регистра eax — это второй аргумент оператора вывода в stream (строка 12);

2. Обнуляются три старших байта eax, значение al при этом не меняется (строка 11);

1. Вызывается функция bad() (строка 10);

0. Функция bad() должна поместить возвращаемое значение в регистр al.

Вместо этого в строке 4 — nop (No Operation, пустышка).

В консоль выводится один байт мусора из регистра al. Программа завершается штатно.

Linux x86-x64 gcc 9.3, -O1, -O2, -O3

image

Компилятор всё повыбрасывал в результате оптимизации.

macOs X Apple clang version 11.0.0, -O0

Функция main():

image

Путь булевского аргумента оператора вывода в поток (на сей раз в прямом порядке):

1. В регистр edx помещается содержимое регистра al (строка 8);

2. Зануляются все биты регистра edx, кроме младшего (строка 9);

3. В регистр rdi помещается указатель на std::cout — это первый аргумент оператора вывода в stream (строка 10);

4. В регистр esi помещается содержимое регистра edx — это второй аргумент оператора вывода в stream (строка 11);

5. Вызывается оператор вывода в stream для bool (строка 13);

Функция main ожидает получить результат выполнения функции bad() из регистра al.

Функция bad():

image

1. В регистр al помещается значение из следующего, ещё не выделенного, байта стека (строка 4);

2. Зануляются все биты регистра al, кроме младшего (строка 5);

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

Программа завершается штатно.

macOs X Apple clang version 11.0.0, -O1, -O2

image

Булевский аргумент оператора вывода в stream обнуляется (строка 5).

Вызов bad() выброшен при оптимизации.

Программа всегда выводит в консоль ноль и завершается штатно.

Windows MSVC 2019 16.5.4, усложнённый пример, /Od

image

Видно, что функция bad() должна предоставить возвращаемое значение в регистре al.

image

Значение, возвращённое функцией bad(), помещается сначала на стек, а потом в регистр edx для вывода в stream.

В консоль выводится один байт мусора из регистра al (если чуть точнее — то младший байт результата rand()). Программа завершается штатно.

Windows MSVC 2019 16.5.4, усложнённый пример, /O1, /O2

image

Компилятор принудительно заинлайнил вызов bad(). Функция main():

  • копирует в ebx один байт из памяти, находящейся по адресу [rsp+30h];
  • в случае, если rand() вернул ноль, копирует единицу из ecx в ebx (строка 11);
  • копирует это же значение в dl (точнее, его младший байт) (строка 13);
  • вызывает функцию вывода в stream, осуществляющую вывод значения dl (строка 14).

В stream выводится один байт мусора из оперативной памяти (из адреса rsp+30h).

Вывод по примеру 1

Результаты рассмотрения листингов дизассемблера приведены в таблице:

Как оказалось, компиляторы продемонстрировали не 3, а целых 6 вариантов неопределённого поведения — просто до рассмотрения листингов дизассемблера мы не могли различить некоторые из них.

Пример 1a — управление неопределённым поведением

Попробуем немного порулить неопределённым поведением — повлиять на значение, возвращаемое функцией bad().

Это можно проделать только с теми компиляторами, которые выводят мусор.
Для этого надо подсовывать желаемые значения в те места, из которых компиляторы их будут брать.

Linux x86-x64 gcc 9.3, -O0

Пустая функция bad() не модифицирует значение регистра al, как от неё требует вызывающий код. Таким образом, если мы разместим в al определённое значение до вызова bad(), то ожидаем увидеть именно это значение в качестве результата выполнения bad().

Очевидно, что это можно сделать с помощью вызова любой другой функции, возвращающей bool. Но также это можно сделать с помощью функции, возвращающей, например, unsinged char.

Полный код примера

#include <iostream>

bool bad() {}

bool goodTrue()
{
    return rand();
}

bool goodFalse()
{
    return !goodTrue();
}

unsigned char goodChar(unsigned char ch)
{
    return ch;
}

int main()
{
    goodTrue();
    std::cout << bad() << std::endl;

    goodChar(85);
    std::cout << bad() << std::endl;

    goodFalse();
    std::cout << bad() << std::endl;

    goodChar(240);
    std::cout << bad() << std::endl;

    return 0;
}

Вывод в консоль:

1
85
0
240

Windows MSVC 2019 16.5.4, /Od

В примере для MSVC функция bad() возвращает младший байт результата rand().

Без модификации функции bad() внешний код может повлиять на возвращаемое ею значение, изменяя результат rand().

Полный код примера

#include <iostream>
#include <stdlib.h>

void control(unsigned char value)
{
    uint32_t count = 0;
    srand(0);
    while ((rand() & 0xff) != value) {
        ++count;
    }

    srand(0);
    for (uint32_t i = 0; i < count; ++i) {
        rand();
    }
}

bool bad()
{
    if (rand() == 0) {
        return true;
    }
}

int main()
{
    control(1);
    std::cout << bad() << std::endl;

    control(85);
    std::cout << bad() << std::endl;

    control(0);
    std::cout << bad() << std::endl;

    control(240);
    std::cout << bad() << std::endl;

    return 0;
}

Вывод в консоль:

1
85
0
240

Windows MSVC 2019 16.5.4, /O1, /O2

Чтобы повлиять не значение, «возвращаемое» функцией bad(), достаточно создать одну стековую переменную. Чтоб запись в неё не была выброшена при оптимизации, следует пометить её как volatile.

Полный код примера

#include <iostream>
#include <stdlib.h>

bool bad()
{
  if (rand() == 0) {
    return true;
  }
}

int main()
{
  volatile unsigned char ch = 1;
  std::cout << bad() << std::endl;

  ch = 85;
  std::cout << bad() << std::endl;

  ch = 0;
  std::cout << bad() << std::endl;

  ch = 240;
  std::cout << bad() << std::endl;

  return 0;
}

Вывод в консоль:

1
85
0
240

macOs X Apple clang version 11.0.0, -O0

Надо перед вызовом bad() вписать определённое значение в ту ячейку памяти, которая будет на единицу младше вершины стека в момент вызова bad().

Полный код примера

#include <iostream>

bool bad() {}

void putToStack(uint8_t value)
{
    uint8_t memory[1]{value};
}

int main()
{
    putToStack(20);
    std::cout << bad() << std::endl;

    putToStack(55);
    std::cout << bad() << std::endl;

    putToStack(0xfe);
    std::cout << bad() << std::endl;

    putToStack(11);
    std::cout << bad() << std::endl;

    return 0;
}

Пример предназначен для компиляции с опцией -O0, так что не стоит беспокоиться о сохранности переменной memory. Она не будет выброшена при оптимизации даже несмотря на то, что нигде не используется.

При этом переменная memory должна быть не просто единичным значением, а массивом — иначе компилятор располагает её в регистр процессора, а не на стек, как нам надо.

Пример не является универсальным, т.к. вообще компиляторы могут выделять на стеке больше памяти, чем необходимо для пользовательских переменных — тогда функция putToStack в текущем виде будет промахиваться.

Вывод в консоль:

0
1
0
1

Вроде получилось: удаётся менять выдачу функции bad(), и при этом учитывается только младший бит.

Вывод по примеру 1a

Пример позволил убедиться в корректности трактовки листингов дизассемблера.

Пример 1b — сломанный bool

Ну подууууумаешь, в консоль выведется «41» вместо «1»… Разве это опасно?

Проверять будем на двух компиляторах, предоставивших целый байт мусора.

Windows MSVC 2019 16.5.4, /Od

Полный код примера

#include <iostream>
#include <stdlib.h>
#include <set>
#include <unordered_set>

bool bad()
{
    if (rand() == 0) {
        return true;
    }
}

int main()
{
    bool badBool1 = bad();
    bool badBool2 = bad();

    std::cout << "badBool1: " << badBool1 << std::endl;
    std::cout << "badBool2: " << badBool2 << std::endl;

    if (badBool1) {
      std::cout << "if (badBool1): true" << std::endl;
    } else {
      std::cout << "if (badBool1): false" << std::endl;
    }
    if (!badBool1) {
      std::cout << "if (!badBool1): true" << std::endl;
    } else {
      std::cout << "if (!badBool1): false" << std::endl;
    }

    std::cout << "(badBool1 == true || badBool1 == false || badBool1 == badBool2): "
              << std::boolalpha << (badBool1 == true || badBool1 == false || badBool1 == badBool2)
              << std::endl;
    std::cout << "std::set<bool>{badBool1, badBool2, true, false}.size(): "
              << std::set<bool>{badBool1, badBool2, true, false}.size()
              << std::endl;
    std::cout << "std::unordered_set<bool>{badBool1, badBool2, true, false}.size(): "
              << std::unordered_set<bool>{badBool1, badBool2, true, false}.size()
              << std::endl;

    return 0;
}

Вывод в консоль:

badBool1: 41
badBool2: 35
if (badBool1): true
if (!badBool1): false
(badBool1 == true || badBool1 == false || badBool1 == badBool2): false
std::set<bool>{badBool1, badBool2, true, false}.size(): 4
std::unordered_set<bool>{badBool1, badBool2, true, false}.size(): 4

Неопределённое поведение привело к возникновению булевской переменной, которая ломает как минимум:

  • операторы сравнения булевских значений;
  • хеш-функцию булевского значения.

Windows MSVC 2019 16.5.4, /O1, /O2

Полный код примера

#include <iostream>
#include <stdlib.h>
#include <set>
#include <unordered_set>

bool bad()
{
  if (rand() == 0) {
    return true;
  }
}

int main()
{
  volatile unsigned char ch = 213;
  bool badBool1 = bad();
  ch = 137;
  bool badBool2 = bad();

  std::cout << "badBool1: " << badBool1 << std::endl;
  std::cout << "badBool2: " << badBool2 << std::endl;

  if (badBool1) {
    std::cout << "if (badBool1): true" << std::endl;
  }
  else {
    std::cout << "if (badBool1): false" << std::endl;
  }
  if (!badBool1) {
    std::cout << "if (!badBool1): true" << std::endl;
  }
  else {
    std::cout << "if (!badBool1): false" << std::endl;
  }

  std::cout << "(badBool1 == true || badBool1 == false || badBool1 == badBool2): "
    << std::boolalpha << (badBool1 == true || badBool1 == false || badBool1 == badBool2)
    << std::endl;
  std::cout << "std::set<bool>{badBool1, badBool2, true, false}.size(): "
    << std::set<bool>{badBool1, badBool2, true, false}.size()
    << std::endl;
  std::cout << "std::unordered_set<bool>{badBool1, badBool2, true, false}.size(): "
    << std::unordered_set<bool>{badBool1, badBool2, true, false}.size()
    << std::endl;

  return 0;
}

Вывод в консоль:

badBool1: 213
badBool2: 137
if (badBool1): true
if (!badBool1): false
(badBool1 == true || badBool1 == false || badBool1 == badBool2): false
std::set<bool>{badBool1, badBool2, true, false}.size(): 4
std::unordered_set<bool>{badBool1, badBool2, true, false}.size(): 4

Работа с испорченной булевской переменной не изменилась при включении оптимизации.

Linux x86-x64 gcc 9.3, -O0

Полный код примера

#include <iostream>
#include <stdlib.h>
#include <set>
#include <unordered_set>

bool bad()
{
}

unsigned char goodChar(unsigned char ch)
{
  return ch;
}

int main()
{
  goodChar(213);
  bool badBool1 = bad();

  goodChar(137);
  bool badBool2 = bad();

  std::cout << "badBool1: " << badBool1 << std::endl;
  std::cout << "badBool2: " << badBool2 << std::endl;

  if (badBool1) {
    std::cout << "if (badBool1): true" << std::endl;
  }
  else {
    std::cout << "if (badBool1): false" << std::endl;
  }
  if (!badBool1) {
    std::cout << "if (!badBool1): true" << std::endl;
  }
  else {
    std::cout << "if (!badBool1): false" << std::endl;
  }

  std::cout << "(badBool1 == true || badBool1 == false || badBool1 == badBool2): "
    << std::boolalpha << (badBool1 == true || badBool1 == false || badBool1 == badBool2)
    << std::endl;
  std::cout << "std::set<bool>{badBool1, badBool2, true, false}.size(): "
    << std::set<bool>{badBool1, badBool2, true, false}.size()
    << std::endl;
  std::cout << "std::unordered_set<bool>{badBool1, badBool2, true, false}.size(): "
    << std::unordered_set<bool>{badBool1, badBool2, true, false}.size()
    << std::endl;

  return 0;
}

Вывод в консоль:

badBool1: 213
badBool2: 137
if (badBool1): true
if (!badBool1): true
(badBool1 == true || badBool1 == false || badBool1 == badBool2): false
std::set<bool>{badBool1, badBool2, true, false}.size(): 4
std::unordered_set<bool>{badBool1, badBool2, true, false}.size(): 4

По сравнению с MSVC, в gcc добавилась ещё и некорректная работа оператора not.

Вывод по примеру 1b

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

Почему так произошло?

Потому что некоторые операции с булевскими переменными реализованы в предположении, что true — это строго единица.

В дизассемблере этот вопрос рассматривать не будем — статья и так получилась объёмной.

В очередной раз уточним таблицу с поведением компиляторов:

Четыре компилятора дали 7 различных проявлений неопределённого поведения.

Пример 2 — struct

Возьмём пример чуть посложнее:

#include <iostream>
#include <stdlib.h>

struct Test
{
    Test(uint64_t v)
        : value(v)
    {
        std::cout << "Test::Test(" << v << ")" << std::endl;
    }
    ~Test()
    {
        std::cout << "Test::~Test()" << std::endl;
    }

    uint64_t value;
};

Test bad(int v)
{
    if (v == 0) {
        return {42};
    } else if (v == 1) {
        return {142};
    }
}

int main()
{
    const auto rnd = rand();
    std::cout << "rnd: " << rnd << std::endl;

    std::cout << bad(rnd).value << std::endl;

    return 0;
}

Структура Test требует для конструирования один параметр типа int. Из её конструктора и деструктора производится вывод диагностических сообщений. Функция bad(int) имеет два корректных пути выполнения, ни один из которых не будет реализован при единственном вызове.

На этот раз — сначала таблица, потом разбор дизассемблера по непонятным пунктам.

Опять мы видим множество вариантов: кроме уже известного ud2 есть ещё как минимум 4 разных поведения.

Весьма интересно обращение компиляторов с конструктором:

  • в одних случаях выполнение продолжилось без вызова конструктора — в этом случае объект оказался в каком-то случайном состоянии;
  • в других случаях произошёл вызов конструктора, не предусмотренный на пути выполнения, что довольно странно.

Linux x86-x64 Clang 10.0.0, -O1, -O2

image

В коде производится только одно сравнение (строка 14), и присутствует только один условный переход (строка 15). Компилятор проигнорировал второе сравнение и второй условный переход.
Это наводит на подозрение, что неопределённое поведение началось раньше, чем предписывает Стандарт.

Но проверка условия второго if не содержит побочных эффектов, и логика компилятора сработала следующим образом:

  • если второе условие окажется верным — надо вызвать конструктор Test с аргументом 142;
  • если второе условие окажется не верным — произойдёт выход из функции без возрата значения, что означает неопределённое поведение, при котором компилятор может сделать всё, что угодно. В том числе — вызвать тот же конструктор с тем же аргументом;
  • проверка является лишней, вызов конструктора Test с аргументом 142 можно производить без проверки условия.

Посмотрим, что произойдёт, если вторая проверка будет содержать условие с побочными эффектами:

Test bad(int v)
{
    if (v == 0) {
        return {42};
    } else if (v == rand()) {
        return {142};
    }
}

Полный код

#include <iostream>
#include <stdlib.h>

struct Test
{
    Test(uint64_t v)
        : value(v)
    {
        std::cout << "Test::Test(" << v << ")" << std::endl;
    }
    ~Test()
    {
        std::cout << "Test::~Test()" << std::endl;
    }

    uint64_t value;
};

Test bad(int v)
{
    if (v == 0) {
        return {42};
    } else if (v == rand()) {
        return {142};
    }
}

int main()
{
    const auto rnd = rand();
    std::cout << "rnd: " << rnd << std::endl;

    std::cout << bad(rnd).value << std::endl;

    return 0;
}

image

Компилятор честно воспроизвёл все положенные побочные эффекты, вызвав rand() (строка 16), чем развеял сомнения о неподобающе раннем начале неопределённого поведения.

Windows MSVC 2019 16.5.4, /Od /RTCs

Опция /RTCs включает stack frame run-time error checking. Эта опция доступна только в debug-сборке. Рассмотрим дизассемблированный код участка main():

image

Перед вызовом bad(int) (строка 4) производится подготовка аргументов — в регистр edx копируется значение переменной rnd (строка 2), и в регистр rcx загружается эффективный адрес какой-то локальной переменной, расположенной по адресу rsp+28h (строка 3).

Предположительно, rsp+28 — адрес временной переменной, хранящей результат вызова bad(int).

Это предположение подтверждается строками 19 и 20 — эффективный адрес этой же переменной загружается в rcx, после чего вызывается деструктор.

Однако в интервале строк 4 — 18 к этой переменной нет обращения, несмотря на вывод в stream значения её поля данных.

Как мы видели из прошлых листингов MSVC, аргумент для оператора вывода в поток следует ожидать в регистре rdx. В регистр rdx попадает результат разыменования адреса, находящегося в rax (строка 9).

Таким образом, вызывающий код ожидает от bad(int):

  • заполнения переменной, адрес которой передан через регистр rcx (тут мы видим RVO в действии);
  • возврат адреса этой переменной через регистр rax.

Переходим к рассмотрению листинга bad(int):

image

  • в eax заносится значение 0xCCCCCCCC, которое мы видели в сообщении Access violation (строка 9) (обратите внимание — только 4 байта, в то время как в сообщении AccessViolation адрес состоит из 8 байт);
  • вызывается команда rep stos, осуществляющая 0xC циклов записи содержимого eax в память начиная с адреса rdi (строка 10). Это 48 байтов — ровно столько, сколько выделено на стеке в строке 6;
  • на корректных путях выполнения в rax заносится значение из rsp+40h (строки 23, 36);
  • значение регистра rcx (через который main() передал адрес назначения) помещается на стек по адресу rsp+8 (строка 4);
  • в стек впихивается rdi, что приводит к уменьшению rsp на 8 (строка 5);
  • на стеке выделяется 30h байт путём уменьшению rsp (строка 6).

Таким образом, rsp+8 в строке 4 и rsp+40h в остальной части кода — одно и то же значение.
Код довольно запутанный, т.к. в нём не применяется rbp.

В сообщении Access Violation есть целых две случайности:

  • нули в старшей части адреса — там мог быть любой мусор;
  • адрес случайно оказался некорректным.

Судя по всему, опция /RTCs включила затирание стека определёнными ненулевыми значениями, а сообщение Access Violation — лишь случайный побочный эффект.

Посмотрим, чем отличается код со включённой опцией /RTCs от кода без неё.

image

Код участков main() отличается только адресами локальных переменных на стеке.

image

(для наглядности я разместил рядом два варианта функции bad(int) — с /RTCs и без)
Без /RTCs исчезла инструкция rep stos и подготовка аргументов для неё в начале функции.

Пример 2a

Снова попробуем поуправлять неопределённым поведением. На этот раз только для одного компилятора.

Windows MSVC 2019 16.5.4, /Od /RTCs

С опцией /RTCs компилятор вставляет в начало функции bad(int) код, заполняющий младшую половину rax фиксированным значеним, что может приводить к Access violation.

Чтобы изменить это поведение, достаточно заполнить rax каким-либо корректным адресом.
Этого можно добиться очень простой модификацией: добавить в тело bad(int) вывод чего-нибудь в std::cout.

Полный код примера

#include <iostream>
#include <stdlib.h>

struct Test
{
    Test(uint64_t v)
        : value(v)
    {
        std::cout << "Test::Test(" << v << ")" << std::endl;
    }
    ~Test()
    {
        std::cout << "Test::~Test()" << std::endl;
    }

    uint64_t value;
};

Test bad(int v)
{
  std::cout << "rnd: " << v << std::endl;
  
  if (v == 0) {
        return {42};
    } else if (v == 1) {
        return {142};
    }
}

int main()
{
    const auto rnd = rand();

    std::cout << bad(rnd).value << std::endl;

    return 0;
}

rnd: 41
8791039331928
Test::~Test()

operator<< возвращает ссылку на stream, что реализуется как размещение адреса std::cout в rax. Адрес корректный, его можно разыменовывать. Access violation предотвращён.

Вывод

На простейших примерах нам удалось:

  • собрать порядка 10 различных вариантов проявления неопределённого поведения;
  • в подробностях узнать, как именно эти варианты будут исполняться.

Все компиляторы продемонстрировали чёткое следование Стандарту — ни в одном примере неопределённое поведение не началось раньше положенного. Но и в фантазии разработчикам компиляторов не откажешь.

Зачастую проявление зависит от тонких нюансов: стоит добавить или убрать одну, казалось бы, не относящуюся к делу строку кода — и поведение программы существенно меняется.

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

maytreya

0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

1

21.02.2021, 11:22. Показов 30454. Ответов 19

Метки нет (Все метки)


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

Добрый день, коллеги. Продолжаю решать задачки с целью подготовки к экзамену. Задачу про НОД решил попробовать двумя способами. И если первый вариант я подсмотрел теорию за .. какой то там класс, то второй — это, так сказать, мысли мои и не самые удачные, видимо. Короче …. при запуске второго варианта VS выдает » cpp:72:1: warning: control reaches end of non-void function [-Wreturn-type]
72 | } «. Ответ при этом не соответствует действительности. Помогите разобраться и набраться опыта. Спасибо.

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
67
68
69
70
71
72
#include <iostream>
#include <cmath>
#include <ctime>
using namespace std;
 
//Задание 1   ***********************
//вариант 1
int nod(int n, int m);
//вариант 2
int delitel (int m, int n, int x, int i);
 
 
//mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
//mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
int main()
{
    
 
//*******************************************************
 
//Задание 1
 
  int a,b,k;
  int x=0;
  int i=0;
  cout<< "enter two numbers a and b: n";
  cin>> a;
  cout<<endl;
  cin>> b;
 
  k=delitel(a,b,x,i);
 
        cout<<"the greatest divisor of the numbers a and b: ";
        cout<<k; 
        cout<<endl;
 
 
return 0;
}
//out_main_out_main_out_main_out_main_out_main_out_main_out_main_out_main_
//out_main_out_main_out_main_out_main_out_main_out_main_out_main_out_main_
 
//Задание 1   ***********************
 
int nod(int n, int m)
{
// Алгоритм Евклида
    if (n<m){
    swap(n,m);
    }
    if (m == 0) 
        return n; 
    else 
        return nod(m, n % m);   
}
 
 
//вариант 2
int delitel (int m, int n, int x, int i)
{
  // n=x+i; 
if (n>m)
swap (n,m);
if(x!=0)
{
  if (m%x==0 && n%x==0)
  {
  return x;
  }
delitel (m,n,x-1, i+1);
}
}

Добавлено через 3 минуты
Переменную i удалил не везде. Не обращайте внимание)).



0



Диссидент

Эксперт C

27505 / 17194 / 3785

Регистрация: 24.12.2010

Сообщений: 38,733

21.02.2021, 11:50

2

Цитата
Сообщение от maytreya
Посмотреть сообщение

cpp:72:1: warning: control reaches end of non-void function [-Wreturn-type]

Ерунда. Предупреждение. Функция должна что-то возвращать. Поставь в строчку 71 return 0; Или
void delitel (int m, int n, int x, int i)

Добавлено через 2 минуты
На работу программы влияния оказывать не должно.
Хотя надо стремиться к «чистой» трансляции, без варнингов.



1



0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 13:10

 [ТС]

3

………. delitel (m,n,x-1, i+1);
}
return 0;
}
Вот так? но тогда ответ k=0 всегда.
Вообще то функция действительно работает, но ответ дает не верный. Например при вводе 1 и 2 ответ 6422240.



0



oleg-m1973

6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 13:46

4

Цитата
Сообщение от maytreya
Посмотреть сообщение

Вот так? но тогда ответ k=0 всегда.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int delitel (int m, int n, int x, int i)
{
    // n=x+i; 
    if (n>m)
        swap (n,m);
 
    if(x!=0)
    {
        if (m%x==0 && n%x==0)
            return x;
 
 
        return delitel (m,n,x-1, i+1);
    }
    return 0;
}



0



Диссидент

Эксперт C

27505 / 17194 / 3785

Регистрация: 24.12.2010

Сообщений: 38,733

21.02.2021, 15:18

5

maytreya, все мои замечания касается только твоего

Цитата
Сообщение от maytreya
Посмотреть сообщение

cpp:72:1: warning: control reaches end of non-void function [-Wreturn-type]

Про ответ ничего не знаю, это отдельно смотреть…



0



maytreya

0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 23:27

 [ТС]

6

Спасибо за подсказку. Нашел еще одну логическую нестыковку, попробую добить до рабочего состояния)).

Добавлено через 3 часа 5 минут
Еще раз спасибо всем кто откликнулся… Второй вариант стал «рабочим»… считает верно правда от варнинга не избавился. Конечно 1-й вариант гораздо изящней, но для тренировки 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
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
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <cmath>
#include <ctime>
using namespace std;
 
//Задание 1   ***********************
//вариант 1
int nod(int n, int m);
//вариант 2
int delitel (int m, int n, int x);
 
 
//mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
//mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
int main()
{
    
 
//*******************************************************
 
//Задание 1
 
  int a,b,k,x;
  cout<< "enter two numbers a and b: n";
  cin>> a;
  cout<<endl;
  cin>> b;
  //вариант 1
  //k=nod(a,b);
 
  //вариант 2
  if (b>a)
    x=a;
  else
    x=b; 
  k=delitel(a,b,x);
 
        cout<<"the greatest divisor of the numbers a and b: ";
        cout<<k; 
        cout<<endl;
 
 
return 0;
}
//out_main_out_main_out_main_out_main_out_main_out_main_out_main_out_main_
//out_main_out_main_out_main_out_main_out_main_out_main_out_main_out_main_
 
//Задание 1   ***********************
 //вариант 1
int nod(int n, int m)
{
// Алгоритм Евклида
    if (n<m){
    swap(n,m);
    }
    if (m == 0) 
        return n; 
    else 
        return nod(m, n % m);   
}
 
                      //считает верно, но постоянно выдает предупреждение. Не могу понять почему.
//вариант 2                    Untitled-1.cpp:71:1: warning: control reaches end of non-void function [-Wreturn-type]
                            // 71 | }
                            //    | ^
int delitel (int m, int n, int x)
{
if (n>m)
swap (n,m);
if(x!=0)
{
  if (m%x==0 && n%x==0)
  {
  return x;
  }
delitel (m,n,x-1);
}
}



0



6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 23:29

7

Цитата
Сообщение от maytreya
Посмотреть сообщение

//считает верно, но постоянно выдает предупреждение. Не могу понять почему.
//вариант 2                    Untitled-1.cpp:71:1: warning: control reaches end of non-void function [-Wreturn-type]
                            // 71 | }
                            //    | ^

Я ж тебе вроде показал, что нужно сделать

Цитата
Сообщение от oleg-m1973
Посмотреть сообщение

int delitel (int m, int n, int x, int i)
{
    // n=x+i;
    if (n>m)
        swap (n,m);
if(x!=0)
    {
        if (m%x==0 && n%x==0)
            return x;
return delitel (m,n,x-1, i+1);
    }
    return 0;
}



0



0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 23:33

 [ТС]

8

Кстати, не рискнул выделить в отдельную тему. Подскажите где можно узнать о правильном написании кода, а то мне тут давеча намекнули, что мол пишу коряво)))

Добавлено через 2 минуты

Цитата
Сообщение от oleg-m1973
Посмотреть сообщение

Я ж тебе вроде показал, что нужно сделать

Я так делал… в результате всегда 0(ноль)



0



2434 / 1834 / 404

Регистрация: 15.12.2013

Сообщений: 8,202

21.02.2021, 23:33

9

Больше пишите и больше читайте, вот и весь секрет.



0



6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 23:34

10

Цитата
Сообщение от maytreya
Посмотреть сообщение

Я так делал… в результате всегда 0(ноль)

Покажи, как делал



0



maytreya

0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 23:36

 [ТС]

11

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
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <iostream>
#include <cmath>
#include <ctime>
using namespace std;
 
//Задание 1   ***********************
//вариант 1
int nod(int n, int m);
//вариант 2
int delitel (int m, int n, int x);
 
 
//mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
//mainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmainmain
int main()
{
    
 
//*******************************************************
 
//Задание 1
 
  int a,b,k,x;
  cout<< "enter two numbers a and b: n";
  cin>> a;
  cout<<endl;
  cin>> b;
  //вариант 1
  //k=nod(a,b);
 
  //вариант 2
  if (b>a)
    x=a;
  else
    x=b; 
  k=delitel(a,b,x);
 
        cout<<"the greatest divisor of the numbers a and b: ";
        cout<<k; 
        cout<<endl;
 
 
return 0;
}
//out_main_out_main_out_main_out_main_out_main_out_main_out_main_out_main_
//out_main_out_main_out_main_out_main_out_main_out_main_out_main_out_main_
 
//Задание 1   ***********************
 //вариант 1
int nod(int n, int m)
{
// Алгоритм Евклида
    if (n<m){
    swap(n,m);
    }
    if (m == 0) 
        return n; 
    else 
        return nod(m, n % m);   
}
 
                      //считает верно, но постоянно выдает предупреждение. Не могу понять почему.
//вариант 2                    Untitled-1.cpp:71:1: warning: control reaches end of non-void function [-Wreturn-type]
                            // 71 | }
                            //    | ^
int delitel (int m, int n, int x)
{
if (n>m)
swap (n,m);
if(x!=0)
{
  if (m%x==0 && n%x==0)
  {
  return x;
  }
delitel (m,n,x-1);
}
return 0;
}



0



6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 23:38

12

Цитата
Сообщение от maytreya
Посмотреть сообщение

if (m%x==0 && n%x==0)
  {
  return x;
  }
return delitel (m,n,x-1);
}
return 0;
}

Твоя проблема не в том, что ты коряво пишешь, а в том, что не слушаешь, то что тебе говорят. Корявый код — это следствие.



0



0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 23:39

 [ТС]

13

Цитата
Сообщение от oleg-m1973
Посмотреть сообщение

Покажи, как делал

78 строчка



0



6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 23:40

14

Жирным пометил, на случай, если ты цвета не различаешь

Добавлено через 56 секунд

Цитата
Сообщение от maytreya
Посмотреть сообщение

78 строчка

return delitel (m,n,x-1) сделал?



0



0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 23:44

 [ТС]

15

Цитата
Сообщение от oleg-m1973
Посмотреть сообщение

return delitel (m,n,x-1) сделал?

да… опять ноль

Добавлено через 2 минуты
вот когда убираю «return 0; » тогда считает правильно, но опять предупреждение



0



oleg-m1973

6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 23:46

16

Цитата
Сообщение от maytreya
Посмотреть сообщение

да… опять ноль

Да ладно

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int delitel(int m, int n, int x)
{
    if (n>m)
        std::swap(n,m);
    
    if(x!=0)
    {
        if (m%x==0 && n%x==0)
        {
            return x;
        }
        return delitel(m,n,x-1);
    }
    return 0;
}
int main()
{
    std::cout << delitel(10, 20, 2) << std::endl;
}

Добавлено через 1 минуту

Цитата
Сообщение от maytreya
Посмотреть сообщение

вот когда убираю «return 0; » тогда считает правильно, но опять предупреждение

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



0



maytreya

0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

21.02.2021, 23:49

 [ТС]

17

Весь код не передаю только концовку

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int delitel (int m, int n, int x)
{
if (n>m)
swap (n,m);
if(x!=0)
{
  if (m%x==0 && n%x==0)
  {
  return x;
  }
return delitel (m,n,x-1);
}
 
}

Добавлено через 57 секунд
return 0; если на 13 строке, то ответ ноль



0



6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

21.02.2021, 23:51

18

Цитата
Сообщение от maytreya
Посмотреть сообщение

Весь код не передаю только концовку

return 0 в конце добавь и всё будет правильно.

Добавлено через 23 секунды

Цитата
Сообщение от maytreya
Посмотреть сообщение

return 0; если на 13 строке, то ответ ноль

При каких параметрах?

Добавлено через 1 минуту
Сделай return 1. Это когда общего делителя нет



1



0 / 0 / 0

Регистрация: 05.02.2021

Сообщений: 28

22.02.2021, 00:00

 [ТС]

19

Цитата
Сообщение от oleg-m1973
Посмотреть сообщение

Сделай return 1. Это когда общего делителя нет

ух ты… получилось. теперь бы понять почему….Спасибо огромное … буду разбираться)))

Добавлено через 4 минуты
еще раз спасибо… все понял



0



6577 / 4562 / 1843

Регистрация: 07.05.2019

Сообщений: 13,726

22.02.2021, 00:06

20

Цитата
Сообщение от maytreya
Посмотреть сообщение

еще раз спасибо… все понял

Для спасибо есть специальная кнопка



3



Hello everyone, In this article, we are going to discuss a common problem faced by beginners and also experienced programmers. Often when we run into the warning saying warning: control reaches end of non-void function. Generally, we ignore this error because most of the time as the program still runs the same even if this warning is coming.

Let’s first see a simple example then this kind of error can occur.

#include <bits/stdc++.h>

using namespace std;

int fun(){

    if(false)

        return 0;

}         // Error here

int main() {

    fun();

    return 0;

}

If we run this c++ code it works fine and terminates but we get an error at the 8th line saying control reaches end of non-void function.

To understand this error we first have to understand the working of the compiler a bit in detail. There are two important kinds of error which occurs while running a program.

Compile-time error: Error which can be identified by the compiler without running the program. Generally, these are syntax error which is identified without running the program.

Run-time error: Error which occurs when the program syntax is correct but there is some problem while the program is run. Generally invalid memory access, infinite loops fall under runtime error.

Every function has a return type which states the type of value the function will be returning. If the function is not going to return any value it is given a void return type, Any other return valued function is a non-void function.

We get the above error when the non-void function is not returning any value. 

In the above example the if condition is always false and therefore it will reach the end of the function without returning anything and that’s why we are getting the warning message. Control in the warning message means the flow of the program.

Now coming to the runtime and compile-time error part, the message which we are getting here is identified at compile time using just the syntax of the program and the program is not actually run to check if it is reaching the end of any non-void function.

Let’s take the below example:

#include <bits/stdc++.h>

using namespace std;

int fun(int x){

    if(x < 5)

        return 0;

}   // line 8

int main() {

    fun(4);

    return 0;

}

In this example, we are always sure that if we run the program it will return the values 0, but still we will get the same warning message because the compiler is not smart enough to understand at compile-time that when this program is run it will always return value. The compiler is just checking the syntax and it interprets that for the function fun if the if condition is false, the flow of the program will reach line 8 and it won’t be returning any value and therefore we get the error.

Sometimes it may be the case that we have several if-else if statements with return value which cover all the conditions but if there is no else statement we will still get the error because the compiler could not understand that all the conditions are covered.

How to Solve?

There is an easy solution to the problem, even if we understand that every condition is covered we should add a return statement at the end of the function so the compiler is sure that the non-void function will be returning some value. It may happen that flow is never reaching that part of the code but it is important to write for the compiler.

#include <bits/stdc++.h>

using namespace std;

int fun(int x){

    if(x < 5)

        return 0;

    return 1;    // line 8

}

int main() {

    fun(4);

    return 0;

}

The above code will not give any warning and the flow also won’t go to line 8 but still, it’s important to have the return statement there.

It’s a good practice to avoid this warning, many times code will run just fine even if warning is coming but for some cases, it may cause wrong behaviour of code, to avoid that its good practice to avoid this warning.

The control reaches end of non-void function is an error message that is thrown when a function is declared as returning a value but doesn’t have a return statement in the code.Control Reaches End of Non void Function

This guide will shed light on all the reasons behind this error and how it can be resolved without encountering other errors. Let’s begin!

Contents

  • Why Does Control Reaches End of Non-void Function Error Occur?
    • – Missing Return Statement
    • – Function Not Returning Expected Data Type
    • – Unreachable Code After Return Statement
    • – Incorrect Handling of Exceptions or Error Conditions
    • – Declared With the Non-void Return Type but Not Returning a Value
    • – Syntax Errors
  • How To Fix Control Reaches End of Non-void Function?
    • – Add the Return Statement
    • – Ensure the Correct Data Type Returned
    • – Remove Unreachable Code
    • – Properly Handle Exceptions or Error Conditions
    • – Use a Default Return Value
    • – Check for Unintended Void Return Type
    • – Correct Syntax Errors
  • Conclusion

Why Does Control Reaches End of Non-void Function Error Occur?

The control reaches end of non-void function error message occurs mostly due to syntax errors, missing run statements, unreachable codes used, and inappropriate functions in your program. Other reasons include exception errors, no return of value, and many other reasons.

Here are some other situations that lead to the said error:

  • Missing return statement.
  • Function not returning expected data type.
  • Unreachable code after return statement.
  • Incorrect handling of exceptions or error conditions.
  • The function declared with the non-void return type but not returning a value.

– Missing Return Statement

The error occurs when a non-void function is missing a return statement that returns a value of the expected data type. Let’s take an example below:

def get_sum(a, b):

c = a + b

print(get_sum(1, 2))

– Function Not Returning Expected Data Type

The error occurs when a function declared with a non-void return type returns a value of a different data type. Let’s see an example below:

Control Reaches End of Non void Function Causes

def add_numbers(a, b):

return a + b

result = add_numbers(10, 20)

print(result)

– Unreachable Code After Return Statement

The error occurs when there is code present after a return statement. The code will never be executed, as the function will terminate as soon as it reaches the return statement.

– Incorrect Handling of Exceptions or Error Conditions

The error occurs when an exception or error condition is not handled properly. On top of that, the function doesn’t have a return statement to return a value in case of an error.

– Declared With the Non-void Return Type but Not Returning a Value

The error occurs when a function declared with a non-void return type does not have a return statement that returns a value. In simple words, the error occurs when the developer forgets to add a return statement before declaring a non-void return type.Control Reaches End of Non void Function Reasons

– Syntax Errors

Sometimes, syntax errors can cause this issue to arise. Moreover, it also causes other variations of the error message to occur during the execution of the program, such as:

  • Control reaches end of non void function (-wreturn-type).
  • Error control reaches end of non-void function in c.
  • Control reaches end of non-void function cpp.

To fix control reaches end of non-void function error message, you have to check on a few things to troubleshoot the issue properly, such as looking for syntax errors and adding return statements in the program. Moreover, by ensuring the correct data type return, the error can be resolved.



– Add the Return Statement

GCC expects a return with no return value if the control exists a function without encountering a return. The function needs a return value for this, though. Include a return statement that delivers an acceptable return value at the function’s end, even if control is never passed there.

For example:

int main(void)

{

my_strcpy(strB, strA);

puts(strB);

return 0;

}

– Ensure the Correct Data Type Returned

Ensuring the correct data type returned means making sure that the function returns a value of the data type specified in the function declaration. If the function is declared to return an integer value, it should return an integer value and not a string or any other data type.Control Reaches End of Non void Function Fixes

Let’s take an example below:

int sum(int a, int b) {

int result = a + b;

return result; // returns an integer value as expected

}

int main() {

int x = 5, y = 10;

int result = sum.(x, y);

printf.(“Sum of %d & %d is %dn”, x, y, result);

return 0;

}

In this example, the function sum is declared to return an int value, and it returns the sum of a and b, which is also an int value. The main function calls the sum function and stores the returned value in the result variable, which is also an int. The value of the result is then printed on the screen, confirming that the correct data type is returned from the sum function.

– Remove Unreachable Code

Removing unreachable code refers to eliminating code that cannot be executed due to its position in the program flow. This code can confuse and make the program harder to understand, so removing it is often considered good practice.

Unreachable code can arise due to various reasons, such as conditional statements that always evaluate to false, exceptions that are never thrown, or due to a return or break statement before the code. Removing unreachable code helps to make the code cleaner and easier to maintain and reduces the potential for bugs.

Let’s take an example below:

def calculate_sum(a, b):

if a > b:

return a + b

else:

print(“The first number must be greater than the second number.”)

# Unreachable code

result = a + b

print(result)

calculate_sum(5, 3)

In the above example, if a is greater than b, the code inside the else statement will not be executed and will never reach the unreachable code block. Hence, this block of code can be safely removed.

Given below is the updated code after the removal of the unreachable code:

def calculate_sum(a, b):

if a > b:

return a + b

else:

print(“The first number must be greater than the second number.”)

calculate_sum(5, 3)

– Properly Handle Exceptions or Error Conditions

Properly handle exceptions or error conditions by adding try-catch blocks or checking for error conditions before returning a value. Handling exceptions or error conditions properly can prevent unexpected behavior in the code and improve its reliability.

Here are a few steps to handle exceptions properly:

  1. Catch the exception: Use a try-catch block to catch any exceptions that occur in the code.
  2. Identify the exception: Determine the type of the exception and what caused it.
  3. Log the exception: Record the exception details, such as the type, message, and stack trace, in a log file or database.
  4. Respond to the exception: Take appropriate action based on the type of the exception, such as displaying an error message to the user or retrying a failed operation.
  5. Avoid masking the exception: Don’t catch exceptions just to suppress them. Instead, handle them in a way that is meaningful for the application.
  6. Use appropriate exception types: Use specific exception types, such as FileNotFoundException or ArgumentException, to make it easier to handle specific errors in the code.
  7. Clean up resources: If the user opens any resources in the try block, make sure to close or dispose of them in the final block to avoid resource leaks.

– Use a Default Return Value

If the function is declared with a non-void return type but does not need to return a value, add a return statement that returns a default value for the specified data type. A default return value is a predefined value that a function returns if no other value is specified or can be calculated. It allows for a fallback behavior in case of unexpected or erroneous inputs, improving the robustness of the function. It can be used to simplify the function’s interface and make it easier to use.

Let’s see an example below:

def divide(a, b=2):

return a/b

print(divide(10)) # 5.0

print(divide(10, 5)) # 2.0

In this example, the function divide takes two parameters, a and b. The second parameter, b, has a default value of 2. If the value of b is not provided, it will use the default value. When calling the function, the first call uses the default value of b, and the second call provides a different value for b.

– Check for Unintended Void Return Type

If the function is intended to return a value, ensure that it is declared with the correct non-void return type and not as a void function. A “void return type” means that a function doesn’t return any value.Control Reaches End of Non void Function Solutions

In some programming languages, such as C or C++, it’s possible to accidentally write a function that is intended to return a value but doesn’t actually return anything due to a coding error. This can cause unexpected behavior in the program.

To avoid this, it is a good practice to always check that a function with a non-void return type actually returns a value before exiting. This can be done with a simple code review or by using a static analysis tool that checks for missing return statements. Let’s take two examples below:

public int AddNumbers(int a, int b)

{

if (a == 0 || b == 0)

{

Console.WriteLine(“One of the numbers is 0.”);

return; // unintended void return type

}

return a + b;

}

Correct example:

To fix this, we need to return a value of type int even when the condition is true. Check below.

public int AddNumbers(int a, int b)

{

if (a == 0 || b == 0)

{

Console.WriteLine(“One of the numbers is 0.”);

return 0;

}

return a + b;

}

– Correct Syntax Errors

This is the most common error that is seen while compiling the Arduino code. To fix this issue, the programmer can use various software designs to highlight syntax errors and correct them. By correcting these mistakes, the programmer can also get rid of this error’s variations, such as:

  • Control reaches end of non void function gcc.
  • Control reaches end of non-void function switch.
  • Control reaches end of non-void function recursive.

Conclusion

After reading this guide, the reader can now understand the major reasons behind this error and how they can resolve it. Some key points are given below.

  • This error message indicates that a function declared to return a value has reached its end without returning one.
  • This can happen if the function doesn’t contain a return statement or if the return statement is not reached due to an infinite loop or early exit.
  • To fix the error, add a return statement that returns a value of the appropriate type or modify the code to ensure the return statement is reached.
  • It’s important to ensure that all functions with a non-void return type have a return statement, as it can cause unexpected behavior and lead to bugs in the code.

The reader can now resolve this type of error message on their own. Thank you for reading!

  • Author
  • Recent Posts

Position is Everything

Your Go-To Resource for Learn & Build: CSS,JavaScript,HTML,PHP,C++ and MYSQL. Meet The Team

Position is Everything

Понравилась статья? Поделить с друзьями:
  • Ошибка content not found перевод
  • Ошибка content manager assetto corsa
  • Ошибка co 9250 6 ps vita решение
  • Ошибка cmos при загрузке компьютера
  • Ошибка cmos settings wrong что делать