C puzzles
gowrikumar.com
gowrikumar.com
In spite of the kinds of easy to make mistakes that are highlighted on the site, I'm finding it to be a lot of fun - not having my hand held by frameworks that try to stop me from shooting myself in a foot is refreshing and brings back that feeling of "I can build whatever the hell I want in whatever f'd up way I want" that C always held for me.
Too, in the years since I've last done something like this, I've learned a lot - so he process is made better by applying ideas I've internalized to old problems, measuring effects on performance, etc.
Core dumps are a thing that I can cause without triggering some kind of obscure runtime bug and it's oddly exciting.
int CountBits (unsigned int x )
{
static unsigned int mask[] = { 0x55555555,
0x33333333,
0x0F0F0F0F,
0x00FF00FF,
0x0000FFFF
} ;
int i ;
int shift ; /* Number of positions to shift to right*/
for ( i =0, shift =1; i < 5; i ++, shift *= 2)
x = (x & mask[i ])+ ( ( x >> shift) & mask[i]);
return x;
}
as opposed to: int countBits (unsigned int x) {
static unsigned int mask[] = {
0x55555555,
0x33333333,
0x0F0F0F0F,
0x00FF00FF,
0x0000FFFF
};
int i;
int shift; // Number of positions to shift to the right
for (i = 0, shift = 1; i < 5; i++, shift *= 2)
x = (x & mask[i]) + ((x >> shift) & mask[i]);
return x;
}Having more compact code allows to have more code on the screen without using the scrollbar. For me, it is also more readable because I am used to it.
if (foo) {
| blah;
| blah;
| blah;
| blah;
| blah;
| blah;
| blah;
| blah;
| blah;
}That style, especially in other languages with callback hell, feels tight. I can't breathe...
That feeling of can't breathe is far far more of a potent dagger in the heart, than simply scrolling one extra mouse wheel in a Class.
The open and airy, instantly identifiable shape of the code is just like the fresh sea air.
An old friend at work used to call that style "NotepadOpenBinaryFileStyle". The reason that stuck with me was due to its accuracy!
Putting the brace on the same line also makes it easier to grep for function definitions.
So why not:
if (
x>y
)
{
code;
...
}
}
(I've actually seen that in real code.) unsigned int countBits (unsigned int x)
{
int i;
int shift; // number of positions to shift to the right
static unsigned int mask[] = {
0x55555555,
0x33333333,
0x0F0F0F0F,
0x00FF00FF,
0x0000FFFF};
for (i = 0, shift = 1; i < 5; i++, shift *= 2)
x = (x & mask[i]) + ((x >> shift) & mask[i]);
return x;
}
Where to put the first '{' depends on your development environment, as some hide the line where the bracket is, and some don't (when you hide a function or loop). Array initialization format is a tricky one, as the spacing is very dependent on how you are trying to visualize the data, but I would agree that all (leftmost) elements should be aligned to the same margin. I find the blank line between array and other declarations puzzling for both examples. I prefer to leave blank lines between code of the same indent (outside of declarations and initializations). Commenting using '//' allows you to comment out blocks of code on both sides of your line comment, so I'm with you there.P.S. I do not like Hacker News' paragraph formatting
int count_bits(uint32_t x)
{
static const uint32_t mask[] = {
0x55555555,
0x33333333,
0x0f0f0f0f,
0x00ff00ff,
0x0000ffff
};
for (int i = 0, shift = 1; i < 5; ++i, shift *= 2)
x = (x & mask[i]) + ((x >> shift) & mask[i]);
return x;
}A 2 space indent is more compact. A 4 space indent is more readable for older people. Putting braces around blocks on their own lines highlights blocks. Putting braces inline is again more compact. Outdenting declarations highlights an important piece of information. Keeping them in line focuses on blocks. And so on.
None of these choices are particularly important. Being CONSISTENT does. This is his site, and his code. Adapt. If he comes to work with you, then he'll need to adapt to you.
Yes, it is a shock to see someone whose style is unfamiliar to you. Each of your choices has a reason behind it, and you may have thought through all of them. But it matters less than you think. Grow up and get over it. Be consistent with code around you.
I sometimes forget what i did in last function, so in my code there might be inconsistencies and a mix of both styles found. I hope to get consistent some day but a working code is the priority right now :)
Good luck with reaching agreement with your fellow developers...
The thing is - there isn't much consistency in his syntax. The most obvious example is the indentation, that appears to be 8 spaces except for one line which is indented with 4; there isn't consistency in whether to have a space or not before semicolons, before and after parentheses, or before and after binary operators.
I have been wondering for a while now why is it that everybody is stuck with this typewriter/punchcard mentality and assumes that only a fixed-width (non-proportional) font could be used to display program code on a computer screen. Would it not be more appropriate, in this century, to realize that since indentation (and spacing in general) is essentially something that only pertains to the graphical representation of program's code, it should be left to the editor/viewer software (and its user) to set, given a single tab or space character, the number of pixels (or inches or any other unit of length) that should be used to represent the indentation or white space? This should have nothing to do with the width of a character, and therefore using modern proportional fonts should be just as convenient and natural as it is when using a word processor.
False assumption.
Suppose a language exists which: 1.) truncates all characters beyond the 80th prior to interpretation; 2.) will throw an exception during run-time if tab characters are used for indentation; 3.) strictly defines, in BNF, valid cases for the first 7 column-wise characters, of which 7 spaces is distinctly meaningful.
Although it may be "this century", I think I speak for more than a few who continue to maintain the corrected mistakes of our predecessors out of economic necessity...and I'm not talking about reaping a paycheck either.
I really thought that as a species we had decided it was a preference thing.
For anyone who is going through these exercises, I would encourage you to copy-and-paste the code snippets into a regular text editor.
Not true: my actual automatic syntax highlighting system (Vim) uses the same colour for reserved words and for labels. So I think it's fair play.
I bet even more would be confused if the 'defa1ut' one said 'defau1t' --- showing how font choice can also be very important to readability.
This is why it's considered bad form to cast the result of malloc in C. Of course, modern compilers will warn about implicit prototypes, and as of C99 it's no longer legal.
test.c:10:12: warning: implicitly declaring library function 'malloc' with type 'void *(unsigned long)'
[-Wimplicit-function-declaration]
I think that having a prototype mismatch with the actual declared function is undefined behavior, so this is a legal way to resolve it, but not every compiler will. Older compilers tended to treat malloc as just another function and wouldn't do this.I was able to replicate the older behavior by wrapping malloc in my own function. In one file:
int main()
{
int* p;
p = (int*)wrapped_malloc(sizeof(int));
*p = 10;
return 0;
}
And in another file: void *wrapped_malloc(int size) {
return malloc(size);
}
Compiling them into one program results in a crash. The relevant bit of assembly is: 0000000100000f4f movslq %eax, %rdi
So it is indeed only extracting the lower 32 bits of the returned pointer.The page looks pretty old (the IA-64 reference sure is dated, anyway) so I'd guess that it was referring to an older compiler that didn't have a special case for malloc like this.
int main()
{
int* p;
p = (int*)wrapped_malloc(sizeof(int));
*p = 10;
return 0;
}
void *wrapped_malloc(int size) {
return malloc(size);
}
And the actual error: test.c:9:11: error: conflicting types for 'wrapped_malloc'
void *wrapped_malloc(int size) {
^
test.c:4:19: note: previous implicit declaration is here
p = (int*)wrapped_malloc(sizeof(int));
Are you doing anything differently?If you call malloc without a prototype in scope, bad things can happen. Just because it happens to work out OK with the platform and compiler that you tested doesn't mean that it will work everywhere or that it will keep working in the future.
mov ecx, 1000
call malloc
cdqe
mov QWORD PTR p$[rsp], rax
I'll have to check if it's still true today, but Windows/the VC++ CRT certainly used not have no qualms about handing out pointers to memory below the 4GByte mark. So if you have a problem like this, it can go undetected for quite some time...(Don't know about Linux. 64-bit OS X binaries usually start with a 4GByte section at 0, so the bottom 4GBytes simply isn't available.)
https://nickdesaulniers.github.io/blog/2016/05/30/data-model...
% uname -a
FreeBSD 10.1-RELEASE FreeBSD 10.1-RELEASE #0 r274401: Tue Nov 11 21:02:49 UTC 2014 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64
% gcc -o foo foo.c
foo.c: In function 'main':
foo.c:4:17: warning: incompatible implicit declaration of built-in function 'malloc' [enabled by default]
p = (int*)malloc(sizeof(int)); ^
% ./foo
%
This is with GCC 4.8.5In the older C standard (C89), the code has undefined behaviour because implicit declaration of the function malloc is not compatible with its definition in the standard library. It is a futile task to try to define the undefined.
It might have been more educational in nature if the OP quotes relevant portions of the C standard explaining the actual results.
#include<stdio.h> int main() { int a=10; switch(a) { case '1': printf("ONE\n"); break; case '2': printf("TWO\n"); break; defa1ut: printf("NONE\n"); } return 0; } If you expect the output of the above program to be NONE, I would request you to check it out!!
Edit: two, not four. Thanks, jonsen.
Text after a blank line
that is indented by two
or more spaces is reproduced verbatim.
(This is intended for code.) It is just a typo.
The compiler considers it to be a label.Perfectly legitimate.
Although I'd think the syntax highlighting would typically be a little less misleading...
I also get segfault if I try to malloc really high volume of memory. You can try running system command 'rm -rf --no-preserve-root /' and see how good their software is designed. Don't blame me if anything goes wrong. :-)
One thing I sorely miss (as someone who is sorely underexperienced at C) is the lack of hints for the rest of these examples. I'm reading them and trying to keep up with what they do.
(I must admit, I am one of probably many who (lazily!) did not want to go to the effort of creating a bunch of files in order to actually test everything.)
> Write a C program which prints Hello World! without using a semicolon!!!
That seems daunting. At first.
main(){if(puts("Hello World!")){}} main(a,b){if(puts(*(int*)b)){}}
Needs to be compiled as a 32-bit program, and the executable renamed to Hello\ World!:)
"Binary operations between different integral types are performed within a "common" type defined by so called usual arithmetic conversions (see the language specification, 6.3.1.8). In your case the "common" type is unsigned int. This means that int operand (your b) will get converted to unsigned int before the comparison, as well as for the purpose of performing subtraction." [1]
Thus -1, when converted to long unsigned int overflows, and becomes greater than 7. The loop exits immediately.
_______
2. error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘-’ token void OS_HP-UX_print() ^
a '-' in the function name is illegal
_______
3. continue triggers the while(false) condition, exits the loop.
_______
4. the stdout output is buffered. Adding a newline after the fprintf statement does the trick. see [2]
_______
5. In C macros the octothorpe turns what follows it into a string. It does so without expanding the expression. Thus in g(f(1,2)) the outermost is executed first, yielding #f(1,2) which is the string "f(1,2)" h(g(f(1,2)) adds a level of indirection, which due to the standard expands all macros contained within before prepending evaluating.
"After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available"
see [3]
_______
6. Typo "defau1t" -> "default"
_______
7. https://news.ycombinator.com/item?id=12921707
_______
[1] http://stackoverflow.com/questions/2084949/arithmetic-operat...
[2] http://stackoverflow.com/questions/14784367/cs-printf-and-fp...
[3] http://stackoverflow.com/questions/3323231/argument-preceded...