7

I have a struct that contains a string and a length:

typedef struct string {
  char* data;
  size_t len;
} string_t;

Which is all fine and dandy. But, I want to be able to output the contents of this struct using a printf-like function. data may not have a nul terminator (or have it in the wrong place), so I can't just use %s. But the %.*s specifier requires an int, while I have a size_t.

So the question now is, how can I output the string using printf?

3
  • 7
    You can convert a size_t to an int, provided the value fits... Commented Oct 2, 2013 at 20:13
  • 3
    @KerrekSB Well if the length doesn't fit in an int that's going to be one interesting printf call :-)) Should check though since it could overflow. Commented Oct 2, 2013 at 20:13
  • 6
    If data might contain non-printable characters (like a null character), you don't want %s at all. Write a loop. Commented Oct 2, 2013 at 20:13

3 Answers 3

14

Assuming that your string doesn't have any embedded NUL characters in it, you can use the %.*s specifier after casting the size_t to an int:

string_t *s = ...;
printf("The string is: %.*s\n", (int)s->len, s->data);

That's also assuming that your string length is less than INT_MAX. If you have a string longer than INT_MAX, then you have other problems (it will take quite a while to print out 2 billion characters, for one thing).

Sign up to request clarification or add additional context in comments.

3 Comments

not very likely, but the OP could be targeting an embedded or historic device with 16-bit int
Is this really the best way to go about doing this? When I first noted the behavior, my first question was "Why isn't it a size_t anyway?"
@JeremyRodi: Not sure, but my guess is that it uses int instead of size_t because it's for backwards compatibility with code that was written before the C language was standardized, before the type size_t even existed.
4

A simple solution would just be to use unformatted output:

fwrite(x.data, 1, x.len, stdout);
This is actually bad form, since `fwrite` may not write everything, so it should be used in a loop;
for (size_t i, remaining = x.len;
     remaining > 0 && (i = fwrite(x.data, 1, remaining, stdout)) > 0;
     remaining -= i) {
}

(Edit: fwrite does indeed write the entire requested range on success; looping is not needed.)

Be sure that x.len is no larger than SIZE_T_MAX.

11 Comments

But if I wanted to use something like snprintf, this wouldn't work as well. While in that case I could just copy over the data, I'd rather be able to use other formatting specifiers (like %i).
@drderp: I don't understand. How would you use snprintf? You can use memcpy to write to a memory location rather than a file...
@KerrekSB if I wanted to create a string along with a number, I'd have to first use memcpy to copy over the string, and then use snprintf to output the number at the location I want. Not as great as just doing it all in one go, I think...
@drderp: Yeah. Well, it depends whether you want to handle arbitrary data with null bytes. If not, formatted printing with %.*s is preferable.
@KerrekSB Ok, one more question: does %.*s ignore null bytes?
|
1

how can I output the string using printf?

In a single call? You can't in any meaningful way, since you say you might have null terminators in strange places. In general, if your buffer might contain unprintable characters, you'll need to figure out how you want to print (or not) those characters when outputting your string. Write a loop, test each character, and print it (or not) as your logic dictates.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.