2017-01-24 4 views
0

Следующая функция «increment» добавляет 1 к числу, представленному в виде массива.Возврат массива переменной длины из функции

int* increment(int array[], int size, int *sizeLen) 
{ 
    int temp[size+1]; 

    int carry = 0; 
    carry = (array[size-1]+1)/10; 
    temp[size] = (array[size-1]+1)%10; 

    for(int i=size-2;i>=0;i--) 
    { 
     temp[i+1] = (array[i] + carry)%10; 
     carry = (array[i]+carry)/10; 
    } 
    if(carry) 
    { 
     temp[0] = 1; 
     *sizeLen = size+1; 
     return temp; 
    } 
    else 
    { 
     *sizeLen = size; 
     return (temp+1); 
    } 
} 

int main() 
{ 
    int array[] = {9,9,9}; 
    int length; 
    int *res = increment(array, sizeof(array)/sizeof(int), &length); 
    for(int i=0;i<length;i++) 
    { 
     cout << res[i] << " "; 
    } 
} 

Я знаю, что GCC поддерживает variable length arrays и они хранятся в стеке. Я ожидаю, что темп исчезнет из области действия после завершения этой функции, и попытка распечатать массив в главном окне должна отображать значения мусора. Но в моем случае печатаются фактические значения. Когда массив переменной длины, объявленный в функции, выходит из области видимости?

+6

Что вы видите - это неопределенное поведение. Это ловушка C. –

+1

C++ даже не поддерживает VLA, а ваш код в основном C. Используйте контейнеры и итераторы. – Downvoter

+0

Я не заметил тега. Почему вы используете VLA в коде C++ (расширение, нестандартное)? Ваш вопрос действительно о C? Уточните, пожалуйста. Это разные языки. – StoryTeller

ответ

0

Ключевым моментом здесь является длительность хранения массива. Он имеет автоматическую продолжительность хранения. Время жизни переменных с автоматическим временем хранения заканчивается в тот момент, когда область, в которой они находятся, завершается.

Выполняется точно так, как вы ожидаете. Но ничто в C не позволяет вам взять адрес локального объекта и вернуть его из функции.

Использование этого указателя является неопределенным поведением. Возможно, он работает, но массив все еще по-прежнему «мертв». Такие указатели известны в разговоре как «оборванные указатели».


Ну, это верно для С. Поскольку речь идет о расширении GCC, то же самое относится в основном, но, возможно, должны быть приняты с зерном соли.

0

Это неопределенное поведение. В следующий раз, когда программа подталкивает к стеку, данные будут потеряны.

Не полагайтесь на это для профессиональных программ.

+0

Прошу прощения за мою краткость. Я сейчас на Android-планшете. –

0

Вы правы. temp выходит из области видимости, и он должен печатать значения мусора.

Но иногда он печатает правильные значения. Поскольку локальная память стека не очищается/переопределяется немедленно, когда текущая функция заканчивается.

1

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

C++ способ делать такие вещи, как использование std::vector<>. Например (если предположить, что в исходном size и sizelen называется число элементов в array и вернулся, соответственно)

std::vector<int> increment(std::vector<int> const&array) 
{ 
    std::vector<int> temp(array.size()); 
    auto a = array.rbegin(); 
    auto b = temp.rbegin(); 
    auto carry = (*a+1)/10; 
    *b = (*a+1)%10; 
    for(++a,++b; a!=array.rend(); ++a,++b) 
    { 
     *b = (*a+carry)%10; 
     carry = (*a+carry)/10; 
    } 
    if(carry) { // we must add another element into temp at the start 
       // that cannot be done with vector, hence we must create 
       // another vector. Note that this should rarely happen. 
     std::vector<int> result(temp.size()+1); 
     auto r = result.begin(); 
     *r = 1; 
     std::copy(temp.begin(),temp.end(),++r); 
     return result; 
    } else 
     return temp; 
} 

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

std::vector<std::uint8_t> increment(std::vector<std::uint8_t> const&input) 
{ 
    std::vector<std::uint8_t> result; 
    result.reserve(input.size()+1); 
    auto a = input.begin(); 
    auto carry = (*a+1)/10; 
    result.push_back((*a+1)%10); 
    for(++a; a!=input.end(); ++a) 
    { 
     result.push_back((*a+carry)%10); 
     carry = (*a+carry)/10; 
    } 
    if(carry) 
     result.push_back(1); 
    return result; 
} 
+0

Что не так с 'if (carry) temp.insert (1, temp.begin());'? Это включает в себя перетасовку всех элементов, но это не хуже, чем копия, которую вы реализуете. –

+0

Также, если вы инициализируете 'carry' до 1, а затем создаете цикл' do-while', вся вещь становится 'std :: vector result (array);' (или лучше, принимать массив по значению - сохраняет копию если вход является временным). 'int carry = 1; auto it = result.rbegin(); do {auto v = * it + carry; * it ++ = v% 10; carry = v/10; } while (carry && it! = result.rend()) if (carry) {result.insert (carry, result.begin();} return result; ' –

+0

Ah no! Не работает с пустым вводом.' int carry = 1, for (auto it = result.rbegin(); carry && it! = result.rend(); ++ it) {auto v = * it + carry; * it = v% 10; carry = v/10;} if (carry) {result.insert (carry, result.begin();} return result; ' –

Смежные вопросы