![]() |
[QUOTE=Dubslow;299853][QUOTE=jcrombie;299829]I generally hate macros. Maybe the new debuggers can handle them better, but I
remember many painful steppings through code with multiple line macros hidden in some header file who knows where. I see that you kept it to a single line and close to where you used it. Thanks for that. [/QUOTE] Yeah, I realize they're one of the hardest things to understand, hence the relatively heavy commenting. How do you do multi-line macros? I think I still prefer the gotos of the original attempt to these macros, but it's fun to experiment with the macro at least. [/QUOTE] If you really want to experiment with macros, especially macros with arguments, you should really look at post #215 again. There are some important issues you need to be aware of that are dealt with there. |
[QUOTE=jyb;299876]No, no, memory checkers may be helpful, but I agree completely with your general point about avoiding uninitialized memory. Zeroing can be very effective for that (though I'll point out that there's no zeroing variant of realloc, so in this case it would be somewhat futile anyway). My point was just that your first post about this could have been confusing to Dubslow if he didn't know what you meant.
[/QUOTE] I guess we are in agreement then. Although I fail to see how malloc() + memset() would be superior to one call to calloc(). [QUOTE=jyb;299876] But how is this different from "long l = 0;"? sizeof(long) may be 8 and sizeof(0) may be 4, but I assume you don't have a problem with that? The point is that the compiler has to do *something* when it does an assignment across types, and it frequently does this quite transparently. In my example, the "something" is to widen the 0 value from 4 bytes to 8 when writing to memory. When the lhs is a pointer, the "something" is to simply write the bit pattern for a null pointer into the pointer variable's memory. [/QUOTE] Yes, I like "long l = 0;". They are both integers, just one is bigger than the other. I even like "float f = 0.0;", since they are both floats (but non-intuitively a demotion). I would also say that "out = NULL;" would be less ambiguous for someone just looking at that one line of code wrt what type is "out". [QUOTE=jyb;299876] I don't understand your point about sizeof(NULL), or indeed why it matters what sizeof(NULL) is. The compiler just takes care of this for you when it needs to, and you never have to worry about it. [/QUOTE] My original point was that "NULL" is less farther away from "char*" in some sense than "0" is. (In terms of amount work to promote, and is therefore a superior choice). I realize now that is a rather weak argument and thinking about it now, I really don't care about the amount of work. [QUOTE=jyb;299876] And as for the standard sucking, I quite disagree: if it worked the way you apparently want it to, then C would be limited to working on a particular kind of machine architecture (which happens to be about the only architecture still around, but still). The way the standard is written doesn't impose any such requirements on the machine architecture, and that's a good thing. And after all, there's nothing that prevents an implementation from using all-0s as the representation for a null pointer (and that's exactly what pretty much all modern machines/compilers do). [/QUOTE] I hope you realize that making a whole bunch of people suffer in the present for a future that probably will never happen doesn't make sense to me. NULL is de facto address 0 and will probably always be that way. The standard just needs to acknowledge that. Cheers |
[QUOTE=jyb;299874]
Ah, well then help me out here. What were you actually trying to accomplish with that macro? Using exit(1) means the program will just end, regardless of what else may have been going on. Many would consider this to be rude behavior for a utility function like this, but again it's not entirely clear what the correct behavior should be in case of memory allocation failure. Was that what you thought, or did you think that exit(1) would just return from the function?[/quote]Indeed, ending anything with exit() had been my default for a while. In Python, I write my own exit() as a frontend to system.exit(), and furthermore, with the particular work I had been doing, an error in any part led immediately to an exit all and early termination. I just wrote exit() here without thinking :razz:. It definitely is a rude thing to do for such a function. [QUOTE=jyb;299874] Go back and look at the description I gave for ReadLine (in essence its "spec") in post #195. What is it supposed to do when there are no characters to read because it's at the end of the file? I.e. how should a program that is using ReadLine know when it's read everything? The answer is that in this case ReadLine should return NULL. So if it also returns NULL to indicate to indicate that a memory error occurred, then that's ambiguous. What should a program do if it calls ReadLine and gets back NULL? Assume it got everything, or assume that something went wrong while trying? [/quote]Hmm... the thing is, the standard-error-return for all the library functions is NULL, so it would make more sense to retain that practice and return something else for EOF. In either case though, what else could you return besides NULL? A pointer has to be NULL or pointing to something; pointing to something has to be understood as success of some sort. Perhaps just put the EOF in the one byte array (should there be a null-terminator) and return that? [QUOTE=jyb;299874] If that book really said that, then no, it is not to believed and I would recommend throwing it out! The "whole point" of void * is exactly the opposite. It's a pointer type which is guaranteed to be assignment compatible with most other pointer types, so it doesn't require a cast when assigning between them. The cast is not necessary; as for whether it's actually harmful, well again, more on that below.[/QUOTE] Hmm... perhaps I misinterpreted something? Would you be willing to read [URL="http://publications.gbdirect.co.uk/c_book/chapter5/pointers.html"]5.3.5[/URL] and [URL="http://publications.gbdirect.co.uk/c_book/chapter5/pointer_expressions.html"]5.7.1[/URL] and tell me what you make of those?[QUOTE=jyb;299875]I won't try to persuade you to change your mind on this. As I said before, there is much variation in how people do this. But I do want to address one point you make: pointers are inherently not "primitive" because by their very nature they must have something to point to. If you want to maintain a distinction between different pointer types (and I claim this is a very useful distinction to have, about which more below), then pointer types have to be defined by their "base" type. They're really no different from arrays in that respect. If you want to declare an array, it has to be an array of some type; the same goes for pointers. So the pointed-to type really is a fundamental part of the pointer type. And this isn't just arbitrary, it really matters. If you dereference a pointer, how big is the memory you're reading/writing? The answer lies in the pointer's type, which is derived from the type it's pointing to. char *cp = 0; and int *ip = 0; do very different things to memory. [/quote]Yes, this is true, but my point was that for all practical intents and purposes, pointers [i]look and feel[/i] like they are their own primitive types, regardless of the technical details of what the compilers actually do. Excepting multiple declarations, the syntax certainly makes it look that way, and I only choose to support that illusion because (IMO) it's cleaner and makes more sense that way. [QUOTE=jyb;299875] I see it as fundamental for two reasons. One, it requires a fairly sophisticated understanding of how integer conversions work in order to know exactly what will go wrong if you assign the return value to a char. It's not hugely complicated, but it requires more understanding than most people have when they first see the getchar/getc functions. [/quote]To understand exactly what goes wrong, sure, but that's not necessary to understand that [i]something[/i] will go wrong if you know that EOF is not a char.[QUOTE=jyb;299875] Two (and more importantly) it gets into some fairly deep philosophical questions about language design and exception handling. (When I say exception handling here, I'm not referring specifically to exception handlers as they exist in C++ and Java, though that's related of course; I just mean the handling of exceptional conditions in general.) To take getchar as an example, its interface sounds pretty straightforward: read and return the next character from stdout. What type would you want it to return? Well duh, it's supposed to return a character, so it should return a char. But what happens when something exceptional happens (the most common being there are no more characters in the file)? Well, there are three possibilities that come to mind: 1) f*ck up your nice simple interface by having the return type be something unintuitive, just so it can also indicate an exceptional condition. I.e. do a form of in-band signaling. 2) Add an extra pointer argument so getchar can pass back an error indicator. This, too, f*cks up the nice simple interface. 3) Have a dynamically-scoped exception mechanism, a la C++ and Java, and have getchar throw an exception if something exceptional happens. This preserves the simple interface, but requires a great deal more language support. Obviously the designers of C chose #1, probably because it required the fewest changes and the least thinking. But it also happens to be a choice which is highly prone to mistakes. You would be surprised by the number of professional C programmers who make the same mistake you made, and don't even realized they have a bug, or why.[/quote]Sure, I could buy that. I interpreted "fundamental to understanding C" not "fundamental to language/library design", the latter of which is how I read you to mean fundamental. [QUOTE=jyb;299875] I just have to disagree that casting can't possibly hurt. Here's how I think of it. If you really wanted a bare-metal language, you wouldn't really have to have types at all. You could just always indicate a size when you declare a variable and then do what you want with it. Well C is often described as a bare-metal language, and certainly compared to many languages it does tilt that way. But it does actually do a fair amount for you, and the biggest way it does this is through the type system. The type system allows the compiler to keep track of sizes and do address calculations so you don't have to (most of the time), and it includes a lot of safety mechanisms to prevent you from doing things that probably don't make sense. So for example, the types int * and long * are not assignment compatible. This is a good thing, because it prevents you from doing something like this: [code] int i; long *p; p = &i; *p = 5; [/code] If we assume that long is a wider type than int (which is not required but common these days), then that code would probably be disastrous. And the compiler can dutifully give you an error (in fact, it's required to "emit a diagnostic" for the first assignment). That is a good prompt that you should take a closer look. Now it sometimes happens that you really do know better than the compiler what needs to happen, and for such cases C provides an override. That override is the cast operator. You really want to have the above code? Fine, make the first assignment be [code] p = (long *)&i; [/code] (That may not work due to alignment considerations, another reason why the compiler warning/error would be helpful, but let's not worry about that now.) I.e. casting is a way of saying to the compiler "I know what I'm doing, let me do this." If you are in the habit of casting things all over the place, just because you can ("Hey, being explicit doesn't hurt"), then you are taking some type safety features of C out of the picture, and thereby preventing the compiler from helping you when it can.[/QUOTE]Hmm... a very good point. My initial pull-the-trigger reaction is that a programmer should know when a cast may or may not override disallowed-implicit-casts. Hmm... this requires further thought. [QUOTE=jyb;299876]No, no, memory checkers may be helpful, but I agree completely with your general point about avoiding uninitialized memory. Zeroing can be very effective for that (though I'll point out that there's no zeroing variant of realloc, so in this case it would be somewhat futile anyway). My point was just that your first post about this could have been confusing to Dubslow if he didn't know what you meant.[/quote]I'm not THAT much a novice :razz: If there's one thing I've learned the hard way in the last three months, it's that learning a language is not syntax (relatively easy to do) but learning its standard library well and thoroughly. I'm here now for the nuances (e.g. syntax for pointers and casting-as-compiler-override). (Yes, the reason I don't use calloc() is because the sizing is different from malloc() :razz: and realloc() has the same sizing as calloc(), I wish they'd just decided on one. No reason to use two different methods.) PS Are we going for longest single page on the forum? :smile: (PPS I'll get back to the macro thing at some point.) |
This is the same as before, except with a few more printfs. (I did understand some things would be changed for an 'actual' implementation, but it doesn't really matter for this demonstration purpose.)
[code]#include <stdio.h> #include <stdlib.h> #include <stddef.h> #define INIT 4 // Initial guess at line length (including trailing NULL) #define memerr(ptr) if( ptr == NULL) { printf("Memory error! Exiting!\n"); exit(1); } /* A macro to test for memory allocation errors (does not require trailing semi-colon). */ char* ReadLine(FILE* stream, size_t* size_ptr) { /* Returns a dynamically allocated array of chars containing the whole line of input read, and nothing more (besides terminating NULL). This can theoretically handle any size input, physical limitations aside. *Size_ptr will point to the number of chars/bytes in the array, which is strlen()+1. */ int size = INIT; char* base = (char*)malloc(INIT); memerr(base) // No ';' necessary, see macro char* out = base; // In theory, out should always point to the next blank byte. int count = 0; // In theory, &base[count] == out, or equivalently, out - base == count. char c; // Input buffer while( ((c = getc(stream)) != EOF) && (c != '\n') ) { if( !(count < size-1) ) { /* Need more memory, so double the current allocation. */ printf("Current text: %s\n", base); printf("Reallocating from %d bytes", size); base = (char*)realloc(base, ((size) *= 2)); /* Whenever we need more memory, double it. */ memerr(out) // No ';' necessary, see macro out = &base[count]; printf(" to %d bytes.\n", size); } *out++ = c; count++; } // Found an EOF or \n *out = 0; count++; // Now count == length of array printf("Reallocating from %d bytes", size); base = (char*)realloc(base, (size = count)); // The assignment isn't necessary, but I like the consistency of use. printf(" to %d bytes.\n", size); *size_ptr = (size_t)size; return base; }[/code] [code]Please enter a line of text: This is in theory a line of text that contains 200 characters. On the other hand, this will take a rather long time to type; at least, unlike Charles Dickens, I have the advantage of an automatic letter-producing machine, a.k.a. a keyboard. That poor sap had to write with his hand everything that came from his head. Current text: Thi Reallocating from 4 bytes to 8 bytes. Current text: This is Reallocating from 8 bytes to 16 bytes. Current text: This is in theo Reallocating from 16 bytes to 32 bytes. Current text: This is in theory a line of tex Reallocating from 32 bytes to 64 bytes. Current text: This is in theory a line of text that contains 200 characters. Reallocating from 64 bytes to 128 bytes. Current text: This is in theory a line of text that contains 200 characters. On the other hand, this will take a rather long time to type; at Reallocating from 128 bytes to 256 bytes. Current text: This is in theory a line of text that contains 200 characters. On the other hand, this will take a rather long time to type; at least, unlike Charles Dickens, I have the advantage of an automatic letter-producing machine, a.k.a. a keyboard. That poor sap Reallocating from 256 bytes to 512 bytes. Reallocating from 512 bytes to 318 bytes. I'm pretty sure you typed: This is in theory a line of text that contains 200 characters. On the other hand, this will take a rather long time to type; at least, unlike Charles Dickens, I have the advantage of an automatic letter-producing machine, a.k.a. a keyboard. That poor sap had to write with his hand everything that came from his head. It has 317 characters.[/code] It's one thing to see the results, but it's another to "see it in action". :smile: |
[QUOTE=jcrombie;299884]I guess we are in agreement then. Although I fail to see how malloc() + memset() would be superior to one call to calloc().
[/QUOTE] So do I. I don't recall ever saying (or implying) that it would. [QUOTE=jcrombie;299884] Yes, I like "long l = 0;". They are both integers, just one is bigger than the other. I even like "float f = 0.0;", since they are both floats (but non-intuitively a demotion). [/QUOTE] Truthfully, I can't imagine why those would be fine with you but not the pointer assignment. What about "double d = 1;"? Here there's an integer on the rhs, but a double on the lhs. When converting, the compiler not only has to change sizes, but it also has to put a completely different bit pattern into the memory set aside for d (I'm assuming that the machine uses IEEE 754 floating point formats, but just about any floating point format will have the same issue). How is assigning a pointer this way any different? [QUOTE=jcrombie;299884] I would also say that "out = NULL;" would be less ambiguous for someone just looking at that one line of code wrt what type is "out". [/QUOTE] I guess I can sort of see that. Looking at the line "out = 0;" one can't immediately tell that "out" should (not must, but let's assume good coding practices) be a pointer type. Still, if one relies on the rhs of an assignment to infer the type of the lhs, one will often be surprised. [QUOTE=jcrombie;299884] I hope you realize that making a whole bunch of people suffer in the present for a future that probably will never happen doesn't make sense to me. [/QUOTE] No, this just makes no sense to me at all. First of all, the decision wasn't originally about the future, it was about the past. Architectures (or at least their abstractions, as presented to the programmer) used to be a lot more varied than they are now. When the standard was written, it had to take current practice into account, and making assumptions about the particular style of architecture would have been onerous for some. But more importantly, I really don't understand what defect you think there is. In what way are any people suffering in the present because null pointers aren't required to have an all-bits-0 representation? How does this even come up for you, as a programmer? [QUOTE=jcrombie;299884] NULL is de facto address 0 and will probably always be that way. The standard just needs to acknowledge that. [/QUOTE] Why? How does this affect you at all as a programmer? And weren't you the one who said just yesterday that NULL need not represent address 0? What changed in the last day that made you think that was so onerous? |
[QUOTE=Dubslow;299897]
Hmm... the thing is, the standard-error-return for all the library functions is NULL, so it would make more sense to retain that practice and return something else for EOF. In either case though, what else could you return besides NULL? A pointer has to be NULL or pointing to something; pointing to something has to be understood as success of some sort. Perhaps just put the EOF in the one byte array (should there be a null-terminator) and return that? [/QUOTE] NOOOOOOOOOO! You've just made the same mistake again. What do you mean by "the EOF"? And how would you put it in a one-byte array? As we just discussed, EOF is not a character and it won't fit in a single byte. Here's a phrase I want you to memorize. If necessary, intone it like a mantra until you've embedded it deeply in your conscious and subconscious: "There is no such thing as the EOF character." This is important, and I thought I had managed to explain it already. Novices (and unfortunately plenty of non-novices) often imagine that there's something called the EOF character, and that it's stuffed at the end of all files. That seems to accord with the idea that you can read all the characters in a file with getc, and read EOF last. But it's utterly and completely wrong. If necessary, reread the posts from yesterday to see if you can understand why. This is exactly what I mean about this issue being subtle. You thought you understood it, but you're still making the mistake. I.e. this "simple" design decision for getchar/getc leads to many misunderstandings. [QUOTE=Dubslow;299897] Hmm... perhaps I misinterpreted something? Would you be willing to read [URL="http://publications.gbdirect.co.uk/c_book/chapter5/pointers.html"]5.3.5[/URL] and [URL="http://publications.gbdirect.co.uk/c_book/chapter5/pointer_expressions.html"]5.7.1[/URL] and tell me what you make of those? [/QUOTE] Sure, those look pretty good to me. Nowhere in those sections does it say that you should use casts when converting between void * and other pointer types. In fact, it says this: [QUOTE] Pointers to void can be freely converted backwards and forwards with pointers to any object or incomplete type. Converting a pointer to an object or an incomplete type to void * and then back gives a value which is equal to the original one: [/QUOTE] And then it gives some examples of such conversions without using a cast. So where does that give you the idea that one should use casts for such conversions? [QUOTE=Dubslow;299897] Yes, this is true, but my point was that for all practical intents and purposes, pointers [i]look and feel[/i] like they are their own primitive types, regardless of the technical details of what the compilers actually do. Excepting multiple declarations, the syntax certainly makes it look that way, and I only choose to support that illusion because (IMO) it's cleaner and makes more sense that way. [/QUOTE] If it makes more sense to you that way, then undoubtedly that's the way you should do it. As I said I have no particular desire to convince you here. I'll only add that in my experience most C programmers tend to change their minds about this and move to the more "standard" syntax as their knowledge of C becomes more sophisticated. [QUOTE=Dubslow;299897] To understand exactly what goes wrong, sure, but that's not necessary to understand that [i]something[/i] will go wrong if you know that EOF is not a char. [/QUOTE] And yet knowing that is apparently not enough to avoid serious conceptual mistakes, as above. That's part of what I meant about it being fundamental: if one has the wrong understanding, it requires a serious rethink of the way things work in order to internalize the correct model. [QUOTE=Dubslow;299897] (Yes, the reason I don't use calloc() is because the sizing is different from malloc() :razz: and realloc() has the same sizing as calloc(), I wish they'd just decided on one. No reason to use two different methods.) [/QUOTE] Huh? realloc() doesn't do anything like calloc(). It just takes the new number of bytes (and the existing pointer, obviously). |
[QUOTE=jyb;299924]NOOOOOOOOOO! You've just made the same mistake again. What do you mean by "the EOF"? And how would you put it in a one-byte array? As we just discussed, EOF is not a character and it won't fit in a single byte. Here's a phrase I want you to memorize. If necessary, intone it like a mantra until you've embedded it deeply in your conscious and subconscious: "There is no such thing as the EOF character." This is important, and I thought I had managed to explain it already.
Novices (and unfortunately plenty of non-novices) often imagine that there's something called the EOF character, and that it's stuffed at the end of all files. That seems to accord with the idea that you can read all the characters in a file with getc, and read EOF last. But it's utterly and completely wrong. If necessary, reread the posts from yesterday to see if you can understand why. This is exactly what I mean about this issue being subtle. You thought you understood it, but you're still making the mistake. I.e. this "simple" design decision for getchar/getc leads to many misunderstandings. [/quote]I realized after the fact that that's the whole reason for int return type, and that it obviously wouldn't go into a char array. I either was too lazy to edit it, or forgot, or only figured it out after the hour. Now: When you say EOF is not a char, do you simply mean that it cannot be represented by a char-type, or do you mean more fundamentally that it's different from what ints/chars/longs are? I'm pretty sure the former, since you said EOF is often chosen to be represented by -1. (I'll go back and reread the previous post about this in any case.) [QUOTE=jyb;299924] Sure, those look pretty good to me. Nowhere in those sections does it say that you should use casts when converting between void * and other pointer types. [/quote] In [URL="http://publications.gbdirect.co.uk/c_book/chapter5/sizeof_and_malloc.html"]5.5[/URL], which is examples pretty similar to ReadLine except not requiring realloc(), all malloc() calls are cast. [QUOTE=jyb;299924] I'll only add that in my experience most C programmers tend to change their minds about this and move to the more "standard" syntax as their knowledge of C becomes more sophisticated.[/quote]Really? Huh. Talk about a curve ball. Ask me again in a few years. :smile: [QUOTE=jyb;299924] Huh? realloc() doesn't do anything like calloc(). It just takes the new number of bytes (and the existing pointer, obviously).[/QUOTE][strike]I'm saying realloc() and calloc() use the same sizing methods, namely two separate size arguments which are then multiplied internally, vs. malloc() in which you need to do the multiplying and pass the result as the only size argument.[/strike] I'm sure we all wished they chose one or the other and stuck with it for all three functions. Edit: Whoops, messed that up. malloc() and realloc() share the one-argument, whereas calloc() has two. Either way, seems to be a silly choice to me. |
[QUOTE=xilman;299855]There's a (to my mind) serious conceptual difficulty with C and with many other languages. Back when it was invented, a char could hold a character (hence the name) but what it really held was a small integer. Even then there were problems, some of which have been raised in this thread, with what happened when widened to a short, int or long.
Toto, I've a feeling we're not in Ascii anymore. In other words, a char can no longer hold a character, in general. Some encodings allow £ or ß to be stored in a char but I don't know of any which allow 㒩 or ������ or ������ to be so stored. This is actually a serious problem which a few languages have addressed seriously. I'm a Perl fan partly because it has, to my mind, taken the problem seriously and has come up with a remarkably useful solution. The solution isn't perfect but it works well, by and large, despite having to try very hard to support older software and notions of what character strings should be.[/QUOTE] Huh, even Ubuntu/Chrome can't render that. The first is U+34A9, the second is (I think) U+130B8, and the third is (again, not entirely sure) U+12110. I have no idea what the last two look like. |
Just for the sake of correctness, there IS an eof character codable on one byte, which is still in use on many systems... :razz:
|
[QUOTE=jyb;299921]So do I. I don't recall ever saying (or implying) that it would.
Truthfully, I can't imagine why those would be fine with you but not the pointer assignment. What about "double d = 1;"? Here there's an integer on the rhs, but a double on the lhs. When converting, the compiler not only has to change sizes, but it also has to put a completely different bit pattern into the memory set aside for d (I'm assuming that the machine uses IEEE 754 floating point formats, but just about any floating point format will have the same issue). How is assigning a pointer this way any different? I guess I can sort of see that. Looking at the line "out = 0;" one can't immediately tell that "out" should (not must, but let's assume good coding practices) be a pointer type. Still, if one relies on the rhs of an assignment to infer the type of the lhs, one will often be surprised. No, this just makes no sense to me at all. First of all, the decision wasn't originally about the future, it was about the past. Architectures (or at least their abstractions, as presented to the programmer) used to be a lot more varied than they are now. When the standard was written, it had to take current practice into account, and making assumptions about the particular style of architecture would have been onerous for some. But more importantly, I really don't understand what defect you think there is. In what way are any people suffering in the present because null pointers aren't required to have an all-bits-0 representation? How does this even come up for you, as a programmer? Why? How does this affect you at all as a programmer? And weren't you the one who said just yesterday that NULL need not represent address 0? What changed in the last day that made you think that was so onerous?[/QUOTE] I think I can see where you're coming from here. It's good practice to follow the standard. (Agreed) The standard allows all kinds programmer behavior. (Agreed) We shouldn't criticize what appears to be bad programmer behavior as long as it conforms to the standard (Disagreed) eg. The standard allows "double d = 1;". True. But wouldn't "double d = 1.0;" be superior in some sense? Sure, I'm splitting hairs here, but I still think I have right to point that out. (Aside: why is the second possibly superior? Well, from my perspective, when you're reading someone else's code you also have to assess the skill level of that programmer. If you're looking for a bug, you might save yourself a whole lot of time by zeroing in on the amateurish looking code. The second instance would be showing me that this person knew they wanted a double assigned) About NULL: Yes NULL happens to be zero in all implementations. This is a coincidence. Relying on it is bad practice. Thousands of instances of people relying on it exist and thus this will never change. The standard should be updated to reflect this. eg. [CODE] char* p = (char*) malloc( .... ); if ( !p ) { printf some error message. } [/CODE] the correct code: [CODE] char* p = (char*) malloc( .... ); if ( p == NULL ) { printf some error message. } [/CODE] (Sorry about attributing use of malloc() + memset(). I thought for some reason you didn't like calloc() ) Cheers |
[QUOTE=Dubslow;299927]Huh, even Ubuntu/Chrome can't render that. The first is U+34A9[/quote]
Unicode Han Character '(same as 梴 裸) naked, to strip; to unclothe' (U+34A9) [quote]the second is (I think) U+130B8,[/QUOTE] Egyptian Hieroglyph. Mary's Dad: "How did you get the beans above the frank?" Ted: "I dont know, it's not like it was a well thought out plan." [quote] and the third is (again, not entirely sure) U+12110[/QUOTE]Sumero-Akkadian Cuneiform [URL="www.sumerian.org/sumlogo.htm"]Sumerian's phonetically more complex logograms[/URL][QUOTE]Gisal: oar; rudder (Giš, 'wooden tool', + sal, 'thin, wide')[/QUOTE] Perhaps also chosen for salaciousness. In the future kids will be giggling over obscure font characters instead of un-Bowdlerized dictionaries. |
| All times are UTC. The time now is 08:00. |
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.