Intermediate C Programming Notes
eskimo.com
eskimo.com
char *itoa(int n, char buf[]) {
sprintf(buf, "%d", n);
return buf;
}
... but you'd really want to take a length parameter and call snprintf to prevent buffer overflows. This pre-C99 style C is one of the reasons C has a bad rep wrt memory hazards.edit: Anyone interested in this should probably read through something newer, like http://c.learncodethehardway.org/book/
Oops, make that eleven, for the zero terminator.
Oops, make that twelve, for the sign.
Oops, int can be 64 bits, better make that twenty-one.
Oops, sprintf honors locale. There may be thousands separators in there. Make it twenty-seven (or twenty-eight? I lost count. Also, looking at lconv, decimal points and thousands separators need not be at every third digit and may be more than one character long)
Yeah, I am sure that length parameter is superfluous.
struct dmatrix {
size_t rows, cols;
double *data;
}
and then a simple double dmatrix_get(struct dmatrix *m, size_t r, size_t c) {
return m->data[r * m->cols + c];
}
is all you need? In particular I find explicit array sizes in function headers very contrived and I have never used them in serious code. Normally it's just a pointer and a length argument similar to e.g. read/write functions in the standard library. double* dmatrix_get_row(struct dmatrix* m, size_t r){
return &m->data[r*m->cols];
}
A pointer-to-pointer approach I think is most suitable for an actual array-of-arrays, where the length of each subarray can be different. Then you store an array of pointers to the first element of each subarray, and if the data is contiguous you can use that to calculate lengths as well. typedef struct {
char c_str[25];
} number_string_t;
number_string_t
itoa(int i) {
number_string_t retval = {0};
int written = snprintf(&retval.c_str[0], sizeof(retval.c_str), "%d", i);
// Error checking.....
return retval;
}Are the performance issues in the case the architecture does aggregate type returning differently?
Whether you can put your return value in a register, or whether you need to housekeep the stack. That's a performance difference.
And yes, different platforms handle it differently. IIRC SysV PowerPC only supports up to 64 bits in r3,r4 but AIX POWER forces all returned structures into memory. I think SPARC ABI prior to V9 did not use registers either.
I admit to not having read the entire thing, but I've looked through most of it. A few things that stood out to me:
1. They dance around what a multidimensional array in C actually is, and the line "C does not have true multidimensional arrays." is simply false. I'd let it go, but there's a disconnect from when they go from sections 23 and 23.1 to section 23.2. Section 23.2 is most definitely not a multidimension array. Allocating an actual multidimensional array is actually much simpler, just malloc a block of memory into a pointer to an array:
int (*a)[3] = malloc(2 * 3 * sizeof(int));
This allocated array works just like any other multidimensional array in C and can be passed as such, and it's much easier to use then the pointer-to-pointer approach. The pointer-to-pointer approach is the only one shown though, as though it's the only option, even though it's not equivalent.2. In section 18.1.7, type qualifiers, they say 'const' is fairly rare to see. That may have been true when this was written, but in properly written code today it should be decently common to see, mostly in the case that functions are receiving pointers to data they shouldn't be changing and for string literals that shouldn't be changed. Not using const correctly is a source for lots of annoying compilation warnings and runtime errors.
3. The very end of section 20.1 notes that there is no way to define a variable argument macro. That was true at the time of writing (kinda), but it's not true with C99.
4. In section 18.3.1, the switch statement, everything's perfectly fine except that where it says it's common to use a list of #define's over a switch. It's much better to use an enum type to do so since it can annotate your code a bit better, acts basically the same way, you'll get better compilation warnings from it, and in most cases you can let the compiler assign the numbers for you and thus not risk any conflicts from adding and removing options. For whatever reason, I couldn't find any info on enum's in the entire read, which is a huge red-flag.
5. A lot of their function pointer information involves pointers defined as:
int (*p)();
The huge issue with that is that they defined it with an unknown parameter list, and it was intended by the author. There are basically zero times when you actually want to do that, in most cases you should either include the argument types, such as: int (*p)(int);
Or put void to indicate no arguments: int (*p)(void);http://shop.oreilly.com/product/0636920025108.do
It has mixed reviews - my impression from reading it was that if you agree with the author's choice of tools, it's a good read. There's supposedly a new edition coming out in September.
There's also Learn C the Hard Way:
http://c.learncodethehardway.org/book/
I've struggled to find any other modern books on C programming.
Mixed reviews applies to this book as well. For how often I see it recommended on the net these days, I am disappointed at how many annoying little lies it has in it; it's littered with them. It's also very opinionated (and in my opinion some of these opinions are bullshit) and again it likes to impose the author's choice of tools on you, to the point where he doesn't really even bother explaining how to live without them.
It could do a better job of introducing standard C terminology instead of only presenting the ideas the author came up with, and which often conflict with the concepts as defined by C. It could also do a better job of explaining C's pitfalls and UB instead of just making the reader break certain things and see how his Linux/OS X/Valgrind responds.
EDIT n+1: I just skimmed again through half of the book. Zed actually uses bstring in one of his examples. I don't know whether that should be taken as an implicit endorsement or not, but shame on him. Also, his ideas about secure string handling make me cringe. Also, his "safer" string copying function is not correct. In fact the entire book is littered with lies about strings which no doubt contribute to the confusion people have about them. I'm sure he's well intentioned and he makes good points but for someone wanting to learn how to do C cleanly and securely, I just cannot recommend this book.
The bstring thing and his safer copy function are still my favorites for the irony of it. He's sounds so condescending when he attacks C strings and an old K&R example. And then goes on to show how to do it wrong, without really even solving the problem he's trying to solve. And he keeps repeating the lie about C strings just being nothing but an array of chars.
I originally wanted to write more and point out some other important things. For example, “long long” never gets mentioned! This is not a good book to learn C. I believe it could be an adequate (or even good) one to train already existent C skills, but it isn't even that at the moment.
Okay, it isn't a final version. We have to wait until next year for that, but LCTHW gets too much positive attention for being just another less-than-mediocre tutorial book on the Internet.
I don't know what'd be a better replacement; it depends so much on the use case. I'd say there is no one-size-fits-all solution to string handling in C. For most code which occassionally deals with strings but isn't really focusing on text, the usual standard library functions and BSD extensions are just fine. I mean malloc, free, strdup, strlcat, strlcpy, strchr, strstr, snprintf, and so on.
For my code editor, I wrote my own dynamic text buffer with the goal of supporting large files with binary data in it. This is highly application specific code and wouldn't make a good general purpose string library.
I skimmed through the bstring code long ago, and I recall seeing quite a bit of questionable or hairy code. But abusing undefined behavior for security purpose was the final stake so I gave up and decided I do not want to use or endorse that library. Also, using ints for string lengths is just stupid if your code ever has to interact with the outside world. Why does bstring do this?
EDIT: Actually, let me just quote the (in)security statement here:
> Bstrlib is, by design, impervious to memory size overflow attacks. The reason is it is resiliant to length overflows is that bstring lengths are bounded above by INT_MAX, instead of ~(size_t)0. So length addition overflows cause a wrap around of the integer value making them negative causing balloc() to fail before an erroneous operation can occurr.