Я хочу избежать печати пробела ("") или символа подчеркивания ("_") после последнего элемента массива при печати их с использованием цикла for. В задаче упоминается один из способов сделать это, я хочу понять, как и почему это происходит.
vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
for (int i = 0; i < n; i++) {
cout << v1[i] << "_"[i==n-1];
}
Это печатает 1_2_3_4_5, как и ожидалось.
Хочу обойти работу этого оператора. Как он работает и какие другие аспекты он охватывает.
Я рекомендую вам инвестировать в несколько хороших книг по C++ для начинающих, так как они объяснят все это (и многое другое).
Распечатайте "_"[0] и "_"[1]. Что вы получаете?
Я не уверен, что это хорошо печатать \0, может быть, так и должно быть "_" + (i==n-1), но все же не рекомендую.
То, что это выводит, зависит от того, на какой носитель он выводится - некоторые терминалы отображают нулевой символ как «ничего», но это не гарантируется.
В перегрузке нет никакой магии. Логическое значение можно преобразовать в целое число; false на 0, а true на 1. Так что это то же самое, что и int index = i==n-1 ? 1 : 0; cout << v1[i] << "_"[index];
Обратитесь к , как спрашивать , где первым шагом является «поиск, а затем исследование», и вы найдете множество обманов для этого.
@PaulMcKenzie, ваш пример был в точку, спасибо.
В этом заявлении
cout << v1[i] << "_"[i==n-1];
Выражение "_"[i==n-1] является выражением со строковым литералом "_".
Строковые литералы имеют типы константных массивов символов. Литерал "_" имеет тип const char[2] и представляется в памяти как
{ '_', '\0' }
Таким образом, выражение "_"[i==n-1] использует оператор нижнего индекса со строковым литералом, который является массивом. Выражение i == n-1 всегда равно false, которое преобразуется в целое число 0, когда i не равно n-1. В противном случае он равен true, который преобразуется в 1.
Таким образом, у вас есть либо "_"[0], что дает символ '_', либо "_"[1], который дает символ '\0';.
То есть, когда i не равно n-1 у вас на самом деле есть
cout << v1[i] << '_';
И когда i равно n -1, у вас действительно есть
cout << v1[i] << '\0';
Обратите внимание, что в этом выражении "_"[i==n-1] строковый литерал неявно преобразуется в указатель на его первый элемент.
В качестве альтернативы вы можете написать с тем же эффектом
vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
const char literal[] = "_";
for (int i = 0; i < n; i++) {
cout << v1[i] << literal[i==n-1];
}
Чтобы расширить свои знания, имейте в виду, что это выражение "_"[i==n-1] вы также можете переписать как ( i == n-1 )["_"], хотя такое выражение только запутает читателей кода.
Из стандарта С++ 17 (8.2.1 Подписка)
1 Постфиксное выражение, за которым следует выражение в квадратных скобках, постфиксное выражение. Одно из выражений должно быть glvalue введите «массив T» или prvalue типа «указатель на T», а другой должно быть значением перечисления с незаданной областью или целочисленного типа. результат имеет тип «Т». Тип «Т» должен быть полностью определенным тип объекта.66 Выражение E1[E2] идентично (по определению) выражению *((E1)+(E2)) [Примечание: см. 8.3 и 8.7 для подробностей * и + и 11.3.4 для деталей массивов. — примечание в конце] , за исключением случаев, когда операнд массива, результатом является lvalue, если этот операнд является lvalue и xvalue в противном случае. Выражение E1 расположено перед выражение Е2.
Обратите внимание на то, что этот фрагмент кода
vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
for (int i = 0; i < n; i++) {
cout << v1[i] << "_"[i==n-1];
}
Может выглядеть более ясно, используя цикл for на основе диапазона. Например
std::vector<int> v1 = {1, 2, 3, 4, 5};
bool next = false;
for ( const auto &item : v1 )
{
if ( next ) std::cout << '_';
else next = true;
std::cout << item;
}
Если компилятор поддерживает стандарт С++ 20, вы можете написать также как
std::vector<int> v1 = {1, 2, 3, 4, 5};
for ( bool next = false; const auto &item : v1 )
{
if ( next ) std::cout << '_';
else next = true;
std::cout << item;
}
Спасибо за подробное объяснение, это развеяло сомнения.
@FaizanAhmed Вовсе нет. Пожалуйста.:)
Ваша проблема не имеет ничего общего с оператором индекса, хотя вам просто нужно выяснить, когда печатать _ или нет, основываясь на позиции в массиве при печати.
#include <vector>
#include <iostream>
// using namespace std; ehr no don't do this
int main()
{
std::vector<int> v1{ 1, 2, 3, 4, 5 };
for (std::size_t i{ 0ul }; i < v1.size(); ++i)
{
if (i != 0ul) std::cout << "_";
std::cout << v1[i];
}
std::cout << "\n";
// with vector use range based for if you can
// and then it becomes
bool print_underscore = false;
for (const int value : v1)
{
if (print_underscore) std::cout << "_";
std::cout << value;
print_underscore = true;
}
std::cout << "\n";
return 0;
}
С [] здесь ничего особенного не происходит. Это обычный доступ к элементам. "_" — это const char [2], где первый символ — _, а второй — нулевой терминатор \0.
Вы можете сделать то же самое со строкой:
vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
for (int i = 0; i < n; i++) {
cout << v1[i] << std::string{"_"}[i==n-1];
}
Обратите внимание, что std::string является исключением среди контейнеров, и можно читать \0 из str[str.size()].
\0 не является печатным символом, поэтому вы видите вывод, который видите.
Возможно, код станет более понятным, если вы явно используете пустую строку:
vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<std::string> d{ "_",""};
int n = v1.size();
for (int i = 0; i < n; i++) {
cout << v1[i] << d[i==n-1];
}
В качестве альтернативы, поскольку вы все равно используете цикл на основе индекса (а не цикл на основе диапазона, который следует предпочесть), вы можете начать цикл со второго элемента:
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<std::string> d{ "_",""};
int n = v1.size();
if (n) std::cout << v1[0];
for (int i = 1; i < n; i++) {
std::cout << "_" << v1[i];
}
""_" это char [2]..." Нет, это не так
Это просто реализация оператора индекса над вектором (даже без перегрузки). И вектор должен выделять непрерывный кусок памяти. Так что это просто вопрос вычисления смещения в памяти и возврата найденного там элемента. Возможно, это вам поможет learncpp.com/cpp-tutorial/overloading-the-subscript-operator