user11: Say I have some variables that I want to print out to the terminal, what's the easiest way to print them in a string?
Currently I do something like this:
Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);

Is there a better way to do this?

user3917: Using Streaming.h, in place of 
Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);

one can write
Serial << "Var 1:" << var1) << " Var 2:" << var2 << " Var 3:" << var3 << endl;

The definition of << in Streaming.h in effect translates that into a series of ordinary Serial.print() calls.  That is, << is syntactic sugar, implemented without increasing code size.
If you don't have Streaming.h installed, get Streaming5.zip from arduiniana.org.  Unzip it in your libraries directory, for example in ~/sketchbook/libraries.  Add the line #include <Streaming.h> within sketches where you use << as a stream operator.
Base-conversion specifiers _HEX, _DEC, _OCT, and _BIN are provided, as well as a _FLOAT function (with number of decimal places) and endl.  For example, to print latitude and longitude values in a form like "Your coordinates are -23.123, 135.4567” one could write:
Serial << "Your coordinates are " << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;

This could also be written as
Serial << F("Your coordinates are ") << _FLOAT(latitude,3) << ", " << _FLOAT(longitude,4) << endl;

which would keep the longer string in PROGMEM instead of bringing it into RAM.  
Note, Streaming.h  doesn't build any strings as such; it just delivers the text of its <<-arguments to a stream.  A PString class at arduiniana can build strings from stream inputs, if strings instead of streamed output are desired or needed.

user304: I usually (painfully) stick with multiple lines of Serial.print but when it becomes convoluted I go back to sprintf. It's annoying in that you have to have an available buffer for it. 
Usage is as simple (??) as:
char buffer[35]; // you have to be aware of how long your data can be
                 // not forgetting unprintable and null term chars
sprintf(buffer,"var1:%i\tvar2:%i\tvar3:%i",var1,var2,var3);
Serial.println(buffer);

A word of warning though, it doesn't (by default) support floating types. 

user34731: The usage will depend of the data type of your variables.
If they are int, it would be %d or %i
If they are string, it would be %s
Wrapper for printf
You can change the limit based on your requirements
#include <stdarg.h>
void p(char *fmt, ... ){
    char buf[128]; // resulting string limited to 128 chars
    va_list args;
    va_start (args, fmt );
    vsnprintf(buf, 128, fmt, args);
    va_end (args);
    Serial.print(buf); // Output result to Serial
}

Source: https://playground.arduino.cc/Main/Printf
Usage examples:
p("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3); // strings
p("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3); // numbers

ESP8266
Its built-in in Serial class of the framework. No need for additional library or function.
// strings
Serial.printf("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3);
// numbers
Serial.printf("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3);


More details about formatting tips on the printf format reference page : http://www.cplusplus.com/reference/cstdio/printf/
\n is the escape sequence for the line feed.

Escape sequences are used to represent certain special characters within string literals and character literals.

Source: http://en.cppreference.com/w/cpp/language/escape
[EDIT]
- As @Juraj mentioned, it's not available on most of the AVR modules. So I added ESP8266 mention and a printf wrapper for common AVR modules

user58839: I am newbie in Arduino world, but I recently found that this is just a regular C++ (without exceptions and probably polymorphism). But you still can enjoy templates. So my solution is to use following templates:
void myprint(void)
{
  Serial.println("");
}

template<typename ...Args>
void myprint(const uint64_t & val, Args && ...args)
{
  serialPrintUint64(val);
  myprint(args...);
}

template<typename T, typename ...Args>
void myprint(const T & t, Args && ...args)
{
  Serial.print(t);
  myprint(args...);
}

....

// somewhere in your code
myprint("type: ", results.decode_type, 
        "\t value: ", results.value, 
        "\t addr: ", results.address,
        "\t cmd: ", results.command);

Nice thing here is that it does not use any extra memory and extra processing here. 

user60392: 
One possible solution is:
Serial.println((String)"Var 1:" + var1 + " Var 2:" + var2 + " Var 3:" + var3);

The above solution from Iain increases code size.

user62: I usually used Tabs to make things line up better in the Serial. Having things line up like I do allow the arduino to fire as fast as possible while being able to notice certain changes in the variables.
Try something like this:
Serial.println("Var 1:\tVar 2tVar 3:");
Serial.print("\t");
Serial.print(var1);
Serial.print("\t");
Serial.print(var2);
Serial.print("\t");
Serial.print(var3);
Serial.println();

Or something like this:
Serial.print("Var 1:");Serial.println(var1);
Serial.print("\tVar 2:");Serial.println(var2);
Serial.print("\tVar 3:");Serial.println(var3);


user89: 
This is probably not better, just different.
You can use the String object for output. These objects allow concatenation and support automatic typecasting.
Serial.begin(9600);
String label = "Var";
const byte nValues = 3;
int var[nValues] = {36, 72, 49};

for (int i = 0; i < nValues; i++) {
    String stuff = label + i + ": ";
    Serial.println(stuff + var[i]);
}


user7297: I only use this for debugging but:
int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));


user304: I wouldn't normally put two answers to a question, but I only just found this today, where you can use printf without any buffer. 
// Function that printf and related will use to print
int serial_putchar(char c, FILE* f) {
    if (c == '\n') serial_putchar('\r', f);
    return Serial.write(c) == 1? 0 : 1;
}

FILE serial_stdout;

void setup(){
    Serial.begin(9600);

    // Set up stdout
    fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
    stdout = &serial_stdout;

    printf("My favorite number is %6d!\n", 12);
}

void loop() {
  static long counter = 0;
  if (millis()%300==0){
    printf("millis(): %ld\tcounter: %ld (%02X)\n", millis(), counter, counter++);
    delay(1);    
  }
}

This still has the floating point limitation.
edit: I thought I would do a little testing on this, and it works quite well. I added a better test to the loop with formatted output. 

user65: ardprintf is a function that I hacked together which simulates printf over the serial connection. This function (given at the bottom) can be pasted in the beginning of the files where the function is needed. It should not create any conflicts.
It can be called similar to printf. See it in action in this example:

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int l=2;
  char *j = "test";
  long k = 123456789;
  char s = 'g';
  float f = 2.3;

  ardprintf("test %d %l %c %s %f", l, k, s, j, f);

  delay(5000);

}

The output as expected is:
test 2 123456789 g test 2.30


The function prototype is:
int ardprintf(char *, ...);

It returns the number of arguments detected in the function call. 
This is the function definition:
#ifndef ARDPRINTF
#define ARDPRINTF
#define ARDBUFFER 16
#include <stdarg.h>
#include <Arduino.h>

int ardprintf(char *str, ...)
{
  int i, count=0, j=0, flag=0;
  char temp[ARDBUFFER+1];
  for(i=0; str[i]!='\0';i++)  if(str[i]=='%')  count++;

  va_list argv;
  va_start(argv, count);
  for(i=0,j=0; str[i]!='\0';i++)
  {
    if(str[i]=='%')
    {
      temp[j] = '\0';
      Serial.print(temp);
      j=0;
      temp[0] = '\0';

      switch(str[++i])
      {
        case 'd': Serial.print(va_arg(argv, int));
                  break;
        case 'l': Serial.print(va_arg(argv, long));
                  break;
        case 'f': Serial.print(va_arg(argv, double));
                  break;
        case 'c': Serial.print((char)va_arg(argv, int));
                  break;
        case 's': Serial.print(va_arg(argv, char *));
                  break;
        default:  ;
      };
    }
    else 
    {
      temp[j] = str[i];
      j = (j+1)%ARDBUFFER;
      if(j==0) 
      {
        temp[ARDBUFFER] = '\0';
        Serial.print(temp);
        temp[0]='\0';
      }
    }
  };
  Serial.println();
  return count + 1;
}
#undef ARDBUFFER
#endif

**To print the % character, use %%.*

Now, available on Github gists.

11: <p>Say I have some variables that I want to print out to the terminal, what's the easiest way to print them in a string?</p>

<p>Currently I do something like this:</p>

<pre><code>Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);
</code></pre>

<p>Is there a better way to do this?</p>
 3917: <p>Using <code>Streaming.h</code>, in place of </p>

<pre><code>Serial.print("Var 1:");Serial.println(var1);
Serial.print(" Var 2:");Serial.println(var2);
Serial.print(" Var 3:");Serial.println(var3);
</code></pre>

<p>one can write</p>

<pre><code>Serial &lt;&lt; "Var 1:" &lt;&lt; var1) &lt;&lt; " Var 2:" &lt;&lt; var2 &lt;&lt; " Var 3:" &lt;&lt; var3 &lt;&lt; endl;
</code></pre>

<p>The definition of <code>&lt;&lt;</code> in <code>Streaming.h</code> in effect translates that into a series of ordinary <code>Serial.print()</code> calls.  That is, <code>&lt;&lt;</code> is syntactic sugar, implemented without increasing code size.</p>

<p>If you don't have <code>Streaming.h</code> installed, get <a href="http://arduiniana.org/Streaming/Streaming5.zip" rel="nofollow noreferrer"><code>Streaming5.zip</code></a> from <a href="http://arduiniana.org/libraries/streaming/" rel="nofollow noreferrer">arduiniana.org</a>.  Unzip it in your libraries directory, for example in <code>~/sketchbook/libraries</code>.  Add the line <code>#include &lt;Streaming.h&gt;</code> within sketches where you use <code>&lt;&lt;</code> as a stream operator.</p>

<p>Base-conversion specifiers _HEX, _DEC, _OCT, and _BIN are provided, as well as a _FLOAT function (with number of decimal places) and <code>endl</code>.  For example, to print latitude and longitude values in a form like "Your coordinates are -23.123, 135.4567” one could write:</p>

<pre><code>Serial &lt;&lt; "Your coordinates are " &lt;&lt; _FLOAT(latitude,3) &lt;&lt; ", " &lt;&lt; _FLOAT(longitude,4) &lt;&lt; endl;
</code></pre>

<p>This could also be written as</p>

<pre><code>Serial &lt;&lt; F("Your coordinates are ") &lt;&lt; _FLOAT(latitude,3) &lt;&lt; ", " &lt;&lt; _FLOAT(longitude,4) &lt;&lt; endl;
</code></pre>

<p>which would keep the longer string in PROGMEM instead of bringing it into RAM.  </p>

<p>Note, <code>Streaming.h</code>  doesn't build any strings as such; it just delivers the text of its <code>&lt;&lt;</code>-arguments to a stream.  A <a href="http://arduiniana.org/libraries/pstring/" rel="nofollow noreferrer">PString class at arduiniana</a> can build strings from stream inputs, if strings instead of streamed output are desired or needed.</p>
 304: <p>I usually (painfully) stick with multiple lines of <code>Serial.print</code> but when it becomes convoluted I go back to <code>sprintf</code>. It's annoying in that you have to have an available buffer for it. </p>

<p>Usage is as simple (??) as:</p>

<pre><code>char buffer[35]; // you have to be aware of how long your data can be
                 // not forgetting unprintable and null term chars
sprintf(buffer,"var1:%i\tvar2:%i\tvar3:%i",var1,var2,var3);
Serial.println(buffer);
</code></pre>

<p>A word of warning though, it doesn't (by default) support floating types. </p>
 34731: 

<p>The usage will depend of the data type of your variables.</p>

<p>If they are <code>int</code>, it would be <code>%d</code> or <code>%i</code>
If they are <code>string</code>, it would be <code>%s</code></p>

<p><strong>Wrapper for printf</strong></p>

<p>You can change the limit based on your requirements</p>

<pre class="lang-cpp prettyprint-override"><code>#include &lt;stdarg.h&gt;
void p(char *fmt, ... ){
    char buf[128]; // resulting string limited to 128 chars
    va_list args;
    va_start (args, fmt );
    vsnprintf(buf, 128, fmt, args);
    va_end (args);
    Serial.print(buf); // Output result to Serial
}
</code></pre>

<p>Source: <a href="https://playground.arduino.cc/Main/Printf" rel="nofollow noreferrer">https://playground.arduino.cc/Main/Printf</a></p>

<p>Usage examples:</p>

<pre class="lang-cpp prettyprint-override"><code>p("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3); // strings
p("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3); // numbers
</code></pre>

<p><strong>ESP8266</strong></p>

<p>Its built-in in <code>Serial</code> class of the framework. No need for additional library or function.</p>

<pre class="lang-cpp prettyprint-override"><code>// strings
Serial.printf("Var 1:%s\nVar 2:%s\nVar 3:%s\n", var1, var2, var3);
// numbers
Serial.printf("Var 1:%d\nVar 2:%d\nVar 3:%d\n", var1, var2, var3);
</code></pre>

<hr>

<p>More details about formatting tips on the printf format reference page : <a href="http://www.cplusplus.com/reference/cstdio/printf/" rel="nofollow noreferrer">http://www.cplusplus.com/reference/cstdio/printf/</a></p>

<p><code>\n</code> is the escape sequence for the line feed.</p>

<blockquote>
  <p>Escape sequences are used to represent certain special characters within string literals and character literals.</p>
</blockquote>

<p>Source: <a href="http://en.cppreference.com/w/cpp/language/escape" rel="nofollow noreferrer">http://en.cppreference.com/w/cpp/language/escape</a></p>

<p><strong>[EDIT]</strong>
- As @Juraj mentioned, it's not available on most of the AVR modules. So I added ESP8266 mention and a printf wrapper for common AVR modules</p>
 58839: <p>I am newbie in Arduino world, but I recently found that this is just a regular C++ (without exceptions and probably polymorphism). But you still can enjoy templates. So my solution is to use following templates:</p>

<pre><code>void myprint(void)
{
  Serial.println("");
}

template&lt;typename ...Args&gt;
void myprint(const uint64_t &amp; val, Args &amp;&amp; ...args)
{
  serialPrintUint64(val);
  myprint(args...);
}

template&lt;typename T, typename ...Args&gt;
void myprint(const T &amp; t, Args &amp;&amp; ...args)
{
  Serial.print(t);
  myprint(args...);
}

....

// somewhere in your code
myprint("type: ", results.decode_type, 
        "\t value: ", results.value, 
        "\t addr: ", results.address,
        "\t cmd: ", results.command);
</code></pre>

<p>Nice thing here is that it does not use any extra memory and extra processing here. </p>
 60392: <blockquote>
<p>One possible solution is:</p>
<p><code>Serial.println((String)&quot;Var 1:&quot; + var1 + &quot; Var 2:&quot; + var2 + &quot; Var 3:&quot; + var3);</code></p>
</blockquote>
<p>The above solution from Iain increases code size.</p>
 62: <p>I usually used Tabs to make things line up better in the Serial. Having things line up like I do allow the arduino to fire as fast as possible while being able to notice certain changes in the variables.</p>

<p>Try something like this:</p>

<pre><code>Serial.println("Var 1:\tVar 2tVar 3:");
Serial.print("\t");
Serial.print(var1);
Serial.print("\t");
Serial.print(var2);
Serial.print("\t");
Serial.print(var3);
Serial.println();
</code></pre>

<p>Or something like this:</p>

<pre><code>Serial.print("Var 1:");Serial.println(var1);
Serial.print("\tVar 2:");Serial.println(var2);
Serial.print("\tVar 3:");Serial.println(var3);
</code></pre>
 89: <p>
This is probably not better, just different.
You can use the <strong>String</strong> object for output. These objects allow concatenation and support automatic typecasting.</p>

<pre><code>Serial.begin(9600);
String label = "Var";
const byte nValues = 3;
int var[nValues] = {36, 72, 49};

for (int i = 0; i &lt; nValues; i++) {
    String stuff = label + i + ": ";
    Serial.println(stuff + var[i]);
}
</code></pre>
 7297: <p>I only use this for debugging but:</p>

<pre><code>int a = 10;
int b = 20;
Serial.println("a = " + String(a) + " and b = " + String(b));
</code></pre>
 304: <p>I wouldn't normally put two answers to a question, but I only just found <a href="http://forum.arduino.cc/index.php/topic,120440.0.html">this</a> today, where you can use printf without any buffer. </p>

<pre><code>// Function that printf and related will use to print
int serial_putchar(char c, FILE* f) {
    if (c == '\n') serial_putchar('\r', f);
    return Serial.write(c) == 1? 0 : 1;
}

FILE serial_stdout;

void setup(){
    Serial.begin(9600);

    // Set up stdout
    fdev_setup_stream(&amp;serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
    stdout = &amp;serial_stdout;

    printf("My favorite number is %6d!\n", 12);
}

void loop() {
  static long counter = 0;
  if (millis()%300==0){
    printf("millis(): %ld\tcounter: %ld (%02X)\n", millis(), counter, counter++);
    delay(1);    
  }
}
</code></pre>

<p>This still has the floating point limitation.</p>

<p>edit: I thought I would do a little testing on this, and it works quite well. I added a better test to the loop with formatted output. </p>
 65: <p><code>ardprintf</code> is a function that I hacked together which simulates <code>printf</code> over the serial connection. This function (given at the bottom) can be pasted in the beginning of the files where the function is needed. It should not create any conflicts.</p>

<p>It can be called similar to <code>printf</code>. See it in action in this example:
</p>

<pre class="lang-cpp prettyprint-override"><code>void setup()
{
  Serial.begin(9600);
}

void loop()
{
  int l=2;
  char *j = "test";
  long k = 123456789;
  char s = 'g';
  float f = 2.3;

  ardprintf("test %d %l %c %s %f", l, k, s, j, f);

  delay(5000);

}
</code></pre>

<p>The output as expected is:</p>

<pre class="lang-cpp prettyprint-override"><code>test 2 123456789 g test 2.30
</code></pre>

<hr>

<p>The function prototype is:</p>

<pre class="lang-cpp prettyprint-override"><code>int ardprintf(char *, ...);
</code></pre>

<p>It returns the number of arguments detected in the function call. </p>

<p>This is the function definition:</p>

<pre class="lang-cpp prettyprint-override"><code>#ifndef ARDPRINTF
#define ARDPRINTF
#define ARDBUFFER 16
#include &lt;stdarg.h&gt;
#include &lt;Arduino.h&gt;

int ardprintf(char *str, ...)
{
  int i, count=0, j=0, flag=0;
  char temp[ARDBUFFER+1];
  for(i=0; str[i]!='\0';i++)  if(str[i]=='%')  count++;

  va_list argv;
  va_start(argv, count);
  for(i=0,j=0; str[i]!='\0';i++)
  {
    if(str[i]=='%')
    {
      temp[j] = '\0';
      Serial.print(temp);
      j=0;
      temp[0] = '\0';

      switch(str[++i])
      {
        case 'd': Serial.print(va_arg(argv, int));
                  break;
        case 'l': Serial.print(va_arg(argv, long));
                  break;
        case 'f': Serial.print(va_arg(argv, double));
                  break;
        case 'c': Serial.print((char)va_arg(argv, int));
                  break;
        case 's': Serial.print(va_arg(argv, char *));
                  break;
        default:  ;
      };
    }
    else 
    {
      temp[j] = str[i];
      j = (j+1)%ARDBUFFER;
      if(j==0) 
      {
        temp[ARDBUFFER] = '\0';
        Serial.print(temp);
        temp[0]='\0';
      }
    }
  };
  Serial.println();
  return count + 1;
}
#undef ARDBUFFER
#endif
</code></pre>

<p>**To print the <code>%</code> character, use <code>%%</code>.*</p>

<hr>

<p>Now, available on <a href="https://gist.github.com/asheeshr/9004783">Github gists</a>.</p>
