Interesting C Interview Questions and Answers
thegeekstuff.comQuestion 10 is a bad question. The host environment is under no obligation to provide you with pointers to writable memory in argv. Furthermore, this is veering off from C knowledge into C trivia.
Question 12 is even worse, as the processing order is implementation defined. On my compiler, it prints out 60..40..60.
C questions should be designed to show whether or not the candidate can write robust, professional quality code, not to test esoteric knowledge that even the interviewer can't get right.
I was asked questions like these in an interview once. It was such a massive red flag to me that I went with another company (the final straw was arguing over the size of int, which he insisted was always 32 bits).
I think alot of these examples use esoteric or just plain "bad" C code just in order to create pits for programmers to fall into. In real life you would avoid writing code like this precisely because of these problems. The author even mentions for at least one of the examples that he had to disable or ignore compiler errors/warnings to run the code.
Sure, it can be important to know what these problems are and how to identify them if you should have to read through some terrible code written by someone else. Still, I think the author goes a bit far; it should be more important that you hire a programmer who doesn't produce this sort of code in the first place.
In the end I think there is no "best way" to technically test programmers; you're better off covering as many bases as possible with a bit of programming, a bit of documentation/explanation, some debugging, etc.
Further to point one, it's under no obligation to provide you with writable memory of the size you'd like.
WTF does 7 come from, the source or the destination?strncpy(argv[0], "NewName", 7);Question 10 is just crazy broken. I'm not a standards expert, but I don't think argv[0] is guaranteed to be big enough to hold the 7 characters (plus \0) of NewName (i.e., even if it is writable, it may not be big enough).
As the comments said, argv[0] does not have to point to writable memory, because argc can be 0. In this case argv[0] is supposed to be NULL, which would segfault the program.
If you had to solve this question (it would be better not to ask it, because you don't learn much), I think the solution:
would be better. It's at least shorter.argv[0] = "NewName";It also wouldn't work. This just reassigns your local copy of argv[0] to a different memory location. Also, in what circumstances can argc be 0?
zero will have argc == 0.int main() { char *args[] = { 0 }; execve("./zero", args, 0L); }On my MBP with gcc 4.2.1, it does work -- I tried it before posting. (Your interpretation of what it does is correct.)
I think the best solution would be something like "Hope setproctitle() is available and use it".
Wow, question 12 is so bad that they completely ignore the C standard and make up rules (arguments evaluated left-to-right). There's no sequence point between function arguments.
Not to defend question 10 per se (I think it's dumb too) I think your criticism is wrong. The ISO C standard declares argv as having type "char*", which clearly requires writability. I don't believe this is covered by an exception like the one for string literals.
Certainly on all real-world platforms with a conforming main() implementation (i.e. there are some embedded systems which have stubbed mains, or just use something like _start directly) argv is passed on the stack and is writable.
The char* formal parameter type is there for historical reasons. The implementation is under no obligation to ensure writability.
Also, while argv itself may be passed on the stack, the memory each entry of argv points to is not.
But once again, we're arguing esoteric edge cases that have no place in an interview, let alone in professional code.
Again, I think that's wrong. Can you cite anything in the standard that allows a non-writable argv? Again, the only comparable situation I can think of is the allowed implicit conversion of a string literal to char* even though the memory is allowed to be read-only. That doesn't apply here.
And the bit about argv not being passed on the stack is simply wrong on all real platforms. The argv[] array of pointers and the memory they point to is absolutely on the stack at process start.
Hmm actually you are right on the writable part:
5.1.2.2.1 Program Startup: "The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination."
However, nothing says they must be on the stack. I ran the following program to test this:
I passed it the params "1 2 3" and got the following:int main(int argc, const char * argv[]) { char buff1[0x1000]; char buff2[0x1000]; printf("buff1 = %p\n", buff1); printf("buff2 = %p\n", buff2); printf("argv = %p\n", argv); for(int i = 0; i < argc; i++) { printf("arg %d = %p\n", i, argv[i]); } return 0; }
It seems odd that there's a 376 byte gap between what's clearly the stack and what addresses the arguments reside at. They also appear in ascending order despite the fact that the stack is descending (a required convention, maybe?). It's still pretty damn close to the stack so it might very well be the stack with a bunch of process-specific stuff in it, or it could just be that the OS put the start of the stack close to the startup parameters.buff1 = 0x7fff5fbfe7b0 buff2 = 0x7fff5fbfd7b0 argv = 0x7fff5fbff7f8 arg 0 = 0x7fff5fbff970 arg 1 = 0x7fff5fbff9e9 arg 2 = 0x7fff5fbff9eb arg 3 = 0x7fff5fbff9edEither way, this is very esoteric stuff that has no place in an interview. I've read through the complete spec a number of times and I STILL get stuff wrong!
The location of process arguments on the stack (and environment variables) is just a convention between your kernel and system libc's crt0.a / the dynamic loader. (Not part of the C standard.)
Your program doesn't actually start executing at main(). It runs a bunch of code before it gets there, all of which is farther up the stack.
2, 3, 4, 7 are all bad questions too. It's pretty bad as collections of C questions go.
Well, Though the above code is not freeing up the memory allocated to ‘ptr’ but still this would not cause a memory leak as after the processing is done the program exits.
Well this is always true, so I guess that means there's no such thing as memory leaks in C. Checkmate garbage collectors.
The code in question 4 is not a memory leak by any reasonable definition of memory leak. Allocating a fixed amount of memory and using it until the program terminates, without ever bothering to free it, is a perfectly valid programming practice. Why should it trouble anyone more than say a statically allocated global array? In fact, it's a wiser thing to do than religiously freeing everything at exit.
An actual memory leak is when the amount of memory program allocates grows with runtime while the amount of data the program is processing remains the same.
Generally modern operating systems free any memory left that the program is not using, but is still accessible. However, if you overwrite an old pointer, there is no way that the operating system could catch that.
To illustrate the difference, I wrote two quick programs.
This one has memory that the OS can recover:
This one has memory that the OS cannot recover:#include <stdlib.h> int main(void) { char *a = malloc(10); return 0; }#include <stdlib.h> int main(void) { char *a = malloc(10); a = malloc(10); return 0; }The OS always recovers all of the memory from a process that has terminated (barring horrible kernel bugs), and it doesn't need to look through the process image for pointers that need to be freed.
When a process allocates memory, the kernel doesn't give it a physical memory address. It gives it a virtual address, which, when accessed, will be mapped to some physical address. The kernel has to keep track of which virtual addresses map to which physical addresses for each process, and it stores these mappings in a data structure called a "page table". At all times, the kernel knows what memory segments are in use by a given process.
So, when a process exits, the kernel can just look at its page table to see what memory had been allocated to it. Provided that no other processes shared the same memory, it can free it.
So if you run your second example a billion times or so, your OS will run out of RAM?
No, that's not how it works....
To get rid of the hard memory leak in the second example, just call free(a) twice before returning. They don't call it pointer magic for nothing!
/sarcasm
Don't give me hints in the title of the question! That kind of wrecked the challenge.
I've studied most of these as part of my introductory C course in university. Are they really that interesting?
None of these are especially interesting C questions... Most rely on assumptions about the x86 architecture and GCC (stack grows downwards, ordering of stack variables in memory (not registers), ...).
#3 notes a warning for main not returning int, but doesn't point out that without stdlib.h, malloc() and free() will be declared implicitly with incorrect types. (Edit: this produces a warning on 4.7 GCC and Clang 3.0.)
Almost all of these questions have something wrong with them other than what the author is pointing out. I question the author's familiarity with C.
Also the casts to (char *) from malloc suggest that the author is a C++ programmer.
There's a discussion in "The Practice of Programming" of which is a better style when programming in straight C. The argument for casting was that could make porting to C++ easier. The argument against it went like that (hope I'm not botching it): if the appropriate header file was not included and consequently malloc() was implicitly assumed to return an int, then without a cast assigning the return value of malloc to say char* would likely trigger a warning, but the cast will silence the warning. This could be a real bug if the convention for returning an int was different from returning a pointer (say if one was returned via a register and the other wasn't.)
Or if you were programming on an 8086 in large memory model where int is 16 bit and a pointer is 32 bit (16 bit segment, 16 bit offset)
This makes me wonder if anyone ever did a compiler where malloc returned a segment + offset (I think that gives a 48 bit pointer) for a 386 class machine.
Considering that tons and tons of people apply for programming jobs who don't know how to program at all, they're at least borderline useful. Additionally there are not many classes with C at all these days.
Additionally there are not many classes with C at all these days.
Depends on what you are studying. While I never had any specific "learn C" courses it is by far the most used language in other courses (OS programming, parallel programming, compiler construction, network programming, micro-controllers, graphics and game programming, security courses etc.). No other language comes close (a lot of the time a C++ compiler is used but the C subset is what ultimately has been used).
I took a total of 2 C classes as part of my CS degree and never learned any of these...
If nothing else, you should be aware of the issues that using fixed size buffers with no bounds checking causes. So at least know enough to know not to use 'gets' and 'strcpy'.
I wonder why there are so many articles about C getting passed around where the author just doesn't know the language that well, or at the very least ventures well outside the boundaries of his knowledge. Where are the good articles on C, and why don't they ever show up on HN?
The question is why it's on 12th place on the index. Don't people read articles before they press the little arrow?
For those of you interested, the first stage in the SpaceX interview process, at least for the flight software group, is a series of questions like these, with A-G multiple choice answers. You had to identify the class of bug.
The G choice was always "No, looks good".
It's timed.
Here is another interesting C/Objective-C Q&A: http://www.eosgarden.com/en/articles/objc-quizz/
Is there a reason for any modern compiler to support linking to functions like gets() without setting an "--use-unsafe-libraries" flag?