Как перевести строку из чисел в массив из чисел?
Я новичок в 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 шт):
Как по мне тут лучше использовать динамическое выделение памяти (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;
}
Надо понимать что {} - является константой. Теоретически константа не должна менятся, но на практике, проигнорировав сообщение компилятора, константу получится поменять, это может при особых случаях прокатить, но в будущем станет причиной багов или падений программы. Если программа не слетит - то баг будет проявляться в том, что присвоив значение в две разные переменные - значения в двух переменных окажется одинаковым (ведь это константа).
Вместо {} нужно использовать 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 (не стандартная библиотека)
Ещё есть пара
mallocfreeи подобные, есть пулы и много ещё чего.можно вернуть int32 и оспользовать его отдельные байты
c_str и string - дополнительные удобства, для економии памяти так же можно использовать ascii-z функции strlen.
Проблема в вашем коде, что вы возвращаете указатель на удаленную память. Решение с "умным указателем":
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() менять не надо.
Все остальные ответы сложные и хитрые. 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';
}