Jump to page: 1 2
Thread overview
[Beginner] My first program - Access Violation :-(
Mar 26, 2004
Dave Sieber
Mar 26, 2004
J Anderson
Mar 26, 2004
J Anderson
Mar 26, 2004
Dave Sieber
Mar 26, 2004
SL
Mar 26, 2004
Dave Sieber
Mar 26, 2004
Russ Lewis
Mar 26, 2004
Dave Sieber
Mar 26, 2004
J Anderson
Apr 07, 2004
Russ Lewis
Apr 08, 2004
J C Calvarese
Mar 26, 2004
Brad Anderson
Mar 26, 2004
Dave Sieber
March 26, 2004
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
March 26, 2004
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
>  
>
printf("%.*s\n", std.compiler.name); //Very common mistake

>   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.
>
>  
>
This is the best group.

-- 
-Anderson: http://badmama.com.au/~anderson/
March 26, 2004
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
>>  
>>
> 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

Hope that helps.

-- 
-Anderson: http://badmama.com.au/~anderson/
March 26, 2004
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.
> 
March 26, 2004
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
March 26, 2004
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
March 26, 2004
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
March 26, 2004
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
March 26, 2004
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

March 26, 2004
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
« First   ‹ Prev
1 2