Thread overview | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
March 26, 2004 [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave Sieber | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to J Anderson | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to J Anderson | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to J Anderson | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to Brad Anderson | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dave Sieber | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to SL | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to J Anderson | 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 Re: [Beginner] My first program - Access Violation :-( | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | 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 |
Copyright © 1999-2021 by the D Language Foundation