![]() |
|
|
#243 |
|
Aug 2005
Seattle, WA
2·883 Posts |
|
|
|
|
|
|
#244 | |
|
Bamboozled!
"𒉺𒌌𒇷𒆷𒀭"
May 2003
Down not across
2A2116 Posts |
Quote:
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;
}
}
}
|
|
|
|
|
|
|
#245 | |
|
Dec 2010
Monticello
5·359 Posts |
Quote:
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 |
|
|
|
|
|
|
#246 |
|
Basketry That Evening!
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88
3·29·83 Posts |
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;
}
[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. ![]() ![]() 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 |
|
|
|
|
|
#247 |
|
"Jonathan"
Jul 2010
In a tangled web...
5·43 Posts |
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 wasn't available when I first started programming. |
|
|
|
|
|
#248 |
|
Basketry That Evening!
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88
722110 Posts |
This isn't for any practical purpose, but for me to learn
(That certainly is a crap ton easier.)
|
|
|
|
|
|
#249 | |
|
Aug 2005
Seattle, WA
2×883 Posts |
Quote:
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; Code:
char* base,out; Code:
char *base, *out; - 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. |
|
|
|
|
|
|
#250 |
|
"Jonathan"
Jul 2010
In a tangled web...
5×43 Posts |
@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. |
|
|
|
|
|
#251 | |||||||||
|
Basketry That Evening!
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88
722110 Posts |
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:
, but next time I'm at my own computer, I'll give it a shotQuote:
![]() Quote:
Quote:
(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.'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 :DQuote:
Quote:
Quote:
Quote:
Quote:
(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; ...}
)
Last fiddled with by Dubslow on 2012-05-19 at 17:11 |
|||||||||
|
|
|
|
|
#252 | |||||
|
Basketry That Evening!
"Bunslow the Bold"
Jun 2011
40<A<43 -89<O<-88
3×29×83 Posts |
Since I was out of room, this is really just a short addendum to my previous post.
Quote:
Quote:
)Quote:
There they say to use 0, not NULL, and here: Quote:
Quote:
![]() 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 |
|||||
|
|
|
|
|
#253 | |
|
Bamboozled!
"𒉺𒌌𒇷𒆷𒀭"
May 2003
Down not across
3·5·719 Posts |
Quote:
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. |
|
|
|
|