Jump to content

[C] Pointers


9three

Recommended Posts

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 **’

 

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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..

Link to comment
Share on other sites

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? ;)

Link to comment
Share on other sites

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

 

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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? :P

 

Link to comment
Share on other sites

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? :P

...

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.