Fog Creek Software
Discussion Board




Becoming a C _developer_


I've for long known the C syntax and I like it very much. I can easily understand C programs and I've myself written lots and lots of Win32 API code. However, since my mainstay has been VB, I'm not at all confident of calling myself a C programmer and I still believe I do not know C. That is true because when I look at projects on SourceForge, I do not see myself as capable as re-creating that stuff. I can't write sound editors, zip/compression apps, binary editors et al.

As a self-analysis, I believe I know the C syntax but I score rather measley on the vocabulary. So I ask you gurus: what's your advise to someone who's known _only_ the C syntax for a long period of time now, say more than 7 years, has done some Windows Programming but nothing else in C. What's your tip to this self-learner who wants to take the next step in writing apps alike those that feature on SourceForge. Where do I look next? How do I improve my C vocabulary? Should I first browse the standard library documentation? What next? I believe C in itself is such a small language with a very general function set dealing with basic data types in the standard library. Am I right in assuming that for doing a particular non-generic application like the ones mentioned above, I'd have to depend on a library and read its documentation? Do these developers developing, say, a database application that provides a C front-end connecting to a MS SQL Server database do the connection thing from scratch in C or do they rely on an external pre-packaged library?

How do I proceed improving my vocabulary? I want to quickly begin writing a useful application that I can proudly boast of.

Estudiantin
Monday, May 31, 2004

There's a lot more to writing good software than learning the syntax and vocabulary of one or two languages.

Subjects like data structures, set theory, relational algebra, statistics, regular expressions, etc. are important in whatever language you're using. You probably know the C language as well as you need to, move on to bigger things.

Tom H
Monday, May 31, 2004

Why not read some open source C code. Head over to http://www.sourceforge.net and find a small C project that you are interested in, then download the code and read through it.

Matthew Lock
Monday, May 31, 2004


I want to do more of C because I think I am fascinated by the apps on SourceForge. I love the terse, and yet flowery syntax of C and the sheer power it lends the developer. How do I proceed beside looking up apps on Source Forge and reading them? What were the things you did when you were learning C, after you had read many times the books on its syntax, programming contructs, data structures and even practiced the few trivial programs and all excercises? Where did you find the key to industrial programming in C? How did you get better at C? What were the places you got your learning of C from beside the job?

Estudiantin
Monday, May 31, 2004

I'll give you three problems and see if you can solve them using C.

1. Create a function that adds X to a given number without using the +, -, * or / operators.  The prototype will be:

int Add(int);

2. Create a function that multiplies by 7 without using the + or * operators.  The prototype will be:

int Mul7(int);

3. Create a function that divides by 7 without using the - or / operators.  Thr prototype will be:

int Div7(int);

Anon
Monday, May 31, 2004

The first prototype should be: int Add(int, int);

Anon
Monday, May 31, 2004

C does not provide anything more than a few programming constructs. Even, to read from the keyboard and write to the screen, you need to make a few functions calls to the C standard library. C won't even allocate memory for you.

The real power of C is in the structures (structs) and pointers. You want anything more than that, you need to link to a library. You want to write GUIs for Windows, you need to use the functions provided by the Windows API. You want to connect to a database, you need to find the API provided by its vendor. You want to write network-aware applications, you need to use the functions provided by the WinSock API.

For most of the applications, you have to rely on APIs (or libraries) provided by several vendors, in particular Microsoft.  Ofcourse, you can try to develop lower-level stuff on your own but in some cases you might be restricted by the limitations imposed by the modern day operating systems (which in fact, is a good thing). A lower level stuff in the modern day programming would be to implement an MP3 decoder yourself but depend on Windows to send audio output.

But to develop real world software, C can be overwhelming. You'll need to cater for bugs in all the operating systems that you'll target. Instead of trying to come up with a list of everything you'll need to look for, I would like to make it clear that whatever your programming environment abstracts from you will become your own responsibility in C.

Green Pajamas
Monday, May 31, 2004


Anon, I am thinking about the answers to your questions. Meanwhile, I would request that no one else please answers them. At least not until I sweat and give up.

Estudiantin
Monday, May 31, 2004


Hello Anon,

The answer to the first one has just dawned on me. Without using the + operator, it will be done using the identity:

(x^2 - y^2) = (x+y)(x-y), thus
(x+y) = (x^2 - y^2) / (x-y)

The Add function will be coded as:

int Add (int x, int y)
{
    if (x==y) return 0;
    return (int)(x^2 - y^2) / (x-y);
}

Estudiantin
Monday, May 31, 2004


I wrote this line,

if (x==y) return 0;

in a hurry when I suddenly forgot I was supposed to add and not subtract. I was in a hurry accounting for the "division by zero" thing.

This function would work only if x and y are unequal.

Estudiantin
Monday, May 31, 2004

Remember that you can't use +, -, * or /. :-)

Anon
Monday, May 31, 2004


Okay, Anon. I'll rethink the first one. In the mean time, for your other two questions, 7 is the key. I am thinking in the direction of bitwise shifting. I request other members not to answer the questions while I am on them, or till I give up.

Thanks.

Estudiantin
Monday, May 31, 2004


Ok, I got the second one. If I shift 3 bits to the left, I factor the number 8 times. Then I subtract the original number from the product. This gives me the number multiplied by 7.

Here,

int Multiply7 (int x)
{
    return (x<<3)-x;
}

Estudiantin
Monday, May 31, 2004

It may also be worthwhile to note that the C operator ^ is a 'bitwise XOR operator' and not a 'to the power of' operator.

Anon
Monday, May 31, 2004

Yup.  Good Job on the Multiply.

Anon
Monday, May 31, 2004


Can I use a constant for the divide function? If yes, then here it is:

const float FACTOR = 1.14285

float Div7 (int x)
{

    if (x%2)
    {

...hang on. I'll write in the IDE and paste it later. Gimme a sec.

Estudiantin
Monday, May 31, 2004


> may also be worthwhile to note that the C operator ^ is a 'bitwise XOR operator' and not a 'to the power of' operator.

Oh yes! I forgot altogether. Then I can't even make another function for power because that would also use the * operator. So this must have something to do with binary operators again. I am working, please just gimme some time, that's all.

Estudiantin
Monday, May 31, 2004

You may use a constant but stick to integers.  Remember the function prototype returns an int.

Anon
Monday, May 31, 2004


OK, here. It is still not on the mark because of the loss of data from a double to a float. I am still at it and your first question.

#include <stdio.h>

float Div7(int);

const float FACTOR = 1.1428571428571428571428571428571;

int main()
{
    printf("div7(10) = %f\n",Div7(10));
    printf("div7(14) = %f\n",Div7(14));
    return 1;
}

float Div7(int x)
{
    int i,j;
    i=j=0;
    for(j=1;j<=3;j++)
    {
        i=(x%2)?i:++i;
        x=(x>>1);
        printf("x = %d\n", x);
    }
    return (x + (float)0.5*i) * FACTOR;
}

Estudiantin
Monday, May 31, 2004


>You may use a constant but stick to integers.  Remember the function prototype returns an int.

Are you sure? Must I round off the decimal part of the results then?

Estudiantin
Monday, May 31, 2004

Yup. I'm sure.

Let's take a nice large even number that can be easily bitshifted.  Let say 2048.

Now let's take 1/7 of that number and round it to a whole number.

1/7 * 2048 = 293

So our fraction is now 293/2048.  The constant would be 293.

(293 * dividend) / 2048

which can rewritten as:

(293 * dividend) >> 11

That was a tough one.

Anon
Monday, May 31, 2004


Anon, that isn't funny. You're taking a specific example that is a perfect power of 2. What if it was not 2048 but 2047? Your function won't be generic then. Or am I missing something?

Estudiantin
Monday, May 31, 2004

It only has to divide by 7 bud.  It is, by definition, not generic.

Anon
Monday, May 31, 2004


Sorry, I got it. Thanks! That was clever indeed.

Estudiantin
Monday, May 31, 2004

These silly little puzzles really have *nothing* to do with the C language.

In real programming you aren't forbidden from using any of the operators.

Don't look on SourceForge either. Way too many rookies put their projects there.

If you want to learn C, write C programs (using all the operators, not some bizarre subset).

Jorel on Software
Monday, May 31, 2004

>> "I want to do more of C because I think I am fascinated by the apps on SourceForge. I love the terse, and yet flowery syntax of C and the sheer power it lends the developer. How do I proceed beside looking up apps on Source Forge and reading them?" <<

Just giving him what he asked for.

Anon
Monday, May 31, 2004


And the Add()? I hope the bitwise OR is the same as the logical OR and you won't tell me it was to be used in the Add(), because ORing is not binary addition no? OR (|) doesn't carry over the bit values, no?

Estudiantin
Monday, May 31, 2004

How about I just solve it for ya guy.  (So we don't get into petty disputes.)

/* add y to x */
int Add(int x, int y)
{
    int i;

                i = y;

    while(x & i)
    {
        x &= ~i;
        i <<= 1;
    }
    
    return x | i;
}

These tricks are not necessary to know or to learn the C language but they are common tricks that most C coders would learn over time.  I regularly encounter the XOR swap trick for example.

At any rate good luck with your endeavor to become a C developer.

Anon
Monday, May 31, 2004

I believe the int's in the function above should 'unsigned' perhaps.

Anon
Monday, May 31, 2004


Disputes? Nay, I am enjoying it. This code was sexy but I don't quite understand:

x &= ~i;

Please explain. And more pointers to becoming a good C developer, to explore some good libraries. Any place to find some nice libraries with documentation?

Estudiantin
Monday, May 31, 2004

You could read this free online book: C++ In Action: Industrial Strength Programming Techniques http://www.relisoft.com/book/index.htm

However it's about C++.

Somorone
Monday, May 31, 2004


Thanks for the link to the free online book.

Estudiantin
Monday, May 31, 2004

Really, unless you need to write low-level code, or particularly fast code, or you're interested in how things are implemented closer to the machine level, C isn't worth the bother.

But, the 'vocubulary' you need would be the C standard libraries. Read up all about them, and about the C APIs for any platforms/applications/technologies/GUI frameworks/databases you might need to work with. C has no builtin functions whatsoever (as you probably know), so the libraries are extremely important. You need to know them inside out.

Good things to help you learn what C is all about: read up about data structures and implement them in C. I'm not just talking about learning to use the 'struct' syntax... I mean say implementing a binary tree index or a hash-table array or a linked list or whatever. Because you'll need to do things like this when writing C programs, and they're things you should know about anyway from a general perspective. Most programming these days uses high-level language features or relational databases to do all the complex data structures stuff, but C doesn't hold your hand through it. You might find some C libraries out there that do, but what the standard libaries offer is pretty fucking minimal.

Glad I left C behind some time ago. Heh.

Matt
Monday, May 31, 2004

Anon and Estudiantin, that's not good programming at all.


Monday, May 31, 2004

What the hell, let the petty argument begin!

What's not good programming?  Who's talking about good programming?  I'm not.  The tricks I've presented do have their place and have been used in video games, compilers etc etc. 

It's more about testing your knowledge than practicality.  It's about learning and having fun.

Good and bad code/programming has been beaten to death on this board and many others.  You can only label code good or bad in the presence of a set of rules or standards that will be adhered to by the person or group of people writing the code.  There are no such rules to compare to here.

Anon
Monday, May 31, 2004

If you like free online books check out these:

http://oopweb.com/ (Many good books)

http://mindview.net/Books (Java + C++)

http://www.icce.rug.nl/documents/cplusplus/ (c++ book)

http://www.roadmap.ubc.ca/quick_reference/ (Quick references for many uses)

http://www.books-on-line.com/bol/DeweyResults.cfm?DeweyP=005 (Older books and topics)

http://onlinebooks.library.upenn.edu/lists.html (Old books and many dead links e.g. needs some hunting to find a book for example I just found the 1997 version of: Compilers and Compiler Generators - an
introduction with C++)

Somorone
Monday, May 31, 2004

Better solution to adding without +/- operators:

int Add( int x, int y)
{
    char* a = (char*) x;
    char* b = (char*) y;

    return (int)(&a[b]);
}

(not tested, not too portable, but you get the idea)

Roose
Monday, May 31, 2004

Being knowledgable in 'C' is NOT being able to add without using the '+' operator.  Sheesh.  Don't you have bigger problems to worry about?

My recommendations:  Get Plum's "Reliable Data Structures in 'C'".  It's focus is more on 'Reliable' than 'Data structures', but that makes it more useful.

It will tell you the reasons why the 'C' standard libary can be so terse and difficult, and give you ways to defuse many of the hidden problems you can run into.

The big problem, opportunity, and feature of 'C' is that the language itself has so little (no inherent I/O, for one thing).  This means external libraries are used for EVERYTHING important.  The Standard Library finally has become "Standard", and is supported for multiple platforms.

Then, everybody and his brother is making new API interfaces for 'C' (Unix, Windows API, VxWorks, etc.)  So merely knowing the language is insufficient -- knowing how to apply the API's for your application domain is critical also.

Start with Plum, then get the 'key books' of your environment, and good luck.

AllanL5
Monday, May 31, 2004

>> "Being knowledgable in 'C' is NOT being able to add without using the '+' operator"

I don't believe anyone in this thread has even implied such a thing.

Shooting down Visions of Granduer since 1901
Monday, May 31, 2004

Nope, but it's still pretty cool, in a pointless sort of way.

Matt
Monday, May 31, 2004

I'll also say that knowing how to do multiplications without using the * operator does not make you a good programmer. Writing well-designed, error-free, tested, efficient, maintainable code makes you a good programmer.

If you want a fun C programming exercise, try this:

Write a function in C that returns a function that adds x to its argument.

Quickie
Monday, May 31, 2004

"in a pointless sort of way"

Very pointless, since it's both common and simple:

http://www.mhuffman.com/notes/hardware/addition.htm
http://www.ee.byu.edu/ee/class/ee320/labs/lab5.html

Tom H
Monday, May 31, 2004

Woo tricky one in C... the prototype alone is pretty screwy:

int (*AdderFactory(int))(int x) {

  ...

}

Because of C being a statically compiled language, and unable to create a function 'on the fly', this one seems particularly hard without resorting to ugly platform-dependent tricks of copying/altering code in memory... care to give any hints? ;)

Matt
Monday, May 31, 2004

Pointless, if you're handed the more convenient operator on a plate but arbitrarily decide to use something more obscure (and probably slower).

Useful if you're designing fricking logic chips, yes.

Cool either way.

Matt
Monday, May 31, 2004

I don't think anyone except students and masochists would do a hardware adder from raw gates, in hardware, unless it's a really, really odd situation. Normally, if you'd need to do this sort of thing, you'd be using an FPGA, and you'd just write some higher-level VHDL and let the synthesiser work it out, probably using the library primitives in the FPGA library

hardware guy
Monday, May 31, 2004

>> "I'll also say that knowing how to do multiplications without using the * operator does not make you a good programmer." <<

I don't believe anyone in this thread has even implied such a thing.

Sorry, couldn't resist. ;-)

Shooting down Visions of Granduer since 1901
Monday, May 31, 2004

"Write a function in C that returns a function that adds x to its argument"

For fun lets compare the statically typed solution (C) to a dynamically typed language (Python). Here's the complete program:

def a():
    return adder

def adder(a):
    return a+1

f = a()

print f(2) # prints 3
print f(1,2) #prints "TypeError: adder() takes exactly 1 arguement (2 given)"

Tom H
Monday, May 31, 2004

Why is it that these threads almost always turn into a pedantic, bragging discussion about stupid stuff, even though the orginal thread had good intentions?  This is crazy.  Crazy I say!

Anon
Monday, May 31, 2004

Haha. Beats all the 'my boss sucks and my job sucks' threads.

Come on then how do you return the adder function in C? I'm curious now.

Matt
Monday, May 31, 2004

Oh, just realised, I was presuming you meant something like:

def AdderFactory(x):
    def Adder(y):
        return x+y
    return Adder

rather than just

def AdderFactory():
    def Adder(y):
        return x+1
    return Adder

The latter easy to do in C...

Meep
Monday, May 31, 2004

(set make-adder (lambda (n) (lambda (x) (+ x n))))
((make-adder 1) 1) ; 2


Monday, May 31, 2004

Since I've just sold my adding-software company, I now have $11.5 million in the bank.

Should I retire?

Jimmy Jo-jo
Monday, May 31, 2004

You mean "in the bank."

Yes, you should retire. ;-)

Anon
Monday, May 31, 2004

Anon, your code does not work.

Here's some code that does actually work...

#include <stdio.h>

#define A0 !lsb_x
#define A1  lsb_x
#define B0 !lsb_y
#define B1  lsb_y
#define C0 !carry
#define C1  carry

/*
X+Y=Z
-----
0 0 0 = 0
0 0 1 = 1
0 1 0 = 1
0 1 1 = 0, C
1 0 0 = 1
1 0 1 = 0, C
1 1 0 = 0, C
1 1 1 = 1, C
*/

int add(int x, int y)
{
        unsigned int ux = x;
        unsigned int uy = y;
        unsigned int uz = 0;

        unsigned int lsb_x = ux & 1;
        unsigned int lsb_y = uy & 1;
        unsigned int carry = 0;
        unsigned int parity = 0;

        unsigned int count = 1;
        while (ux | uy | carry) {
                lsb_x = ux & 1;
                lsb_y = uy & 1;

                parity = (lsb_x ^ lsb_y ^ carry);
                carry = ((A0 && B1 && C1) ||
                        (A1 && B0 && C1) ||
                        (A1 && B1 && C0) ||
                        (A1 && B1 && C1));

                uz |= ((parity ? ~(0u) : 0u) & count);

                ux >>= 1;
                uy >>= 1;

                count <<= 1;
        }

        return (int)uz;
}

int add2(int x, int y)
{
        int i;
        i = y;

        while(x & i) {
                x &= ~i;
                i <<= 1;
        }
   
        return x | i;
}

main()
{
        int x = 222;
        int y = 37;
        printf("%d + %d = %d\n", x, y, add(x,y));
        printf("%d + %d = %d\n", x, y, add2(x,y));
}

emacsdude
Tuesday, June 01, 2004

Wow, nice but somehow terrible.  See my solution which takes advantage of the fact that a[b] = *(a + b).  I think that is the entire point of the interview question.

Roose
Tuesday, June 01, 2004

Roose, very nice solution.  Quite clever!

I design microprocessors for a living, so I just did what was natural to me (carry-ripple-adder).  It's hideous, yes!  It should scare people away from hardware design.

emacsdude
Tuesday, June 01, 2004

better learn to write clean, simple and self-documenting code, not like in those "dont use + operator" examples

IJ
Tuesday, June 01, 2004

It doesn't work as a general solution because it was originally written to only add 1 to a given number.  So it does work for all x if y == 1, but other than that it is useless.  So I guess it should be named Add1... :-)

Glad to see someone was paying attention.  Nice solution Emacs guy.

Anon
Tuesday, June 01, 2004


I am overwhelmed in awwwwwwe. I eat my own words in the original post about knowing C syntax. Reading these posts makes me think I still don't know anything about C. Or better yet, I don't know anything about anything at all. I am just a toddler trying to wend his way through the jungle of wild code-beasts and lethal pointers.

I see I don't quite understand some code here. Especially the one written by Roose and EmacsDude. I guess EmacsDude's code was a manual drudge over the binary addition. If I am right, I got the overview but it's mind-numbing for my unaccustumed brain.

In the code Roose gave, I didn't get what he meant when he was casting the integer arguments into char pointers. And  neither the value the Add function returned. What is &a[b], Roose?

Anon, is the x&=~i the same as x+=~1?

Much thanks, people. I am humbled by your responses. I got a lot to learn. Back to learning...

Hey! Thanks for the links, folks! They're awesome. I won't know where to start and oh! there's so much to read. I spend all the time bookmarking the links and never get to read them.

Estudiantin
Tuesday, June 01, 2004

Don't worry about not understanding my solution.  It's a brute force solution that does the addition one bit at a time using an algorithm that you probably learned in grade school for adding -- working in columns, adding each one and taking the carry to input to the next column.

It's very inefficient in that it's complexity is O(n) in the number of bits to add.

There exist O(log n) algorithms for addition.

If you don't know what O() notation is, I would recommend learning about that first.  Pick up a good book on algorithms and data structures - I always have my "Introduction to Algorithms" by Cormen et al. nearby.  You should know what stacks, lists and queues are before you start programming :)

There's a difference between understanding a language and understanding a domain.  For example, if I asked you to write a program to solve the Navier-Stokes equation, you would have no idea how to do it without understanding the equation first.  The implementation is just a series of loops with floating-point multiplies and additions over arrays.  Solving the Navier Stokes equation requires domain knowledge in fluid dynamics and vector differential equations.  Solving the "add" problem requires domain knowledge in computer arithmetic.

As for the C language itself, I recommend "C Programming Language" by Kernighan and Ritchie to start and then "Expert C Programming" by Peter van der Linden to round out your knowledge.

Roose's solution is nice and elegant and fits the problem description.  If the problem were restated to say that the compiled assembly code could not contain any "add/sub" instructions then you would need my solution or something more efficient that worked from first principles.

To understand Roose's solution you need to understand pointer arithmetic.  The two books on C that recommended should clear that up for you.

Once you've mastered algorithms and then the C programming language - figure out what it is you want to do with your knowledge and use C and algorithms as tools to accomplish your goals. 

One final comment about C.  Only use C if you want or need to be "close" to the hardware.  I see C-code as a shorthand for assembly.  If you really need to know about where your memory is and how it is allocated, or you need some direct access to bare hardware, then C is for you.  If you are trying to solve some problems where this is not a concern, consider some higher level (safer) languages like java or c# or ruby.

emacsdude
Tuesday, June 01, 2004


I do understand pointers. I thought I did. I guess I have to revisit them again for an umpteenth helping. I have used stacks, queues, linked lists, collections and some compression algorithms (RLE and Huffman) but no further. I have not studied algorithms and data structures from a book, but mostly by surfing the Net and practicing and implementing them in my projects.

I do have K&R, but it is succinct text pregnent with meaning, so I ponder over a few pages over a long period of time. Hence, I have not been able to move further with it after 4 chapters. Then I have my job too that is very demanding so I cannot devote much time outside of the routine development work.

Thanks for such valuable tips.

Estudiantin
Tuesday, June 01, 2004

I'd like to see the solution to the "return a function that will add x to its argument" problem, if anyone is still reading this thread.

Michael Eisenberg
Tuesday, June 01, 2004

Michael, C doesn't support lexical scope, so any solution is going to be a bit hacky.

In C it might look like this (lots of problems with this solution, btw):

int n = 0;
int adder(int x) { return n + x; }
int *padder(int) make_adder(int m) { n = m; return adder; }

(make_adder(5))(1); // = 6
adder(2); // 7 (oops)

In C++ you can make function objects, so you can emulate lexical scope:

class adder
{
public:
  adder(int m) : n(m) { }
  int operator()(int x) { return n + x; }
private:
  int n;
};

adder make_adder(int m) { return adder(m); }

(make_adder(5))(1); // 6
(make_adder(6))(1); // 7

In Lisp you get lexical scope, so it looks like this:

(defun make-adder (n) (lambda (x) (+ x n)))

((make-adder 5) 1) ; = 6
((make-adder 6) 1) ; = 7


Tuesday, June 01, 2004

As you mentioned, the C-solution you gave doesn't work because it sets a global variable.  What if you want to have 2 function pointers and use them in any order?  You can only use one at a time.

In order to solve the problem with C you'd need to dynamically compile and link the function. 
Not pretty...

Because the add function is so simple you could probably bang out some assembly code rather quickly, but then you lose portability.  Dynamic code generation is best left to languages that support it.

emacsdude
Tuesday, June 01, 2004

>> If the problem were restated to say that the compiled assembly code could not contain any "add/sub" instructions

BTW, that code probably compiles to a LEA ;)

Alex
Wednesday, June 02, 2004

Actually, slight correction, one of the arguments should be cast to an char* pointer but not the other:

int Add( int x, int y)
{
    char* a = (char*) x;

    return (int)(&a[y]);
}

If you understand what pointers do and especially pointer arithmetic, this should be clear.

See if you can explain why these statements are true:

a[b] = *(a + b)
&a[b] = a + b

Where a is a POINTER, and b is an INTEGER.

e.g. a + b is pointer arithmetic, not integer arithmetic.

Note in my original post a[b] where b is a pointer does not make any sense, it will be a compile error.

And if you want to get even trickier, this is true:

a[b] equals b[a]

huh?  a is a pointer and b is an integer, how can you subscript an integer with a pointer (rather than the other way around, subscripting a pointer with an integer).  That is a consequence of the fact I posted above that a[b] = *(a + b).

Think of a pointer as simply an integer, an index, and in general:

p[i] as an integer = (address of p as an integer) + sizeof(*p) * i

i.e. if p is a char*, then sizeof(*p) == 1.  If p is an int*, then sizeof(*p) == 4 typically on 32 bit systems, it is implementation defined however.  if p is a pointer to a struct, then sizeof(*p) == the size of the struct.

As a concrete example:

char someCharArray[10];
char* pChar = someCharArray;

int someIntArray[10];
int* pInt;

Then:

pChar[5] as an numeric address is simply the numeric value of pChar, e.g. (int)pChar plus five.  Say pChar holds the address 0x00008000, then pChar+5 == &pChar[5] holds the numeric address 0x00008005.

On the other hand:

If pInt as a numeric address is 0x00009000, then pInt + 5 == &pInt[5] holds the numeric value 0x00009000 + (5 * sizeof(int)) which is typically 5*4 == 20 on many current machines, so you get 0x00009020.

That is why you can add with the [] operator.  That is all it is doing.  It just produces an address from the base of the array which gives the location of a particular element you specify.

Roose
Wednesday, June 02, 2004

Here's a totally gross, non-portable, x86-only solution to the "write a function that returns a pointer to a function that adds 'x' to an integer" problem:

#include <stdio.h>
#include <malloc.h>

typedef int (__cdecl *addr_t)(int x);

addr_t make_adder(int y)
{
    char *p;

    p = malloc(10);

    if (p != NULL) {
        p[0] = 0x8b;    // move eax, dword ptr [esp-4]
        p[1] = 0x44;
        p[2] = 0x24;
        p[3] = 0x04;

        p[4] = 0x05;    // add eax, n
        *(int *)(p + 5) = y;

        p[9] = 0xc3;    // ret
    }

    return (addr_t)p;
}

int __cdecl main(int argc, char *argv[])
{
    addr_t a1, a2;

    a1 = make_adder(5);
    a2 = make_adder(1234);

    printf("a1(4)  = %d\n", a1(4));
    printf("a2(100) = %d\n", a2(100));

    free(a1);
    free(a2);

    return 0;

}  // main

(Hiding from the inevitable flames...)

Keith Moore
Wednesday, June 02, 2004

Who said code generation in C is impossible? :)

Alex
Wednesday, June 02, 2004

Alex,

The LEA is more expensive in hardware (gates) than an ADD, so the compiler would probably generate an ADD.  In fact, gcc does that for me:

0x00401120 <_Z4add3ii+0>:    push  %ebp
0x00401121 <_Z4add3ii+1>:    mov    %esp,%ebp
0x00401123 <_Z4add3ii+3>:    mov    0xc(%ebp),%eax
0x00401126 <_Z4add3ii+6>:    mov    0x8(%ebp),%ecx
0x00401129 <_Z4add3ii+9>:    pop    %ebp
0x0040112a <_Z4add3ii+10>:    add    %ecx,%eax
0x0040112c <_Z4add3ii+12>:    ret   

emacsdude
Thursday, June 03, 2004

*  Recent Topics

*  Fog Creek Home