0

In C# I can easily enumerate through an array of strings. However, why doesn't the code below work in C++?

int main(int argc, char ** argv)
{
    string hi[] = { "hi", "cool", "what" };

    for (string s : hi)
    {
        printf("%s \n", s);
    }

    return 0;
}

Also, I tried using this instead, but it doesn't work either:

printf(s);

Oddly enough, an array on integers does work, but using %d. And yes, I do have #include <string>.

Error information provided by @chris (generated by the Clang compiler):

main.cpp:12:25: error: cannot pass non-trivial object of type 'string' (aka 'basic_string<char, char_traits<char>, allocator<char> >') to variadic function; expected type from format string was 'char *' [-Wnon-pod-varargs]

  printf("%s \n", s);
           ~~      ^

main.cpp:12:25: note: did you mean to call the c_str() method?

  printf("%s \n", s);
                   ^
                    .c_str()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vippy
  • 1,356
  • 3
  • 20
  • 30
  • 2
    Filling in a couple missing things above this, Clang gives a [perfectly clear error](http://coliru.stacked-crooked.com/a/3878b5362323ab75). This might [help to explain](http://stackoverflow.com/questions/10865957/c-printf-with-stdstring/10865967#10865967), though. – chris Apr 25 '15 at 20:58
  • @chris - I've inlined error message you've provided to make question look reasonable. – Alexei Levenkov Apr 25 '15 at 21:08
  • 1
    @AlexeiLevenkov, I saw. It certainly helps to have an error, but it's worth noting that the one the OP is getting is probably not half as straightforward as that on what to do about it. – chris Apr 25 '15 at 21:09
  • Hmmm... now since I've changed the title there is very good duplicate - http://stackoverflow.com/questions/10865957/c-printf-with-stdstring – Alexei Levenkov Apr 25 '15 at 21:12
  • @chris probably, since OP did not provide any information what they use to compile it is hard to say... Also "variadic function" may confuse newcomer completely from even reading past first line anyway :) – Alexei Levenkov Apr 25 '15 at 21:13
  • 3
    Just a few notes to your code example: In your for loop `s` is actually copied, which you might not expect when coming from C#, in case you just want to print them you definitely want to use `for(const string & s: hi)`. Also you might want to use C++ containers instead of C arrays and obviously `std::cout` instead of `printf`. – hynner Apr 25 '15 at 21:14
  • @AlexeiLevenkov, I added a footnote to explain things for the curious. – chris Apr 25 '15 at 21:20
  • @hynner Good catch! I edited my answer to use a const reference instead. Thanks! – Beta Carotin Apr 25 '15 at 21:25

3 Answers3

3

Use std::cout instead of printf.
The problem is that you are passing a std::string to printf, when you actually need a const char*. Using std::string::c_str solves this. But its better to not use printf in the first place.

Live working example

#include <iostream>
#include <string>

int main(int argc, char** argv)
{
    std::string hi[] = { "hi", "cool", "what" };

    for (const std::string& s : hi) {
        std::cout << s << std::endl;
    }

    return 0;
}
Beta Carotin
  • 1,659
  • 1
  • 10
  • 27
  • Thank you. I was told by my instructor to avoid using `cout`. Is there not a way to accomplish this using `printf`? – Vippy Apr 25 '15 at 21:07
  • 2
    @Vippy There is, as the other answers as well as this one mention: Using `s.c_str()` instead of just `s` in the call to `printf`. Which I do not recommend because of type safety. Google it if you want to learn the details. – Beta Carotin Apr 25 '15 at 21:09
  • 1
    @Vippy, I won't jump and say it's bad advice, but your instructor should certainly be able to back that up with solid reasons instead of it being a "because I say so" rule. Personally, if advising to use `printf`, might as well use a small library that is type safe and extensible (i.e., would work with types like `std::string` and your own types). – chris Apr 25 '15 at 21:10
  • In a nutshell, he gave 2 main reasons he doesn't like it: First if I remember correctly, he said something about `cout` being a class that doesn't behave like a class because it uses some trickery with shift left bitwise operator. And second, he compiled 3 programs using `puts()`, `printf()` and `cout`. `puts()` and `printf()` were roughly the same size, however `cout` was twice the size. – Vippy Apr 25 '15 at 21:23
  • 2
    @Vippy, I can understand not liking the shift operators being overloaded, and many people agree it was a bad design choice. However, if the reason for disliking it is that it is unexpected, this usage is so popular now that the bit shift operators have have become known as input and output operators in some textbooks. As for the extra code size, I haven't tested, but assuming it's true, I would understand if a bit of executable size were a concern. In a teaching environment, it probably makes no difference, but it definitely can elsewhere. – chris Apr 25 '15 at 21:30
  • @Vippy Unless you are programming for embedded systems, you don´t need to be concerned with executable sizes. In that case, using a `std::string` is probably a bad idea aswell, so there´s that ;) `std::cout` is not a class but an object, and it behaves like one. Your instructor should probably brush up on modern c++ programming :) – Beta Carotin Apr 25 '15 at 21:30
1

You can use c_str() property of each string (it returns char* pointer that printf knows how to print correctly):

for (string s : hi)
{
    printf("%s \n", s.c_str());
}

Just take into account that printf is part of older C input-output facilities. While they will work in most cases when used correctly, it is better at least not to mix them with C++ streams (std::cin and std::cout), and even much better is to use more advanced facilities of newer <iostream> classes:

for (std::string s : hi)
{
    std::cout << s;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
1

The issue is the printf (which is a native C function) expects to get a C-string (i.e., char* which is a sequence of characters terminated by '\0') rather than an object of type string which is a C++ type.

However, you can simply convert from string to a C-string by using the c_str() method on the string, i.e., change your original call to printf to printf("%s \n", s.c_str());.

Alternatively, you can use the C++ standard way for printing to the console using std::cout (some examples were provided in the other answers).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Amnon Shochot
  • 8,998
  • 4
  • 24
  • 30