mersenneforum.org  

Go Back   mersenneforum.org > Extra Stuff > Programming

Reply
 
Thread Tools
Old 2012-03-02, 23:59   #243
jyb
 
jyb's Avatar
 
Aug 2005
Seattle, WA

2·883 Posts
Default

Quote:
Originally Posted by ewmayer View Post
FWIW, I think gotos exception-handlers are a perfectly legitimate tool, and that like most tools of any use they can be abused.
Who gave you the copyright? You have copied the writing of others.

Oh wait, I just did too.
jyb is offline   Reply With Quote
Old 2012-03-03, 23:36   #244
xilman
Bamboozled!
 
xilman's Avatar
 
"𒉺𒌌𒇷𒆷𒀭"
May 2003
Down not across

2A2116 Posts
Default

Quote:
Originally Posted by ewmayer View Post
FWIW, I think gotos exception-handlers are a perfectly legitimate tool, and that like most tools of any use they can be abused.
I believe that the goto is evil incarnate and quite clearly the invention of the devil. Modern control constructs, such as if-then-else, while and the case statement render goto unnecessary and greatly improve the readability of software. For example, the following code eschews goto entirely and is therefore much more comprehensible than examples written in the style popular before Wirth's epiphany.

Code:
#include <stdio.h>

main ()
{
  unsigned char i = 50;
  while (i) {
    switch (i) {
    case 033:
      putchar (i*4);
      i = 0xd;
      continue;
    case ~16:
      putchar ((++i, i++, ++i, i++));
      continue;
    case 42:
      putchar (' ');
      continue;
    case 0x13:
      putchar (i+82);
      i = 27;
      continue;
    case 13:
      putchar (i<<3 | (1<<2));
      i = 111;
      continue;
    case 255:
      putchar (i &= (4<<3));
      continue;
    default: putchar (i^'1');
      i &= ~i;
      continue;
    case 'A' % 33:
      putchar (0x77);
    case 17:
    case 017:
    case 0x17:
      i = ~i;
      putchar (i/2);
      continue;
    case 062:
      putchar ('H');
      i = 19;
      continue;
    case 223:
      putchar (i=((i+5) >> 1));
      i -= 7;
    case 109:
      putchar (++i);
      i = (i == i);
      continue;
    case 1:
      putchar ((1<<3)*-(~8+1) + '$');
      i = ';';
      continue;
    case 'o':
      putchar (i);
      i = ~0;
      continue;
    }
  }
}
Paul
xilman is online now   Reply With Quote
Old 2012-03-04, 21:59   #245
Christenson
 
Christenson's Avatar
 
Dec 2010
Monticello

5·359 Posts
Default

Quote:
Originally Posted by rogue View Post
One of the product groups in my company mandates the use of gotos because they want all functions to have a single return statement.

Then again, they didn't support C99 until two years ago. Try convincing a bunch of 40- and 50-somethings that C99 isn't evil incarnate. For them K&R C is the bible and C++ is a four letter word.

Don't get me wrong, C is a great language, but so is C++. The one you choose is dependent upon what you need to do with it. If you care about performance and memory use above all else (like many of the apps we use for factoring, sieving, primality testing), then C (or asm) is for you. If your application would benefit from encapsulation, inheritance, and polymorphism (like most business apps), then C++ is for you. Most apps would benefit from a combination of both and fortunate for us, then can be used together in the same application.
If I need encapsulation and portability, and care what it costs to write the code, then PERL is probably best. Most code costs much more to write than to run. C++ just doesn't seem as well designed as C -- in particular, I like the approach taken by perl where all the basic data structures in the standard template library are intrinsics in the language.

Now, a single return statement has its uses....especially if your debugger doesn't support "step...until return" and you need to set a breakpoint when the function returns, like maybe it doesn't suport "step..over" either!

**************
The few times I have used "goto" in a C program, there has been a single target and you could argue (see Perl again) that something higher level was being implemented.

Example (real code -- startup sequence for a motor control, simplified..there are more checks, and an exit for setup with the hand-held programmer after a delay not shown)

do
{
top: verify e-stop is closed
verify command inputs don't ask for motion
check capacitor bank is charged
if e-stop is open, goto top
} while ( command inputs don't ask for motion)

I got myself into trouble when I used a "continue" statement here, and it's not really clear that this should have been a "do-while" loop in the first place.

Last fiddled with by Christenson on 2012-03-04 at 22:06
Christenson is offline   Reply With Quote
Old 2012-05-19, 03:02   #246
Dubslow
Basketry That Evening!
 
Dubslow's Avatar
 
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88

3·29·83 Posts
Default New ReadLine() function

The gotos are gone, but now they're replaced by a questionable macro. This isn't the best implementation ever, but at least the code is cleaner and shorter (I hope). I haven't looked at my previous attempt since the last posts about it, but then I don't intend to look unless this is declared superior (from a style point of view). (And yes, "superior" is highly subjective, but you all know what I mean.)

Edit: The logic isn't right, BRB.

Okay, a version that actually works.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

#define INIT 200
// 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. */
			base = (char*)realloc(base, ((size) *= 2)); 
			/* Whenever we need more memory, double it. */
			memerr(out) // No ';' necessary, see macro
			out = &base[count];	
		}
		*out++ = c;
		count++;	
		
	} // Found an EOF or \n
	
	*out = 0;
	count++; // Now count == length of array
	base = (char*)realloc(base, (size = count)); 
	// The assignment isn't necessary, but I like the consistency of use.

	*size_ptr = (size_t)size;
	return base;
}
Edit2: Does casting size_t to an int or vice versa cause problems? Here's my main:
[code]#include "readline.c"
int main() {
while(1) {
printf("\nPlease enter a line of text:\n");
size_t size;
char* line = ReadLine(stdin, &size);
printf("\nI'm pretty sure you typed:\n");
printf("\n%s\n", line);
printf("\nIt has %d characters.\n", (int)size);
}
exit(0);
}[/code]
And here's the somewhat dubious output:
[code]bill@Gravemind:~/bin/c∰∂ main

Please enter a line of text:
asdf

I'm pretty sure you typed:

asdf

It has -1729969584 characters.

Please enter a line of text:
This is a test.

I'm pretty sure you typed:

This is a test.

It has -1729969584 characters.

Please enter a line of text:
How long a line could this thing possibly handle? More than 200?

I'm pretty sure you typed:

How long a line could this thing possibly handle? More than 200?

It has -1729969584 characters.[/code]

Edit3: I just realized if the argument-pointer is pointed to a value, not getting the value stored independently; let me fix and test it. Yep, that was it. Now it appears to work fine.
Edit4:
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.

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.
Edit5:
Code:
Please enter a line of text:
Curvéball!

I'm pretty sure you typed:

Curvéball!

It has 11 characters.

Please enter a line of text:
Curveball täke 2!

I'm pretty sure you typed:

Curveball täke 2!

It has 18 characters.

Please enter a line of text:
Ând again?

I'm pretty sure you typed:

Ând again?

It has 11 characters.

Please enter a line of text:
SwȨet!

I'm pretty sure you typed:

SwȨet!

It has 7 characters.

Last fiddled with by Dubslow on 2012-05-19 at 03:50
Dubslow is offline   Reply With Quote
Old 2012-05-19, 04:54   #247
jcrombie
 
jcrombie's Avatar
 
"Jonathan"
Jul 2010
In a tangled web...

5·43 Posts
Default

Here is something that works pretty good for me (simple command line programs)

Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>


int main(int argc, char* argv[] )
{
  char* line = NULL;

  do {
    if ( line != NULL ) {
      free(line);
      line = NULL;
    }
    printf("\nPlease enter a line of text:\n");
    scanf("%ms", &line );
    printf("\nI'm pretty sure you typed:\n");
    printf("\n%s\n", line );
    printf("\nIt has %ld characters.\n", strlen(line) );
  } while ( toupper(line[0]) != 'X' );

  if ( line != NULL ) {
    free( line );
    line = NULL;
  }
}
It uses good old scanf(), but with a new twist -- the %m conversion. Too bad
it wasn't available when I first started programming.
jcrombie is offline   Reply With Quote
Old 2012-05-19, 05:09   #248
Dubslow
Basketry That Evening!
 
Dubslow's Avatar
 
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88

722110 Posts
Default

This isn't for any practical purpose, but for me to learn (That certainly is a crap ton easier.)
Dubslow is offline   Reply With Quote
Old 2012-05-19, 05:12   #249
jyb
 
jyb's Avatar
 
Aug 2005
Seattle, WA

2×883 Posts
Default

Quote:
Originally Posted by Dubslow View Post
The gotos are gone, but now they're replaced by a questionable macro. This isn't the best implementation ever, but at least the code is cleaner and shorter (I hope). I haven't looked at my previous attempt since the last posts about it, but then I don't intend to look unless this is declared superior (from a style point of view). (And yes, "superior" is highly subjective, but you all know what I mean.)

[snip]
Yes, I believe this version is much simpler and clearer overall. You do some new things which I think are unnecessarily dense (I'll detail them below), but the overall structure is a great improvement IMO. In particular, taking out the nested loops and simply having a test for the whether you need to grow in the main loop makes the code much easier to understand.

There are still some stylistic issues (which are of course subjective, so there may not be agreement by others in the forum) and one real correctness issue. I'll detail all of these that I can think of. The correctness issue is subtle, but it's quite important and fundamental to your understanding of C. How can it be both subtle and fundamental? I'll explain below.

By the way, I hope you tested this by setting INIT to a very small value (like, say, 4). That will give your test a chance to force the doubling to happen several times.

Okay, in no obvious order, here are some things I notice:

- It's out of spec!! Having the function return (via a pointer argument) the size of the array was not part of the function's description. Okay, you've obviously decided that your version is an improvement, since now the caller doesn't have to do a strlen to find out the length. That *can* be nice, but the caller might not actually care about the length, in which case it's unnecessary. And in this case it's actually a burden on the caller, since you're forcing him/her to pass a pointer to some size_t variable in order to call this function. If the caller doesn't actually care about the value, then that's going to be annoying for him/her. One way to relieve that burden is to make the argument optional. That is, allow the caller to pass NULL for the second argument, and make sure that your function checks for that before trying to assign the value. That's definitely a useful trick in a lot of situations, but frankly in this one I think most C programmers would simply not have ReadLine take the additional argument. If the caller wants the length, (s)he knows how to get it.

- Do not confuse the identifier "NULL" with the so-called null character. The former is a pointer value (specifically 0), while the latter is a character whose value is '\0' (or 0, if you prefer). The latter is sometimes written as NUL, because that's the ASCII code name for 0. You don't confuse them in any actual code, but your comments confuse them.

- Your method for handling allocation errors isn't unreasonable (really, what should ReadLine do if it can't allocate the memory? If it returns NULL then that's ambiguous). However, your use of the macro is dangerous in at least one respect. See post #215 for some discussion of this.

- Re casting size_t, see post #202.

- I see that unlike the previous version, you've stopped putting multiplies by sizeof(char) in your calls to malloc/realloc. It's good that you recognize that it's unnecessary, since char is guaranteed to be 1 byte in C. So can chars really only store 256 different values? No, that's not required. There is no guarantee that a byte is only 8 bits. If that sounds strange, I can explain.

- I see that you're casting the return value of malloc and realloc. Doing this exposes a rift between C and C++. In C this is definitely unnecessary, and often simply not done. The type void * is assignment compatible with other pointers to objects or incomplete types, so no cast is necessary. The situation is somewhat more complicated in C++ (because casting in general is more complicated), so C++ programmers might insist that casting is the right thing to do.

- Your syntax for pointer declarations is common, but nonetheless slightly dangerous. E.g. you have
Code:
char* base;
The potential problem with this is that someday you're going to commit the same error that legions of novice C programmers have committed before you: you're going to add another variable to the same declaration, like so:
Code:
char* base,out;
What could be simpler, right? You wanted two char * pointers, and there they are. But wait! This actually declares base to be a char * and out to be a char. This is why declarations in C commonly look more like this:
Code:
char *base, *out;
I.e. the * goes with the declarator, not the type. But as I said, you are not alone in doing this, so there will undoubtedly be some disagreement here.

- Your use of both the base and count variables is really just redundant. *out is always just base[count]. So pick one and use that, ditch the other.

- CORRECTNESS: you are storing the return value of getc in a variable of type char, then comparing it to EOF. This fails to work in a subtle way. getc (and getchar and fgetc) return an int, not a char, so if you store the return value in a char then you are truncating the value. Ah, you may say, but so what? The value should just fit in a char anyway, right? NO. The reason it returns an int is so that it can store the value of every possible char value *in addition to* the value EOF. So what can go wrong? Well, that actually depends on whether char is a signed or unsigned type (a decision left up to the compiler). Something can go wrong either way, but it's different, depending on the signedness of char.

To take typical values, EOF is usually -1 (of type int) and char is 8 bits. On normal modern machines that means EOF is stored as 0xFFFFFFFF. The char of value 0xFF is perfectly valid (if somewhat uncommon, at least in text files). What happens if you're reading a file that contains such a character? Well when you read that character, getc will return an int whose value is 0x000000FF. If you were to compare that to EOF, you can see that they're different. But before doing that comparison you're storing the value in a char, which truncates it to 0xFF. When you compare that to EOF, it's converted back to an int for the comparison. But how it's converted depends on the signedness of char.

If char is signed, then it gets sign-extended and converted to 0xFFFFFFFF, which is different from the originally returned value. And in fact it's equal to EOF. So your code would think that it had seen EOF and act accordingly, thereby ignoring the rest of the file.

If char is unsigned, then it gets converted back to int as 0x000000FF, just like its original value. So no problem, right? Wrong. Because think about what happens when you really do hit the end of file. getc returns EOF, which is 0xFFFFFFFF. You stuff it into a char which turns it into 0xFF. When you then compare that to EOF it gets extended to 0x000000FF. So the comparison will be false! I.e. your code will never correctly detect the EOF condition.

The solution is obvious: just make c an int, not a char. If its value fits in a char then it will be truncated later when you store it in the array, and that's fine. But that won't happen until after you've had a chance to test it against EOF.

This is a mistake that is very commonly made by novices. Sad to say, it's also a mistake that's very commonly made by somewhat experienced C programmers. It seems that few people ever have this issue explained to them in an adequate way; I think that's because it requires a sophisticated understanding of the type system and conversions, but getc/getchar is usually taught very early on. So coders see this when they're still too green to understand all the nuances, and they never learn it right. That's why I say it's both subtle (it comes up only in obscure circumstances) and fundamental (because it involves a deep understanding of types and conversions).

I'll let you know if anything else occurs to me about your code.
jyb is offline   Reply With Quote
Old 2012-05-19, 07:03   #250
jcrombie
 
jcrombie's Avatar
 
"Jonathan"
Jul 2010
In a tangled web...

5×43 Posts
Default

@jyb Just want to says thanks again for that tip on computing LM primitives.

Ok, here is my comments on your code:

First off, I'd say don't worry too much about programming style. What you don't
want to do is have your code look different than everybody elses. It really makes
the code hard to read. Be flexible.

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.

A nitpicky thing -- I prefer calloc() to malloc(). Using malloc() introduces a lot of
randomness to your program's behaviour. Better to clear it out and then when you're
tracking down your bugs, things will behave more predictably.

I think jyb mentioned this, but I see that you assigned 0 to a pointer. This is
programming by coincidence. It just so happens that NULL is address 0 on all
implementations that I've seen, but theorectically that could change. Besides, what
you want is an address type and not an integer.

I usually stick the * on the left hand side and just never ever put multiple declarations
on one line. But be flexible, right? (I believe that this area was the one regret Brian Kernighan had when he co-wrote C)

Hope that helps a bit.
jcrombie is offline   Reply With Quote
Old 2012-05-19, 16:56   #251
Dubslow
Basketry That Evening!
 
Dubslow's Avatar
 
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88

722110 Posts
Default

I think many of these can be attributed to the fact that this is essentially the only C I've done since February (first attempt), and in lieu of that it's mostly been Python or Java (e.g. exit(1); vs. return 1; vs. return NULL; that was the Python talking ).
Quote:
Originally Posted by jyb View Post
By the way, I hope you tested this by setting INIT to a very small value (like, say, 4). That will give your test a chance to force the doubling to happen several times.
Nope , but next time I'm at my own computer, I'll give it a shot
Quote:
Originally Posted by jyb View Post
- It's out of spec!! Having the function return (via a pointer argument) the size of the array was not part of the function's description. Okay, you've obviously decided that your version is an improvement, since now the caller doesn't have to do a strlen to find out the length. That *can* be nice, but the caller might not actually care about the length, in which case it's unnecessary. And in this case it's actually a burden on the caller, since you're forcing him/her to pass a pointer to some size_t variable in order to call this function. If the caller doesn't actually care about the value, then that's going to be annoying for him/her. <snip for post length limit>
I think this is indirectly a case of other languages, in this case Java, slipping through. In Java there are no explicit pointers, so you do everything as arrays, but they provide you with the object-field-like array.length which is the length. Going back to C, I had it in my head that I always had to keep track of the length of arrays somewhere (edit: see post #202 :P), but forgot about strlen() applying to char arrays (though for other types AFAICT there's no easy method to get the length of a chunk of memory). Even though it occured to me that the caller could use strlen(), I didn't think to remove the extra argument.
Quote:
Originally Posted by jyb View Post
- Do not confuse the identifier "NULL" with the so-called null character. The former is a pointer value (specifically 0), while the latter is a character whose value is '\0' (or 0, if you prefer). The latter is sometimes written as NUL, because that's the ASCII code name for 0. You don't confuse them in any actual code, but your comments confuse them.
Yeah. I think as long as I remember not to capitalize null as far as terminating strings goes, I'll be fine. (And as you said, I made sure not to mess up the code.)
Quote:
Originally Posted by jyb View Post
- Your method for handling allocation errors isn't unreasonable (really, what should ReadLine do if it can't allocate the memory? If it returns NULL then that's ambiguous). However, your use of the macro is dangerous in at least one respect. See post #215 for some discussion of this.
See my first comment (edit: and the following post.) This was just a plain slip up, though if I had caught myself I probably would have returned NULL. How is that ambiguous? If I read an empty line, (I think) it returns a 1-byte chunk initialized to zero, i.e. just the null terminator, which isn't the same thing as NULL, because *out==0, not out==0.
Quote:
Originally Posted by jyb View Post

- Re casting size_t, see post #202.
'gcc -Wall' gave me a warning about incompatible pointer types without the explicit cast (although now there are no pointers, I kept the cast anyways. It doesn't hurt). And, going back to that post now, I see that we already talked about both when to return NULL and how to keep track of array sizes :D
Quote:
Originally Posted by jyb View Post
- I see that unlike the previous version, you've stopped putting multiplies by sizeof(char) in your calls to malloc/realloc. It's good that you recognize that it's unnecessary, since char is guaranteed to be 1 byte in C. So can chars really only store 256 different values? No, that's not required. There is no guarantee that a byte is only 8 bits. If that sounds strange, I can explain.
Nope, the reference I've been using (I think bsquared recommended it?) says that the Standard (I'm pretty sure that means C89/ANSI C) does not specify the size of a byte.
Quote:
Originally Posted by jyb View Post
- I see that you're casting the return value of malloc and realloc. Doing this exposes a rift between C and C++. In C this is definitely unnecessary, and often simply not done. The type void * is assignment compatible with other pointers to objects or incomplete types, so no cast is necessary. The situation is somewhat more complicated in C++ (because casting in general is more complicated), so C++ programmers might insist that casting is the right thing to do.
This one is due to that reference above. Though I can't find the part where it's explicitly stated, throughout the book (mostly in Chapter 5) all malloc()s are cast to whatever type. That was the whole point of void*, if that book is to be believed.
Quote:
Originally Posted by jyb View Post
- Your syntax for pointer declarations is common, but nonetheless slightly dangerous. E.g. you have
Code:
char* base;
The potential problem with this is that someday you're going to commit the same error that legions of novice C programmers have committed before you: you're going to add another variable to the same declaration, like so:
Code:
char* base,out;
What could be simpler, right? You wanted two char * pointers, and there they are. But wait! This actually declares base to be a char * and out to be a char. This is why declarations in C commonly look more like this:
Code:
char *base, *out;
I.e. the * goes with the declarator, not the type. But as I said, you are not alone in doing this, so there will undoubtedly be some disagreement here.
One thing that I came away with about pointers is that for all purposes that I can see, they are effectively primitve types, if not labelled as such. Their use is syntactically built into the language, without which it wouldn't be C. 'char c' has a very different type from 'char* d'. For instance, for someone is isn't looking real hard, 'int *x=0' looks like a pointer-derefence, when in fact all you're doing is setting it to NULL. 'int* x=0' is much clearer. Writing 'char *d' to me is just a great way of mixing the type-declaration with the variable name d, so I do 'char* d'. It's just unfortunate that pointer-dereference is done with *d =..., and if I were designing C myself I wouldn't have chosen that notation; It's just unfortunate that pointers aren't actually their own types, and if I were designing C myself I would have made it such and chosen a different dereference notation; if jcrombie is to be believed, Mr. Kernighan shares this view. (When I first came to these conclusions, I realized that 'char* a, b' is bad, but I decided that it would just be best to keep them on separate lines, much like jcrombie does. I shall rue the day when I break my own rule.) tl;dr: Agree to disagree
Quote:
Originally Posted by jyb View Post
- Your use of both the base and count variables is really just redundant. *out is always just base[count]. So pick one and use that, ditch the other.
I was feeling lazy. Yes, they are redundant, but hey. (If this was for something other than denomstration purposes, I'd choose one and stick with it. Doing *out++ is simpler IMO, but when we realloc is when we need count = out-base. I'd probably just scratch count "IRL".)
Quote:
Originally Posted by jyb View Post
- CORRECTNESS: you are storing the return value of getc in a variable of type char, then comparing it to EOF. This fails to work in a subtle way. getc (and getchar and fgetc) return an int, not a char, so if you store the return value in a char then you are truncating the value. Ah, you may say, but so what? The value should just fit in a char anyway, right? NO. The reason it returns an int is so that it can store the value of every possible char value *in addition to* the value EOF. So what can go wrong? Well, that actually depends on whether char is a signed or unsigned type (a decision left up to the compiler). Something can go wrong either way, but it's different, depending on the signedness of char.
<snip for pll>

The solution is obvious: just make c an int, not a char. If its value fits in a char then it will be truncated later when you store it in the array, and that's fine. But that won't happen until after you've had a chance to test it against EOF.

<snip for pll>
I understand the subtle, but I'm not sure how this is fundamental to C (like on the same level as pointers). I had in fact seen that getc() etc. are declared to return ints, but as you say, I couldn't find a good answer for 'Why?'. Thanks to you, I now have (I'm not sure that a detailed understanding of types and conversions is necessary; the only key part is that technically EOF is not a char value. The rest is easily inferred from long->int = truncation, int->char = truncation (or some sort of clobbering).
Code:
int c; while( c=getc()...) { ...*out++ = (char)c; ...}
Yes, the cast isn't necessary, but that's the Java showing through, where it is necessary, and it can't posisbly hurt the C. Here, though talking about function declarations/prototypes and implicit return types (from "Old C"), the point is always declare your functions, and even though it's valid, it's better to explicitly return an int then just let the compiler assume it, because then the guy reading the code knows it's deliberate and not a mistake. That's also at least partially why I cast malloc()s. Being explicit doesn't hurt, and it's required in many other languages, so there's no reason not to do it in C. (This is also why I used sizeof(char) in the first attempt; even then I knew chars were one byte, but I was just being explicit. This time I was being lazy )

Last fiddled with by Dubslow on 2012-05-19 at 17:11
Dubslow is offline   Reply With Quote
Old 2012-05-19, 17:05   #252
Dubslow
Basketry That Evening!
 
Dubslow's Avatar
 
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88

3×29×83 Posts
Default

Since I was out of room, this is really just a short addendum to my previous post.
Quote:
Originally Posted by jcrombie View Post
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.
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:
Originally Posted by jcrombie View Post
A nitpicky thing -- I prefer calloc() to malloc(). Using malloc() introduces a lot of
randomness to your program's behaviour. Better to clear it out and then when you're
tracking down your bugs, things will behave more predictably.
Meh -- in this case at least, it's easy enough to make sure everything is initialized. I have used calloc before, and it is nice. (The main problem is that they use different methods for sizing )
Quote:
Originally Posted by jcrombie View Post
I think jyb mentioned this, but I see that you assigned 0 to a pointer. This is
programming by coincidence. It just so happens that NULL is address 0 on all
implementations that I've seen, but theorectically that could change. Besides, what
you want is an address type and not an integer.
http://publications.gbdirect.co.uk/c.../pointers.html
There they say to use 0, not NULL, and here:
Quote:
For reasons which still escape us, there is an ‘implementation defined null pointer constant’ defined in <stddef.h> called NULL. Since the language explicitly defines the integer constant 0 to be the value which can be assigned to, and compared with, a null pointer, this would seem to be unnecessary. However, it is very common practice among experienced C programmers to write this sort of thing:
Code:
#include <stdio.h>
#include <stddef.h>
FILE *fp;
if((fp = fopen("somefile", "r")) != NULL){ 
/* and so on */
I think I assign 0, but compare to NULL. Arbitrary, yes, but hey. (This might have chaned since C89, but most compilers (read: gcc) still use that standard anyways AFAICT.)
Quote:
Originally Posted by jcrombie View Post
I usually stick the * on the left hand side and just never ever put multiple declarations
on one line. But be flexible, right? (I believe that this area was the one regret Brian Kernighan had when he co-wrote C)



Addendum on pointers: I think the confused type/deferencing notation is confusing for a lot of beginners; understanding that in a declaration 'char *d', 'd' as a variable has its own value which is separate from *d. It really does look like you're declaring the variable *d, so working with d is counter-intuitive. That's why I like 'char* d' because then it's clear what the variable actually is, and then *d is just the natural extension of d. IM(A)O this makes pointers conceptually cleaner, and therefore easier to learn.

Last fiddled with by Dubslow on 2012-05-19 at 17:37
Dubslow is offline   Reply With Quote
Old 2012-05-19, 17:23   #253
xilman
Bamboozled!
 
xilman's Avatar
 
"𒉺𒌌𒇷𒆷𒀭"
May 2003
Down not across

3·5·719 Posts
Default

Quote:
Originally Posted by Dubslow View Post
I In Java there are no explicit pointers, so you do everything as arrays, but they provide you with the object-field-like array.length which is the length. Going back to C, I had it in my head that I always had to keep track of the length of arrays somewhere (edit: see post #202 :P), but forgot about strlen() applying to char arrays (though for other types AFAICT there's no easy method to get the length of a chunk of memory). Even though it occured to me that the caller could use strlen(), I didn't think to remove the extra argument.
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.
xilman is online now   Reply With Quote
Reply

Thread Tools


All times are UTC. The time now is 08:00.


Fri Aug 6 08:00:59 UTC 2021 up 14 days, 2:29, 1 user, load averages: 2.27, 2.34, 2.42

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.

This forum has received and complied with 0 (zero) government requests for information.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation.
A copy of the license is included in the FAQ.