-2

I want to avoid printing space (" ") or an underscore ("_") after the last element of an array while printing them using a for loop. The problem mentions one of the way to do so, I want to understand how and why this happens.

    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];
    }

this prints 1_2_3_4_5 which is as expected.

I want to get around the working of this operator. How it functions and what other aspects does it cover.

8
  • It is just an implementation of the subscript operator on vector (not even overloading). And vector must allocate a contiguous piece of memory. So it is just a matter of calculating the offset into memory and return an element found there. Maybe this will help you learncpp.com/cpp-tutorial/overloading-the-subscript-operator Commented Feb 17, 2023 at 8:59
  • I recommend you invest in some good beginners C++ books as they will explain all that (and much more). Commented Feb 17, 2023 at 8:59
  • Print out "_"[0] and "_"[1]. What do you get? Commented Feb 17, 2023 at 9:02
  • 1
    I'm not sure it's good to print \0, maybe it should be "_" + (i==n-1), still not recommend though. Commented Feb 17, 2023 at 9:08
  • What that outputs depends on which medium it's outputting on – some terminals display the null character as "nothing", but that's not guaranteed to happen.
    – molbdnilo
    Commented Feb 17, 2023 at 9:21

3 Answers 3

1

There is nothing special going on with [] here. Its the usual element access. "_" is a const char [2] where first character is _ and the second is a null terminator \0.

You could do the same with a string:

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];
}

Note, that std::string is an excepetion among containers and it is ok to read the \0 from str[str.size()].

\0 is not a printable character, hence you see the output you see.

Perhaps the code more clear if you use an empty string explicitly:

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];
}

Alternatively, as you are using an index based loop anyhow (rather than a range based loop, which should be prefered), you can start the loop at the second element:

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];
}
2
  • ""_" is a char [2]..." No it is not Commented Feb 17, 2023 at 9:53
  • 1
    it can also be &"_"[condition] Commented Feb 17, 2023 at 14:38
1

In this statement

cout << v1[i] << "_"[i==n-1];

the expression "_"[i==n-1] is an expression with the string literal "_".

String literals have types of constant character arrays. The literal "_" has the type const char[2] and is represented in memory like

{ '_', '\0' }

So the expression "_"[i==n-1] uses the subscript operator with the string literal that is with an array. The expression i == n-1 is always equal to false that is converted to integer 0 when i is not equal to n-1. Otherwise it is equal to true that is converted to 1.

So you have either "_"[0] that yields the character '_' or "_"[1] that yields the character '\0';.

That is when i is not equal to n-1 you in fact have

cout << v1[i] << '_';

and when i is equal to n -1 you in fact have

cout << v1[i] << '\0';

Pay attention to that in this expression "_"[i==n-1] the string literal is implicitly converted to pointer to its first element.

Alternatively you could write with the same effect

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];
}

To enlarge your knowledge bear in mind that this expression "_"[i==n-1] you may also rewrite like ( i == n-1 )["_"] though such an expression will only confuse readers of the code.

From the C++17 Standard (8.2.1 Subscripting)

1 A postfix expression followed by an expression in square brackets is a postfix expression. One of the expressions shall be a glvalue of type “array of T” or a prvalue of type “pointer to T” and the other shall be a prvalue of unscoped enumeration or integral type. The result is of type “T”. The type “T” shall be a completely-defined object type.66 The expression E1[E2] is identical (by definition) to *((E1)+(E2)) [ Note: see 8.3 and 8.7 for details of * and + and 11.3.4 for details of arrays. — end note ] , except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise. The expression E1 is sequenced before the expression E2.

Pay attention to that this code snippet

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];
}

could look more clear using the range-based for loop. For example

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;
}

If the compiler supports the C++20 Standard then you can write also like

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;
}
1
  • Thanks for the detailed explanation, this did clear the doubt. Commented Feb 17, 2023 at 11:56
0

your problem has nothing todo with the subscript operator though you just need to figure out when to print the _ or not based on the position in the array when printing.

#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;
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.