Welcome, Guest. Please login or register.

Author Topic: Mr. Biehl learns C  (Read 7262 times)

Description:

0 Members and 1 Guest are viewing this topic.

Offline Cymric

  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 1031
    • Show all replies
Re: Mr. Biehl learns C
« on: April 06, 2005, 09:46:22 AM »
You were correct in your examples, so your understanding what the & and * operators are for is good.

To be honest, one does not use the &-operator often for the reason you already figured out for yourself: there is little information in the address itself. However, if you look at them a little differently, namely what these addresses point at, they become very useful. For nifty programs as well as shooting yourself repeatedly in the foot with a double-barrelled shotgun. I ain't kiddin' ya.

To give an example where pointers are invaluable, consider allocating memory. There are various ways of doing that on the Amiga: you can call on the system function AllocMem(), but if your fancy runs to programs which should run on other systems too, you would use malloc(). In any case, these functions do some internal housekeeping, ask the OS whether you can have what you ask for, and some other things we needn't go into right now. What interests us at the moment is what they return: a pointer! That pointer is (of course) an address, but it's pointing at an area of memory which is now yours to abuse. You access that memory via the pointer. Here's an example:
Code: [Select]
   void *MyMem;
    MyMem = AllocMem(1000, MEMF_PUBLIC);

MyMem now 'points' to an area in memory where we have 1000 bytes at our disposal. To access that memory, we need to apply a second technique, because we've defined MyMem to point at 'void'---in other words, an unknown type. (Make it a habit to try and avoid void where you can.) Suppose we want 1000 integers:
Code: [Select]
   int *MyMem, j;
    MyMem = (int *)AllocMem(1000*sizeof(int), MEMF_PUBLIC);
    for (i=0; j<1000; j++)
        MyMem[j]=j;

First we declare MyMem to be a pointer to int, and then we allocate the memory. The first (int *) is called a cast, an instruction to the compiler to change the type of the result of the proceeding expression to what you specify. AllocMem() returns a void *, and that is not equal to an int *. Note that 'equal' means 'equal in size as well as type': while there is no size difference between int * and void *, there is a type difference. (You could cast a char into an int by doing (int), but that would likely crash the machine as an int is 32 bits, while a char only 8. You'd be accessing a region of 8 bits as if it contained 32, and that is likely going to crash the program.) Then we need to take into account that an int takes not one byte, but four, but we don't specify the four: we specify its size transparently by writing sizeof(int).

And after all that, MyMem is now indistinguishable from an array. You can access it as if you had written:
Code: [Select]
   int MyMem[1000], j;
    for (i=0; j<1000; j++)
        MyMem[j]=j;

The assignment is in fact compiled to exactly the same code as in the pointer-example.

There are more uses to pointers: they provide a fast means of handing over large amounts of data without unnecessary copying. Suppose that you are working on an image processing program and need to pass on the information of a large graphic from one function to another. You can ask the system to copy that information over every time, but that is a huge waste of resources. It is much easier and faster to have one function tell the other: look, here's a pointer to where you can find the information you need, happy processing. You will see that happen a lot of times once you begin with Amiga system programming. In fact, the Amiga would not exist if it weren't for this property.

It is also the reason why pointers are such great ways of shooting yourself in the foot. There are no checks on their validity: you cannot tell whether a pointer is actually pointing at the information it is supposed to be pointing. That is why you should always strive to use casts: it helps the compiler help you because it is much better at spotting type inconsistencies. That is still no guarantee things are bug-free, but at least the most glaring errors will be caught. (The disadvantage is that if things do go wrong, debugging is not fun at all.)

I hope this has improved your understanding why you would want to use pointers. But don't assume that you can't do without them: many modern languages don't have pointers any longer and rely on other means of passing information along.
Some people say that cats are sneaky, evil and cruel. True, and they have many other fine qualities as well.
 

Offline Cymric

  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 1031
    • Show all replies
Re: Mr. Biehl learns C
« Reply #1 on: April 07, 2005, 08:27:59 AM »
Quote
MskoDestny wrote:
Another use is to allow a function to modify values from the calling function.  This can be useful when you need to return more than one value or you need to return a large structure or an array.

Also keep in mind that arrays are just pointers with some statically allocated memory set aside for them ahead of time.  Some examples:

One word of caution: do not try and modify such strings (i.e., make them lower case, turn it into l33t) without making a copy of them first if you want your programs to be portable. Many OSes allocate such strings in read-only memory and dump core with a segmentation fault if you try and modify them. Amigas do not know that concept, so will happily allow you to modify them.
Some people say that cats are sneaky, evil and cruel. True, and they have many other fine qualities as well.
 

Offline Cymric

  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 1031
    • Show all replies
Re: Mr. Biehl learns C
« Reply #2 on: April 19, 2005, 11:15:35 AM »
Quote
ottomobiehl wrote:
Just a quick question though, I've noticed that alot of you have mentioned C++ in your replies.  I had made the decision to learn C first before diving into C++ but have got to thinking that maybe I should be trying to learn that instead or in tandem.  Is this wise or should I stick it out with C and move to C++ a little later?

There is no genuine good answer to that: the languages were designed with different things in mind, and you might not like the style of coding either forces upon you. You can write exceedingly good, nearly C++-style programs with C, but you can also break all the rules and create a C-like mess in C++.

However, since you are still struggling with pointers and other elementary issues, my advice would be: don't do C++, but at the same time: do ANSI-C (or C99). The reason for this advice is that you will have your hands full learning to translate human ideas into program code and figuring out what C does well and what it is bad at; you do not need the added complexity of object oriented programming, C++'s very strict typechecking rules, templates, and sometimes very arcane class and inheritance definitions at this point. Admittedly, if you have a good C++ library, you can dive in straight away and just go from there. But good C++-libraries are hard to find. What I usually see is that people program in C++ with a C mindset (known as 'C with syntactic candy'), and that is not the way to be doing things.

Most self tutorials do a great job of teaching you the syntaxis of C++, but absolutely stink at teaching you how to program object orientedly. C's procedural approach is much simpler, but will still give you very decent results, especially for programs of small to moderate size. Only when you feel that approach is not giving you what you want, or when the last pointer problem left you with little hair on your head, or when the program is growing out of your control, should you consider migrating to C++. The disadvantage is that it will feel like learning to program all over again (you're in effect learning to speak a new language, even though C++ and C are very much alike), but at the same time it will give you a much better insight in when you need C++'s native features and when not.

But this is, of course, just my opinion.
Some people say that cats are sneaky, evil and cruel. True, and they have many other fine qualities as well.
 

Offline Cymric

  • Hero Member
  • *****
  • Join Date: Nov 2002
  • Posts: 1031
    • Show all replies
Re: Mr. Biehl learns C
« Reply #3 on: April 19, 2005, 11:22:41 AM »
@koaftder:

Actually, that code can still be made more efficient. Your buffer is growing in an 'upward' direction, necessitating a comparison against a non-zero value all the time. It is easier and faster to have it grow 'downward' so you can compare against 0 all the time. Most CPUs give you a zero-comparison 'for free' (the MC680x0 set the Z-flag automatically, and I'll eat my shoe if that other brand does not), so that means there's a few mnemonics less in the final code. If the code is heavily used, that will add up in the long run.

Perhaps an optimising compiler can rewrite the code so the loop is traversed in the other direction, but that requires quite a clever compiler.
Some people say that cats are sneaky, evil and cruel. True, and they have many other fine qualities as well.