9three Posted January 23, 2011 Share Posted January 23, 2011 Hi, I'm currently learning C and I'm noticing between the book I'm reading and source code on the net of the following pattern: static void md5_hash(char *md5str, char *arg) { PHP_MD5_CTX context; unsigned char digest[16]; md5str[0] = '\0'; PHP_MD5Init(&context); PHP_MD5Update(&context, arg, strlen(arg)); PHP_MD5Final(digest, &context); make_digest(md5str, digest); } This is a snippet of mongo db source code. If you notice that one of the arguments is being passed a pointer as a parameter. But a few lines down, the variable is being passed to another function without the (*). I'm still a little confused. Aren't you suppose to always use the "*" when working with pointers within a function? I sometimes see the convention of always using the "*" and in other cases (like the code above), they don't bother with it. Which one is it? The book doesn't seem to explain the different approaches. Thanks EDIT: Here's another example that I wrote: void myFunction(char stuff[]) { printf("%s", stuff); } main(int argc, char *argv[]) { myFunction(*argv); return 0; } Arrays are passed by reference yet I cannot pass argv in main without the "*" as the compiler complains: In function ‘main’: warning: passing argument 1 of ‘myFunction’ from incompatible pointer type note: expected ‘char *’ but argument is of type ‘char **’ Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/ Share on other sites More sharing options...
requinix Posted January 23, 2011 Share Posted January 23, 2011 arg and *arg are two different things. The former is a variable and its value is an address in memory. The latter is actual data. You can use one or the other independently. The confusion is because the * is written next to the variable name. I prefer to write them like static void md5_hash(char* md5str, char* arg) { (Keep in mind that "char *arg" and "char* arg" are exactly the same.) Written that way it's more obvious that arg is a variable and it holds char* data (ie, a string). If you want to pass that data to a function you don't need to give it another thought: PHP_MD5Update(&context, arg, strlen(arg)); To explain *arg you need to understand how pointers work. Like I said, arg's value is an address in memory. Nothing more. When you use a * you dereference it: you treat the value of whatever as a memory address and go fetch whatever is at that location. arg *arg v v +--0+--1+--2+--3+--4+--5+--6+--7+--8+--9+ | 0 | 0 | 0 | 4 |'H'|'e'|'l'|'l'|'o'|NUL| +---+---+---+---+---+---+---+---+---+---+ Strictly speaking, arg=0x0004. *arg is then whatever value is at memory location 0x0004, and it's the character 'H' - and only that character. The thing is, C has the concept of "strings": a sequence of characters in memory that end with the first \0 (or NUL character). Treating *arg as the char that it actually is you get just 'H', but treating it as a string you get "Hello\0". For a char* you generally won't dereference it - you'll keep it as a string. In that sense, "char*" is a data type by itself, rather than a pointer to a char. Arrays are passed by reference Er, no. C does not have references. It always passes things by-value. char[] and char* are the same thing: both contain memory addresses. Arrays look like they're being passed by-reference because what's actually being passed is the memory address. char[] being the same as char* should explain why the error message says "char**" instead of "char*[]". A char*[] is an array of char*s - that is, an array of memory addresses. Arrays are also memory addresses so you actually have two levels of indirection: one for the array, one for the char pointer. argv itself, in memory, looks something like argv argv[0] argv[1] argv[2] v v v v +--0+--1+--2+--3+--4+--5+--6+--7+--8+--9+-10+-11+-12+-13+-14+-15+ | 0 | 0 | 0 | 4 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 3 | 0 | 0 | 0 | 0 | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ *argv[0] *argv[1] v v +-16+-17+-18+-19+-20+-21+ |'l'|'s'|NUL|'-'|'l'|NUL| +---+---+---+---+---+---+ argv=0x0004. argv[0] is akin to *argv so you get 0x0010 (=16); argv[1] is *argv + size of a pointer (here, 4) so you get 0x0013 (=19). argv[2] is *argv + 2* size of a pointer so you get 0x0000 - this NULL marks the end of the array, just like how a \0 marks the end of a string. *argv[0] is then *(0x0010) = 'l' and *argv[1]=*(0x0013) = '-'. In all, you get the command line as being "ls -l". Side note: You could access argv[3] if you wanted - it's simply *argv + 3* size of a pointer. You'd end up with argv[3]=0x6C73002D. But accessing *argv[3] would probably crash your program (with the dreaded "Segmentation fault") because the system is smart enough to know that whatever is at that location is not something you should be able to access. Now for your code: A char[] is an array of characters. A string. You can substitute in a char* if you wish, though the latter may raise a compiler warning (because while the two types are equivalent you specifically gave a char[] in one spot and a char* in another - for all the compiler knows that was done accidentally and incorrectly). argv is an array of strings. With a quick thought you should realize why the compiler complains if you try to pass just "argv" and not "*argv" or "argv[0]": a function wants a string but you were trying to give it an array of strings. The two are fundamentally different. Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1163865 Share on other sites More sharing options...
9three Posted January 23, 2011 Author Share Posted January 23, 2011 Hi, Thanks for the response and taking the time on explaining it to me. I do understand it now. Thanks Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164044 Share on other sites More sharing options...
9three Posted January 24, 2011 Author Share Posted January 24, 2011 Hi, Do you have a references into understanding memory (books, sites, etc)? I understand what you're saying but my knowledge is short on figuring out where the data actually exists in an address. Like how you figured out the address for *argv + 3. This makes perfect sense from your example of accessing a pointer and the data it contains: main(int argc, char *argv[]) { printf("%s", argv[1]); prtinf("%c", *argv[1]); } ...Yet I still feel a weak in this subject.. Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164186 Share on other sites More sharing options...
requinix Posted January 24, 2011 Share Posted January 24, 2011 Besides the little I got from school I'm self-taught so I don't really have any resources to point you to... I don't even remember where or when I learned how pointers work, and I think the underlying system/memory stuff came from... operating system design classes in college? Or maybe I knew it before then? Try to find C stuff from the 90's, or maybe even earlier. They're more likely to describe underlying processes than stuff you'll find nowadays. Go to a local [community] college/university and see what kinds of books the programming classes require. Have you considered joining a forum that isn't focused on PHP? Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164188 Share on other sites More sharing options...
9three Posted January 24, 2011 Author Share Posted January 24, 2011 I'll search around for C forums but in the meantime... I do have a question: main(int argc, char *argv[]) { FILE *f = fopen("/home/9three/Desktop/test.txt", "a"); if (f == NULL) { printf("Unable to open file for writting."); return 1; } fprintf(*f, "Testing..\n"); return 0; } If you notice I'm passing a pointer to fprintf. This doesn't compile and the error is "expecting FILE type". But "f" was declared as FILE type to begin with. So "f" is a pointer and variable at the same time? If I pass *f then I'm passing a pointer. But if I pass "f" then I'm just passing a variable with a specific type? eehhh.. what? btw, the book I'm reading was published on 1988 http://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/ref=sr_1_1?ie=UTF8&qid=1295833514&sr=8-1 Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164198 Share on other sites More sharing options...
requinix Posted January 24, 2011 Share Posted January 24, 2011 Remember when I mentioned how I put the * by the type instead of the variable name? Watch: FILE* f Now it looks like f is a variable of type FILE*. A FILE is actually a typedef (a pseudonym, if you will) for something - exactly what it is doesn't matter to you. Thus *f is a value of type FILE. The thing is, the first parameter to fprintf needs to be a FILE* as well. I'm not sure what's with the compiler message you got. Maybe you misremembered it? It should be complaining that you're giving it a FILE when it's expecting a FILE*. So fprintf(f, "Testing..\n"); cplusplus.com is a good reference. So "f" is a pointer and variable at the same time? A "variable" is a generic term for something whose value changes - the value can vary. A "pointer" is a concept. Programmers came up with the one-word term so they can talk to each other more easily. It means that whatever value you're dealing with (be it something hardcoded or be it from a variable) is a memory address. Think of it like term WhatIsThatThingCalledWhereYouUseMemoryAddressesAsValues() { return "a pointer"; } So yes: f is both a variable and a pointer. If I pass *f then I'm passing a pointer. But if I pass "f" then I'm just passing a variable with a specific type? f is a pointer. *f is a dereferenced pointer: you took the memory address reference (the actual value of f) and dereferenced it to get another, wholly separate value. That separate value can be anything - in this case, a FILE. What you're doing (incorrectly, as noted way above) is passing the dereferenced value of f. Except for the extra variable, what you're doing is the same as FILE deref = *f; fprintf(deref, "Testing..\n"); Think of FILE and FILE* as two separate data types. They both have things you can do and can't do. What they have in common is a way to go from one to the other. Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164216 Share on other sites More sharing options...
9three Posted January 24, 2011 Author Share Posted January 24, 2011 aha! The book mentioned about dereferencing but I didn't get it until you explained it. Another issue: main(int argc, char *argv[]) { printf("%s", argv[1]); printf("%c", *argv[1]); } argv[1] is a string, but *argv[1] is a char (or int type). When I dereferenced argv at position 1, it gave me a char type. Is that correct? Thanks. btw, Why bother going to another forum when you're doing a good job? Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164226 Share on other sites More sharing options...
requinix Posted January 24, 2011 Share Posted January 24, 2011 argv[1] is a string, but *argv[1] is a char (or int type). When I dereferenced argv at position 1, it gave me a char type. Is that correct? Yes. argv is an onion: the first layer is an array, the second layer is a pointer, and the core is a char. argv[1] peels off the first layer and gives you a char*; *argv[1] peels off both layers and gives you a char. Why bother going to another forum when you're doing a good job? ... Quote Link to comment https://forums.phpfreaks.com/topic/225373-c-pointers/#findComment-1164271 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.