Amiga.org
Operating System Specific Discussions => Amiga OS => Amiga OS -- Development => Topic started by: ottomobiehl on April 06, 2005, 06:11:48 AM
-
Ok. I've taken it upon myself to learn C as part of a self improvement program I've embarked upon and also to help clean up the environment. As is normal with endeavors such as these I will have questions. Please bear with me and my newbie status.
Pointers are my first hurdle. I think I understand what they are and what they do but not really what to use them for.
To declare a pointer I do this:
int *i;
right?
so if I do:
int x = 5;
int *i;
i = &x;
then i will equal the memory address of x but not the value of x, right?
If I'm good so far, why would I really need the memory address of x? What would be a real world example of the usage for this? :-?
edit: I sure wish that we had a general developer section in the forums for this type of thing.
-
Correct pointer i points to x. Just like you said.
I think pointers are among the most usefull things in c.
You can allocate pieces of memory and use a pointer to access the memory. You can use pointers to manipulate arrays, structures all kinds of things. You can also use function pointers.
Play around with it. Look at various examples on the web: www.codeguru.com www.codeproject.com. Warning: those sites are very win32.
Good luck
-
Warning: those sites are very win32.
You aint kidding. :-)
I've looked at several tutorials on the web and most use simple "hello world" type examples, which don't really tell me where's the appropriate place to use them. I am assuming that one use for them would be to reduce the number of global variables by being able to reference a local variable in a function but somehow I believe there are other ways to do this.
For example:
int main()
{
int j;
int k;
int l;
int *pt1; /* Declares an integer pointer */
int *pt2; /* Declares an integer pointer */
float values[100];
float results[100];
float *pt3; /* Declares a float pointer */
float *pt4; /* Declares a float pointer */
j = 1;
k = 2;
pt1 = &j; /* pt1 contains the address of the variable j */
pt2 = &k; /* pt2 contains the address of variable k */
pt3 = values;
/* pt3 contains the address of the first element of values */
pt3 = &values[0];
/* This is the equivalent of the above statement */
return 0;
}
I found this here (http://cplus.about.com/od/beginnerctutoria1/l/aa040702a.htm) and it does a great job of telling me how to use them but not when or where to use them. Especially referencing a variable's memory address.
Man, I hate feeling clueless. :-?
-
Try the following sites - they may have useful information, or at the very least, a wider audience to ask your questions of...
http://amigadev.amigaworld.net/
http://utilitybase.com/
-
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:
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:
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:
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.
-
@Cymric
This is exactly what I was looking for. :pint:
I'm at work right now so I will have to wait until I get home before I can digest this completely.
Kudos.
@chunder
thanks for the links as I will give them a look. :-)
-
pointers are also used for lists, predefined arrays can only be as long as well, the size you predefined for usage. but an array made out of pointers can go on for as long as you have memory available. see the following page for examples and more pointers advise: http://www.cs.cf.ac.uk/Dave/C/node10.html
-
If you ever used the "makelink" command from the CLI then you know how to use pointers. If you don't know how to use it then it might help to look at any AmigaDOS documentation you might have on it.
For a more complete idea of how pointers work: "hard" links are like references in C++ and "soft" links are like standard pointers in C.
Some of the most useful uses of pointers are linked lists and trees. Both of which you should get familiar with in a hurry because linked lists are used everywhere in AmigaOS and trees are used in directory structures.
-
@ mrsad:
Thanks for the link ... this is one of the more useful sites I've seen explaining this stuff.
@ samuraicrow:
If you ever used the "makelink" command from the CLI then you know how to use pointers. If you don't know how to use it then it might help to look at any AmigaDOS documentation you might have on it.
I've never really used the makefile command though I've come across it in documentation. With the new light on the info I am into my AmigaDOS manuals as I type.
Some of the most useful uses of pointers are linked lists and trees. Both of which you should get familiar with in a hurry because linked lists are used everywhere in AmigaOS and trees are used in directory structures.
Thanks for the heads up. I'm looking at a linked list tutorial right now.
-
I did (most of) a bachelor of computer science, so I had to buy a _lot_ of prescribed textbooks. A fair few of them involved c/c++. I can tell you most books out there on the subject are useless, or a headache at best.
I came across teach yourself c++ in 24 hours on sale at the local angus & robertson. I usually hate the 'teach yourself' series of books, but this one is actually useful. If you see one floating around for a few dollars/euro/yen/drachma etc I'd say, grab it.
I'm going to go through it again, because it's been too long since I've done any programming. I don't even understand any of my old stuff.
You seem to be well on track though. Keep it up!
-
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:
char string1[] "Hello ";
char string2[] "World\n";
char * tmp;
tmp = string1;
string1 = string2;
string2 = tmp
printf("%s%s", string1, string2);
would print
World
Hello
string1[2] is equivalent to *(string1 + 2)
-
I can tell you most books out there on the subject are useless, or a headache at best.
No kidding. I've learned a hell of a lot more about programming once I left college than while I was in school.
Be careful with pointers, though. A lot of times you don't need them. Spend some time to learn all the types and methods supported by your compiler, first. I did some quick and dirty stuff with Lattice a long time ago, but I'm not sure if Amiga compilers are as standardized as most PC compilers.
-
more power to ya here
I still prefer asm
who needs syntax rules when you can just "flip the switches"
metaphorically speaking
-
@ Generale
I came across teach yourself c++ in 24 hours on sale at the local angus & robertson. I usually hate the 'teach yourself' series of books, but this one is actually useful. If you see one floating around for a few dollars/euro/yen/drachma etc I'd say, grab it.
I'll keep an eye (or two) out for it. Seem to remember seeing it at Barnes and Nobles bookstore.
I'm going to go through it again, because it's been too long since I've done any programming. I don't even understand any of my old stuff.
:lol:
@ MskoDestny
Another use is to allow a function to modify values from the calling function.
Is there another way to do this without pointers? The answer is probably plain and clear in front of my face but right now I feel like I can't see the forest for the trees. If you know what I mean.
@ Wacoon
Be careful with pointers, though. A lot of times you don't need them. Spend some time to learn all the types and methods supported by your compiler, first. I did some quick and dirty stuff with Lattice a long time ago, but I'm not sure if Amiga compilers are as standardized as most PC compilers.
Maybe I should learn on my Linux system instead. I've read that pointers are super useful but easy to mess up, that is the main reason why I'm trying to wrap my head around them now.
@ Everyone
Thanks for all the responses. You all have been super helpful. :pint:
-
DethKnight wrote:
more power to ya here
I still prefer asm
who needs syntax rules when you can just "flip the switches"
metaphorically speaking
Ah, to the metal. I dabbled in asm way back in the 80's when I had my C64 (but was never that good) and wouldn't mind going back to learn. Seems like it has gotten way more complicated now. Maybe this will be something I pick up when I get more comfortable with C.
-
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.
-
Pointers can also have other pitfalls, especially when your process is threaded or uses system global values. Essentially in any concurrent system dealing with I/O, external APIs or IPC.
If you are using pointers, there is no guarantee that the values of the data in the location will be the same as they were. Other threads/processes can change the values on you.
If you need the value to be the same as when you entered the function, you either need to stop your process from being pre-empted (varies between OSes), call by reference, or utilise some sort of semaphor or mutex.
I can't believe I know this stuff :-o
Anyhow, if you're going to use linux/unix there are a lot of very nice, but non-ansi things to be aware of. By all means utilise them, but don't rely on them.
Assembler is still easy....just not MIPS RISC. Well, it's easy.....but time consuming. A lot like moving a mountain with a pair of tweezers, a grain at a time.
Anyway, yeah look out for the teach yourself c++ book. it's good value. When pointers and casting make my eyes glaze over, I look at that book.
-
Another use is to allow a function to modify values from the calling function.
Is there another way to do this without pointers? The answer is probably plain and clear in front of my face but right now I feel like I can't see the forest for the trees. If you know what I mean.
The only other way to modifiy a value inside a function is to make the value global (declared outside all functions). Using global variables we can make the code very simple in small programs, but in larger programs it actaully can make things more confusing.
int x; /* declare x as global, outside of all functions */
...
x = 10;
printf("%d\n", x);
do_something(); /* call function with address of x */
printf("%d\n", x);
...
void do_something(void)
{
x = x + 10;
}
Globals aren't bad, but use them in moderation and only when needed. Be careful to make the global varaible name something unique so it won't cause unexpected problems in other functions.
If x is not global the code looks like this:
...
int x;
x = 10;
printf("%d\n", x);
do_something(x); /* call function with a copy of x */
printf("%d\n", x);
...
void do_something(int x)
{
x = x + 10;
}
This will not modify x in the calling function. The reason is that x is passed by value to the do_something() function. This means that a new copy of x is made and modified inside the function. Once the function is complete, this new copy is destroyed and the original copy is used again.
We can use pointers to modify the original x value like this:
...
int x;
x = 10;
printf("%d\n", x);
do_something(&x); /* call function with address of x */
printf("%d\n", x);
...
void do_something(int *x)
{
*x = *x + 10;
}
This time the a new variable is still created, but instead of holding a copy of the value of x, it holds a copy if the address of x. This is called "pass by reference". The do_something() function was changed to modify the value pointed to by x. This time, the value of x in the calling function should've been changed.
-
@Otto
I teach C/C++ as a student teacher, if you like I can send you PDF files of the handouts I create for my students.
PM me with your email address if you are interested.
-
MskoDestny wrote:
char string1[] "Hello ";
char string2[] "World\n";
char * tmp;
tmp = string1;
string1 = string2;
string2 = tmp
Yiekes!
To be honest this won't even compile.
Array names CAN BE CONVERTED to pointers, but are not equally pointers. So impressions, like string1 = string2 or string2 = tmp won't go, sorry.
Happy compiling
PiR
-
Just consider pointers as arrays ;-)
As with a pointer like
char *tab;
char c;
you can do *tab=c; or the SAME tab[0]=c; so a pointer is an (unknown size) "array"
If your pointer ("array") come from a malloc() it give
you access to a block ("array") of memory
If it is the screen data pointer = it is an array of pixel
tab[320*3+64]=0; can set up as black the (x 64 y 3) pixel of a 256 colors screen ("array" of pixels)
If it is a string pointer so can change chars
tab[0]='H'; tab[1]='E'; tab[2]='L'; tab[3]='L'; tab[4]='O'; tab[5]=0; ("array" of chars)
Alain
:-D
-
Check out:
"C/C++ Programmer's Reference" by Herbert Schildt
link (http://www.amazon.com/exec/obidos/tg/detail/-/0072127066/qid=1113862317/sr=8-1/ref=sr_8_xs_ap_i1_xgl14/104-9274230-8114310?v=glance&s=books&n=507846).
It's no a gigantic wordy book which goes into intricate detail, it's short and to the point.
Unlike most programming books, it's cheap:-)
-
I really recommend Thinking In C++ Volume 1 and 2 by Bruce Eckel, they are just about the best C++ books available IMHO.
Download both books here (http://www.cwang.info/books/mindview/)
-
Also, I would recommend the following site especially if you want to get a real understanding of pointers:
http://cslibrary.stanford.edu/
Good tutorial on C, linked lists, trees and more...
Mordo
-
@all
Thanks for the examples and links. They certainly have been helpful and I doubt I would have been able to find them on my own. 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?
Thanks,
Dan
-
ottomobiehl wrote:
@all
Thanks for the examples and links. They certainly have been helpful and I doubt I would have been able to find them on my own. 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?
Thanks,
Dan
It all depends on what you want to develop really.
Low level stuff, stick with C. User level stuff, either will do but I recommend C++ as to a certain extent it's backwardly compatible due it just being an extension of C.
BeOS is the only OS that I know of where 99% of the entire OS (including the lowest level stuff)was written in C++. It has a lovely API too if you are a C++ developer.
-
Hi Nik,
I got your email and just sent you an email back stating so. :-)
Ok, I'll start looking at C++ and the whole OOP theory to boot. Seems like since I've started C seems pretty straight forward (so far) and I often wonder why I would be so intimidated to learn this stuff. Just goes to show that sometimes reality isn't as bad as your mind makes it to be.
As for BeOS; I've never had a chance to try it out. Suppose I should as I like to play with different OS's all the time.
-
Ok, I'll start looking at C++ and the whole OOP theory to boot.
Just don't go crazy with OOP. It was developed to solve a certain number of problems and it helps to know when not to use it. :-)
-
ottomobiehl wrote:
Pointers are my first hurdle. I think I understand what they are and what they do but not really what to use them for.
To declare a pointer I do this:
int *i;
right?
so if I do:
int x = 5;
int *i;
i = &x;
then i will equal the memory address of x but not the value of x, right?
Correct, but unexciting. To get the value of x from the pointer you dereference the pointer thus:
e.g.
int a;
a = *i;
The '*' dereferences the pointer to give the value of what is pointed at. The value of what is pointed at can also be changed thus:
*i = 5;
Which puts 5 at the address pointed to by i.
However, imagine you have a nice big number of things you wished to pass around, like maybe a person and you wished to pass that person (or persons like it) to a collection of functions. Let's say a person has a number of attributes like:
height, weight, dayOfBirth, monthOfBirth, yearOfBirth, name, sex
Each time you wanted to pass all that stuff to a function the call would be tedious indeed.
Instead you can create a structure and just pass a pointer to the structure a la:
typedef struct {
int height;
int weight;
int dayOfBirth;
int monthOfBirth;
int yearOfBirth;
char * name;
char sex;
} personT;
Note that name is a pointer to the start of a string of bytes.
The personT is now a type (like int or char) that you can use to declare variables as. Then you can set the values.
personT mrX;
mrX.height = 167;
mrX.weight = 70;
mrX.dayOfBirth = 5;
mrX.monthOfBirth = 6;
mrX.yearOfBirth = 1978;
mrX.name = "Fred X";
mrX.sex = 'M';
/* and now you can call a function that does something with persons */
printBirthday(&mrX);
and printBirthday would look something like...
void printBirthday(personT * person)
{
printf("%s was born %d/%d/%d\n",
person->name,
person->dayOfBirth,
person->monthOfBirth,
person->yearOfBirth );
}
Note that when you are accessing members of a struct, use . (dot). When you are accessing members of a pointer to a struct use -> (arrow or points-to).
-
But there's more....
One of the most fun things you can do with pointers is screw yourself over in mysterious and dangerous ways.
e.g.
int *bob;
*bob = 12;
You have now written the value 12 into a totally unknown location in memory. If bob is global it'll probably be 0 but it might not be. If bob is on the stack (local to a function) it could point any old place. If you have memory protection and you have written to some memory that doesn't belong to you, or is inappropriate for writing data then you will get a grim reaper. If you don't have mem protection or you get unlucky and write to memory you own, you get a mysterious and bizarre failure that doesn't seem like it is anything to do with this code and maybe only sometimes! FUN!!!
Always make sure your pointer points at something sensible before you use it. Aim THEN fire.
Also, watch out if you are pointing at stuff on the stack.
This is OK:
int func1()
{
int val = 4;
return func2(&val);
}
int func2 (int* val)
{
return *val * 2;
}
val is on the stack. You get a pointer to the stack address of val. You call the next func and the stack grows, val stays where it is, you still have the address, all is fine. The answer is 8.
If you try this:
int * func1()
{
int num = 4;
return #
}
int func2()
{
int val*;
val = func1();
return func3(val);
}
int func3(val)
{
int brianTheStrangeNumber = 23;
return *val * 2;
}
now the answer could be 46, or it could be twice the value of the address of num, or something else depending on how the stack frames are laid out. That's some cool stuff.
-
C is a lot of fun. Lately ive been doing some development with an 8052 microcontroller. I have a situation where i need to buffer data coming in various interrupts, and i need to get to that data in the order it was received, and with minimal delay. I've implimented a ring buffer, which is 16 bytes in length and resides in near memory for fast access.
One of the fun things about c is trying to put together the most efficient algorithms you can come up with. This is the most efficient way i could figure to impliment a ring buffer:
[font=Courier]
char buf[16];
int head ;
int tail ;
void put ( char data ) {
if ( ++head > 16 )
head = 0 ;
if ( head == tail ) {
if ( ++tail > 16 )
tail = 0 ;
}
buf[head] = data ;
}
char snatch ( void ) {
char c ;
if ( head == tail ) {
// this queue is empty
} else {
if ( ++tail > 16 )
tail = 0 ;
}
c = buf[tail] ;
return c ;
}[/font]
-
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.
-
@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.
-
BeOS is the only OS that I know of where 99% of the entire OS (including the lowest level stuff)was written in C++. It has a lovely API too if you are a C++ developer.
I believe the kernel was written in C but everything else was mostly C++.
The API is wonderful and clean, it's probably a pretty good place to learn OOP and C++.
Just don't go crazy with OOP. It was developed to solve a certain number of problems and it helps to know when not to use it.
I wrote an audio editor for BeOS, it was in (very bad) C++ but the effects were all pure C.
I'm writing a modular synth at the moment on OS X and it'll be part Objective-C but the audio processing parts will still be pure C (actually C with AltiVec extensions).
-
DethKnight wrote:
more power to ya here
I still prefer asm
who needs syntax rules when you can just "flip the switches"
metaphorically speaking
I couldnt agree more.
-
Gah! Damn it I have been too long confined to PHP and DHTML coding at work. I missed this entire thread. Oh well :-)
Must get back into C/C++ and ASM...
-
Karlos wrote:
Gah! Damn it I have been too long confined to PHP and DHTML coding at work. I missed this entire thread. Oh well :-)
Must get back into C/C++ and ASM...
HTML? Coding? :-?
Never! ;-)
-
PHP with Javascript/CSS for client side stuff. HTML is just the result ;-)
But I agree. Its a pile of bollocks next to proper coding. I have to degrade myself with the above in order to pay the bills and eat and stuff :roll:
-edit-
On the plus side, I managed to work a tiny amount on my old C++ stuff recently. Got a fledgling linux port, but I am not looking forward to the graphics side of it.
-
Karlos wrote:
PHP with Javascript/CSS for client side stuff. HTML is just the result ;-)
But I agree. Its a pile of bollocks next to proper coding. I have to degrade myself with the above in order to pay the bills and eat and stuff :roll:
A corporate slave like the rest us! :-D