Как перевести строку из чисел в массив из чисел?

Я новичок в C++. Только начал изучать, так что имею только примерное понимание о работе C++, т.к. пришел из питона

Задача: Пользователь вводит число, в котором не меньше 3 символов, например 1321. Последние 2 символа записываются в в одну переменную, первые оставшиеся в другую. Также возможно, что пользователь ввел число с разделителем, например 17|40 Далее с этими переменными будут производиться математические действия, так что они должны быть int. Нужно реализовать функцию, которая принимает 1321 или 13|21 И возвращает две переменные типа int 13,21 В каком формате принимает все равно, я не знаю как удобнее.

Вроде как реализовал, но вот вопрос, как такое сделать в формате функции, ведь, вроде как, функции в C++ не возвращают несколько значений? Поэтому попробовал реализовать через char Вот моя попытка:

char * string_to_time(string time) {
    int sec = atoi(time.substr(time.length() - 2, 2).c_str());
    int hours = atoi(time.substr(0, time.length() - 2).c_str());
    char out[2] = {};
    out[0] = sec;
    out[1] = hours;
    return out;
}

int main(void)
{
    //int mas[] = { 0,0 };
    string timing;
    cin >> timing;
    char a = *string_to_time(timing);
    cout << a << endl;
}

Соответственно, пользователь вводит строку, эта строка подается на вход функции, которая возвращает массив из двух целых чисел: sec, hours (так задумано). Но по факту происходит не пойми что.

На звездочки - разыменования не обращайте внимание, это попытка использовать чужой код. Там было написано, что функция может вернуть массив только через разыменования.

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

Заранее спасибо.


Ответы (4 шт):

Автор решения: n1tr0xs

Как по мне тут лучше использовать динамическое выделение памяти (new):

short* string_to_time(string time){
    short* out = new short[2];
    out[0] = atoi(time.substr(time.length() - 2, 2).c_str());
    out[1] = atoi(time.substr(0, time.length() - 2).c_str());
    return out;
}

int main(){
    string timing;
    cin >> timing;
    short* a = string_to_time(timing);
    cout << a[0] << ' ' << a[1];
    delete[] a;
}
→ Ссылка
Автор решения: nick_n_a

Надо понимать что {} - является константой. Теоретически константа не должна менятся, но на практике, проигнорировав сообщение компилятора, константу получится поменять, это может при особых случаях прокатить, но в будущем станет причиной багов или падений программы. Если программа не слетит - то баг будет проявляться в том, что присвоив значение в две разные переменные - значения в двух переменных окажется одинаковым (ведь это константа).

Вместо {} нужно использовать new int[размер_массива_цифрой] в вашем случае my_var = new int[2]

P.S. после взятия памяти, память желательно освобождать используя delete. Иначе можно рано ли позно столкнуться с тем что память закончилась. Для вашего случая можно сделать как ниже (в вашем случае такая утечка памяти не повлияет на работу, но в будущем пригодится):

char * a = string_to_time(timing);
...
delete a;

Как вариант, что бы не использовать delete, если вы хотите использовать "временную" переменную, но не хотите её делать глобальной, можете обьявить её с ключевым словом static - тогда компилятор зарезервирует область памяти под эту переменную из глобально пространства, а не из стека. Если вас устраивает то что переменная будет после вызова ф-ции может быть использована "один раз" (вызвали функцию - вывели или скопировали и не храним, как у вас сейчас) то тогда можно вашу программу реализовать так (так можно для случая single-thread как у вас сейчас, multi-thread может дать тихий баг как описано вначале - там сложнее: надо бы или thread-var или буфер передавать):

 char* f() {
   static char x_out[2];
   return x_out;
   }

Что будет эквивалентно, но x_out не будет мешать в глобальной области.

 char x_out[2];
 char* f() {
   return x_out;
   }

Но в вашем примере есть ещё ошибки. Одну ошибку можно решить таким образом (если без delete)

char & a = *f();
cout << ((char*)&a)[0] << ' ' << ((char*)&a)[1]; //(вторая ошибка)

Но что бы достать значения - прийдётся мучатся опять с указателями, поэтому вторую ошибку... Ошибка заключается в том, что в char хранится одно число, что бы испоьзовать больше чем одно - обязательно тип ссылка или указатель. Можно ошибку исправить так

 out * a = f();
 cout << a[0] << ' ' << a[1];

Или использовать ASCII-z.


Я ещё подумал, есть третий вариант реализации, без использования памяти вообще. К сожелению на с++ этот вариант чуть сложнее (хотя стандарт позволяет обьявить тип "массив размером 2 елемента", использовать его не получится, и приходится использовать структуру). Хотя на практике стараются делать так что бы функция возвращала либо одну сущность, либо массив.

typedef struct {  char item[2]; } ARRAYC2;

ARRAYC2 string_to_time(string time){
ARRAYC2 out;
out.item[0] = atoi(time.substr(time.length() - 2, 2).c_str());
out.item[1] = atoi(time.substr(0, time.length() - 2).c_str());
return out;
}

int main(){
  string timing;
  cin >> timing;
  ARRAYC2  a = string_to_time(timing);
  cout << a.item[0] << ' ' << a.item[1];
 }

А если принять что item[0] и item[1] числа от 0 до 255 - то можно применить "упаковку" числа в байты, упростив выражение выше:

int string_to_time(string time){
int out;
out  = atoi(time.substr(time.length() - 2, 2).c_str());
out  += atoi(time.substr(0, time.length() - 2).c_str())*256;
return out;
}

int main(){
  string timing;
  cin >> timing;
  int a = string_to_time(timing);
  cout << a % 256 << ' ' << a / 256; // упрощенный вариант
  cout << a & 255 << ' ' << ( a >> 8 ); // вариант для гурманов
 }

Ну и на последок, правильный ответ. На практике, если вам нужно вернуть две величины, обычно "общепринятым" считается передавать назад всё в параметрах (или на крайний случай первую величину через return, остальные через параметры которые "в хвосте" функции):

void string_to_time(string time, char * ret1 , char * ret2){
  *ret1 = atoi(time.substr(time.length() - 2, 2).c_str());
  *ret2 = atoi(time.substr(0, time.length() - 2).c_str())*256;
  }

void main(){
  string timing;
  cin >> timing;
  char a[2]; 
  string_to_time(timing, &a[0], &a[1]);
  cout << a[0] << ' ' << a[1];
  }

Ещё добавлю...

  • Есть умные указатели для С++ (Мне подсказывают), тогда не нужно delete. Они являются частью новой стандартной библиотеки (если не ошибаюсь)

  • Есть GC для c++, тогда тоже не нужно delete (не стандартная библиотека)

  • Ещё есть пара malloc free и подобные, есть пулы и много ещё чего.

  • можно вернуть int32 и оспользовать его отдельные байты

  • c_str и string - дополнительные удобства, для економии памяти так же можно использовать ascii-z функции strlen.

→ Ссылка
Автор решения: Slava

Проблема в вашем коде, что вы возвращаете указатель на удаленную память. Решение с "умным указателем":

std::unique_ptr<int[]> string_to_time(string time) {
    auto ptr = std::make_unique<int[]>(2);
    ptr[0] = std::stoi( time.substr(time.length() - 2, 2) );
    ptr[1] = std::stoi( time.substr(time.length() - 2, 2) );
    return ptr;
}

int main(void)
{
    std::string timing;
    std::cin >> timing;
    auto data = string_to_time(timing);
    std::cout << data[0] << " " << data[1] << std::endl;
}

но на самом деле динамическая память тут не нужна, можно использовать массив по значению:

std::array<int,2> string_to_time(string time) {
    std::array<int,2> arr;
    arr[0] = std::stoi( time.substr(time.length() - 2, 2) );
    arr[1] = std::stoi( time.substr(time.length() - 2, 2) );
    return arr;
}

при этом main() менять не надо.

→ Ссылка
Автор решения: Stanislav Volodarskiy

Все остальные ответы сложные и хитрые. C++ отлично возвращает сколько угодно значений из одной функции. Возможность возвращать std::pair уже упоминали. Ещё упоминали возврат std::array, но этот вариант мне не нравится: совпадение типов у секунд и часов, вообще говоря, случайность. Самой простой путь создать свою структуру, сделать в ней столько полей сколько нужно и вернуть её:

// g++ -Wall -Wextra -Wpedantic -std=c++11 return_pair.cpp

#include <iostream>
#include <string>

struct Time {
    int hours;
    int sec;
};

Time string_to_time(const std::string &time) {
    int sec = atoi(time.substr(time.length() - 2, 2).c_str());
    int hours = atoi(time.substr(0, time.length() - 2).c_str());
    return {hours, sec};
}

int main(void)
{
    std::string timing;
    std::cin >> timing;
    Time time = string_to_time(timing);
    std::cout << time.hours << '|' << time.sec << '\n';
}
→ Ссылка