Skip to content

Commit 7e28e8e

Browse files
authored
[spec/faq] Improve printf docs (#3902)
* [spec/faq] Improve printf docs writef replaces printf, not writefln. Add `RUNNABLE_EXAMPLE`s. Use monospace for printf specifiers. Add stdio import. Mention pragma(printf). Show string literal with `%s`. Fix Bugzilla 24732 - FAQ article is out of date on calling printf. * Use RUNNABLE_EXAMPLE_FAIL
1 parent 1507233 commit 7e28e8e

File tree

2 files changed

+45
-17
lines changed

2 files changed

+45
-17
lines changed

articles/faq.dd

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ $(ITEM q4, Why is printf in D?)
217217
is not part of D, it is part of C's standard
218218
runtime library which is accessible from D.
219219
D's standard runtime library has
220-
$(REF writefln, std,stdio)
220+
$(REF writef, std,stdio)
221221
which is as powerful as $(LINK2 http://www.digitalmars.com/rtl/stdio.html#printf, printf)
222222
but is much easier to use.
223223
)
@@ -381,43 +381,70 @@ void main()
381381

382382
$(ITEM printf, How do I get printf() to work with strings?)
383383

384-
In C, the normal way to printf a string is to use the $(B %s)
385-
format:
384+
$(P In C, the normal way to printf a string is to use the $(D %s)
385+
format:)
386386

387387
$(CCODE
388388
char s[8];
389389
strcpy(s, "foo");
390390
printf("string = '%s'\n", s);
391391
)
392392

393-
Attempting this in D, as in:
393+
$(P Attempting this in D:)
394394

395+
$(RUNNABLE_EXAMPLE_FAIL
395396
---------------------------------
397+
import core.stdc.stdio;
396398
string s;
397399
s = "foo";
398-
printf("string = '%s'\n", s);
400+
printf("string = '%s'\n", s); // Error
399401
---------------------------------
400-
401-
usually results in garbage being printed, or an access violation.
402-
The cause is that in C, strings are terminated by a 0 character.
403-
The $(B %s) format prints until a 0 is encountered.
404-
In D, strings are not 0 terminated, the size is determined
402+
)
403+
$(P The call gets flagged at compile-time (see
404+
$(DDSUBLINK spec/pragma, printf, `pragma(printf)`))
405+
because in general, `string` is not compatible with the `%s` specifier.
406+
This prevents garbage being printed, or an access violation.)
407+
408+
$(P In D, a string is an array, so it has a length and a pointer.
409+
In C, a string is a pointer to memory terminated by a 0 character.
410+
The $(D %s) format prints until a 0 is encountered.
411+
In D, strings in general are not 0-terminated, the size is determined
405412
by a separate length value. So, strings are printf'd using the
406-
$(B %.*s) format:
413+
$(D %.*s) format:)
407414

415+
$(RUNNABLE_EXAMPLE
408416
---------------------------------
417+
import core.stdc.stdio;
409418
string s;
410419
s = "foo";
411-
printf("string = '%.*s'\n", s);
420+
printf("string = '%.*s'\n", cast(int) s.length, s.ptr);
412421
---------------------------------
422+
)
423+
$(P which will behave as expected, for that input data.
424+
There are pitfalls:)
425+
* printf's $(D %.*s) will print until the length
426+
is reached or a 0 is encountered, so D strings with embedded 0's
427+
will only print up to the first 0.
428+
* printf's length argument is an int, so ensure `s.length <= int.max`.
429+
430+
$(P D string literals are actually 0-terminated, so `%s` can be used
431+
with the `.ptr` property and printf will work for those.
432+
However, non-literal strings often are not 0-terminated:)
433+
434+
$(RUNNABLE_EXAMPLE
435+
---
436+
import core.stdc.stdio;
437+
printf("string = '%s'\n", "bar".ptr); // OK, 0-terminated
438+
439+
string s = "food";
440+
s = s[0..3]; // foo, not 0-terminated
441+
printf("string = '%.*s'\n", cast(int) s.length, s.ptr);
413442

414-
$(P which will behave as expected.
415-
Remember, though, that printf's $(B %.*s) will print until the length
416-
is reached or a 0 is encountered, so D strings with embedded 0's
417-
will only print up to the first 0.
443+
s ~= "ie"; // s is still not 0-terminated
444+
---
418445
)
419446

420-
$(P Of course, the easier solution is just use $(REF writefln, std, stdio)
447+
$(P Of course, the easier solution is just to use $(REF writef, std, stdio)
421448
which works correctly with D strings.
422449
)
423450

dlang.org.ddoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ $1
411411
RUNNABLE_EXAMPLE_COMPILE=<div class="runnable-examples" data-compile=''>
412412
$1
413413
</div>
414+
RUNNABLE_EXAMPLE_FAIL=$(RUNNABLE_EXAMPLE $0)
414415
_=These go inside a RUNNABLE_EXAMPLE macro invocation before the first `---`
415416
RUNNABLE_EXAMPLE_STDIN=<code class="runnable-examples-stdin">$0</code>
416417
RUNNABLE_EXAMPLE_ARGS=<code class="runnable-examples-args">$0</code>

0 commit comments

Comments
 (0)