www.digitalmars.com         C & C++   DMDScript  

D - [Beginner] My first program - Access Violation :-(

reply Dave Sieber <dsieber spamnot.sbcglobal.net> writes:
Ok, I'm a complete beginner, and need some guidance, if there are any kind 
souls out there.

I've created my first program. It compiles with no errors. But when I run 
it, I get an Access Violation. It looks like a problem with printf(), but I 
don't know what is wrong with it.

Here's the program:

==========================
import std.compiler;

int main()
{
   // this works
   printf(std.compiler.name);

   // this gets an Access Violation
   printf("%s\n", std.compiler.name);

   return 0;
}
==========================

I have some other questions about this short program, but I'd like to do 
one at a time.

Would a group for beginners be a good idea? I see a lot of technical 
discussion here, and I feel my simple-minded question is out of place. 
Perhaps something like D.learn or D.help? Or, if it's okay to ask beginner 
questions here, I'll carry on.

-- 
dave
Mar 25 2004
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Dave Sieber wrote:

Ok, I'm a complete beginner, and need some guidance, if there are any kind 
souls out there.

I've created my first program. It compiles with no errors. But when I run 
it, I get an Access Violation. It looks like a problem with printf(), but I 
don't know what is wrong with it.

Here's the program:

==========================
import std.compiler;

int main()
{
   // this works
   printf(std.compiler.name);

   // this gets an Access Violation
   printf("%s\n", std.compiler.name); //This line
  

   return 0;
}
==========================
  

I have some other questions about this short program, but I'd like to do 
one at a time.

Would a group for beginners be a good idea? I see a lot of technical 
discussion here, and I feel my simple-minded question is out of place. 
Perhaps something like D.learn or D.help? Or, if it's okay to ask beginner 
questions here, I'll carry on.

  

-- -Anderson: http://badmama.com.au/~anderson/
Mar 25 2004
next sibling parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
J Anderson wrote:

 Dave Sieber wrote:

 Ok, I'm a complete beginner, and need some guidance, if there are any 
 kind souls out there.

 I've created my first program. It compiles with no errors. But when I 
 run it, I get an Access Violation. It looks like a problem with 
 printf(), but I don't know what is wrong with it.

 Here's the program:

 ==========================
 import std.compiler;

 int main()
 {
   // this works
   printf(std.compiler.name);

   // this gets an Access Violation
   printf("%s\n", std.compiler.name); //This line
  


Guess I should explain why. D strings are not null terminated like c. D does it's best to convert D string into zero-terminated strings when using a C like function. However in the case of printf there is no way to tell that the conversion is needed (generally). Therefore you use the .* part which happens to mean the length followed by the string pointer, which is what D strings are. D string (array) struct array { uint length; char* array; //size length } C String char* array; //size length + 1 where last character is zero Hope that helps. -- -Anderson: http://badmama.com.au/~anderson/
Mar 25 2004
next sibling parent reply Dave Sieber <dsieber spamnot.sbcglobal.net> writes:
J Anderson <REMOVEanderson badmama.com.au> wrote:

 printf("%.*s\n", std.compiler.name); //Very common mistake

Guess I should explain why. D strings are not null terminated like c. D does it's best to convert D string into zero-terminated strings when using a C like function. However in the case of printf there is no way to tell that the conversion is needed (generally). Therefore you use the .* part which happens to mean the length followed by the string pointer, which is what D strings are. D string (array) struct array { uint length; char* array; //size length } C String char* array; //size length + 1 where last character is zero

Thank you, very fast response! I understand now what is going on, I'm just not sure I like it :-) You know, when you start a new language and you (perhaps subconsciously) expect things to work as you're used to with some previous language -- it takes a little while, and a few bone-headed errors like mine, before it sinks in and becomes natural. Meanwhile, you curse the new language <BG> I had one other question, but I think I have got it on my own: I was wondering where printf() came from, but std.c.stdio is implicitly imported, yes? And according to phobos.html, it is the only symbol defined in that module. New question: what is the difference (if any) between std.c.stdio's printf(), and object's static printf() ? And why is it there in object? Isn't the one in std.c.stdio available anywhere you want to use it? -- dave
Mar 25 2004
parent reply SL <shadowlord13 users.sourceforge.net> writes:
Dave Sieber wrote:

 New question: what is the difference (if any) between std.c.stdio's 
 printf(), and object's static printf() ?  And why is it there in object? 
 Isn't the one in std.c.stdio available anywhere you want to use it?
 

There's a printf in object? O.o I don't have a clue why, myself, but I can say that printf works quite wonderfully for me, so long as I avoid %s (and use %.*s instead.) :P
Mar 25 2004
parent Dave Sieber <dsieber spamnot.sbcglobal.net> writes:
SL <shadowlord13 users.sourceforge.net> wrote:

 There's a printf in object? O.o

The docs say it's there. I haven't tried to use it.
 I don't have a clue why, myself, but I can say that printf works quite
 wonderfully for me, so long as I avoid %s (and use %.*s instead.) :P

Yes, it's only when there are type mismatches in the args which the compiler can't detect that it is problematic. In C++ code, when programming windows, you still have to do a lot of printf-style calls, and I've taken up the practice of always doing something like this (from memory): inline char const *charptr(char const *p) { return p; } inline char const *charptr(std::string const& s) { return s.c_str(); } // ... likewise for anything else that can be converted // to a character pointer... // then you could do something like this: template <typename T> inline void printit(T const& t) { printf("%s\n", charptr(t)); } IOW, I *always* use charptr() on any "%s" argument to a printf-style function. That way, if I ever have reason to change the argument type (say, from 'std::string const&' to 'char const *') the call still works. And if I change the argument type to something that cannot be converted to 'const char *' via charptr(), I know immediately on the next compile, and I can address it. This has saved me a number of times, especially when you have several team members working on code and they are not all conscientious about their code quality (quite common, at least where I've worked). -- dave
Mar 25 2004
prev sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
J Anderson,
What you've explained so far is correct; I just wanted to add a bit to it.

Dave,
You may eventually find yourself wondering why
	printf(std.compiler.name);
works while
	printf("%.*s\n", std.compiler.name);
doesn't.  A C-library printf() will be expecting that the format string 
(the first argument) is a null-terminated string.  But if D doesn't 
null-terminate strings, and if arrays are actually two numbers (length 
and pointer), then how can it work?

The short answer is that Walter has put in a lot of good default things 
that make your life easier.  The long answer is...

printf() is declared in object.d like this:
	extern (C) int printf(char *, ...);

extern (C) - means that this is a C function, which is implemented in 
some other file (usually, in the standard library).

char* - means, of course, that the first argument (the format string) 
MUST be a char*.

... - means, like C, that this will accept any number of arguments (it 
is a varargs function).



In your first call:
	printf(std.compiler.name);
std.compiler.name is defined in std/compiler.d as:
	char[] name = "Digital Mars D";
That is, the type is a dynamic array, but it is initialized with a 
certain constant string.  When you use constant strings like this, 
Walter automatically places a null byte after the string.  It doesn't 
count in the length of the array, but a C function that reads the array 
will see the null terminator it expects.

The other magic that happens is that when you pass char[] into a 
function that expects char*, the compiler performs an implicit cast.  So 
when you call
	printf(std.compiler.name);
this implicit cast happens:
	printf((char*)std.compiler.name);
which basically means this is happening:
	printf(&std.compiler.name[0]);

So, the argument that printf() sees is a pointer value which points to 
the start of the string; since Walter added an invisible null at the end 
of the string, printf() terminates nicely.  But try this, and it will break:
	printf("std.compiler.name = "~std.compiler.name);
When you concatenate the two arrays with the ~ operator, the compiler 
makes a copy of the two and returns the concatenated string in a new 
array someplace.  This operation does NOT get the automatic null 
terminator.  So printf will run off forever, printing garbage, until it 
hits a 0 by pure luck or until you get an Access Violation (segfault).

So why doesn't the compiler implicitly cast the array in the second call?
	printf("%s\n", std.compiler.name);

In this case, the compier doesn't know the type that printf() expects 
for the second argument.  So it just passes the array onto the stack, 
unaltered.
	printf("%s\n", std.compiler.name);
is more or less the same as calling this:
	printf("%s\n",        std.compiler.name.length,
	               (char*)std.compiler.name);
Your choices are to use the %.s, or to use this:
	printf("%s\n", (char*)std.compiler.name);
The %.s is probably better in most cases, but not all C libraries 
support it.  The cast is supported everywhere, but doesn't work if the 
string isn't null terminated.

So how do you print an arbitrary string in a portable manner?  I use 
this code often:
	char[] foo;
	printf("%s\n", (char*)(foo~\0));
which just appends a null character onto the string, then casts the 
string to a char* so that printf() gets what it expects.  Happily, 
Walter provides the toStringz() function in std.string which does the same:
	char[] foo;
	printf("%s\n", toStringz(foo));
...plus Walter's function has some smarts in it to avoid doing 
unneccesary copies.



So, in summary:
* The compiler adds nulls into the memory after constant strings, but 
when you build a string at runtime these nulls don't exist
* The compiler implicitly casts char[] to char*, when it knows that this 
is necessary
Mar 25 2004
parent reply Dave Sieber <dsieber spamnot.sbcglobal.net> writes:
Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote:

 <snip for brevity>

Wow, Russ, thank you for the excellent explanation! You should get that onto the wiki so that others can benefit from it, especially noobs like me :-) I can see that I'll just need to get to the point of "thinking in D", just as I now do with C++. It's not hard (as your explanation shows), but you do need to make sure you understand what it is doing so that you can use it to your advantage. A couple comments: I like the toStringz() solution the best, as this mirrors what I do in C++ to avoid any "surprises" with a printf-style function call. Also, just to mention in passing something which I'm sure you already know: one should never pass a string in any form to printf() (in D or C/C++) without a "%s" format specifier. Let's say you've asked the user for some input, and then you're going to output it: printf(usersInput); If the user typed a "%s" somewhere in his input, printf() will interpret that as a format string, and will end up trying to print something from a misinterpreted stack -- crashville, Daddy-o! -- dave
Mar 25 2004
parent reply J Anderson <REMOVEanderson badmama.com.au> writes:
Dave Sieber wrote:

Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote:

  

<snip for brevity>
    

Wow, Russ, thank you for the excellent explanation! You should get that onto the wiki so that others can benefit from it, especially noobs like me :-)

-- -Anderson: http://badmama.com.au/~anderson/
Mar 25 2004
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
I'm way buried in work...if somebody else wants to post it, that's great 
with me. :)

J Anderson wrote:
 Dave Sieber wrote:
 
 Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote:

  

 <snip for brevity>
   

Wow, Russ, thank you for the excellent explanation! You should get that onto the wiki so that others can benefit from it, especially noobs like me :-)


Apr 07 2004
parent J C Calvarese <jcc7 cox.net> writes:
In article <c51e7s$ke9$1 digitaldaemon.com>, Russ Lewis says...
I'm way buried in work...if somebody else wants to post it, that's great 
with me. :)

I posted it at: http://www.wikiservice.at/d/wiki.cgi?HowTo/printf#charvschar JC
J Anderson wrote:
 Dave Sieber wrote:
 
 Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote:

  

 <snip for brevity>
   

Wow, Russ, thank you for the excellent explanation! You should get that onto the wiki so that others can benefit from it, especially noobs like me :-)



Apr 08 2004
prev sibling parent reply Brad Anderson <brad sankaty.dot.com> writes:
Try the Wiki also...
http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage

and specifically, the FAQ:
http://www.prowiki.org/wiki4d/wiki.cgi?FaqRoadmap

J Anderson wrote:
 This is the best group.
 

Mar 25 2004
parent Dave Sieber <dsieber spamnot.sbcglobal.net> writes:
Brad Anderson <brad sankaty.dot.com> wrote:

 Try the Wiki also...
 http://www.prowiki.org/wiki4d/wiki.cgi?FrontPage
 
 and specifically, the FAQ:
 http://www.prowiki.org/wiki4d/wiki.cgi?FaqRoadmap

Thank you, I'm visiting it now. -- dave
Mar 25 2004