简体   繁体   中英

Why can't I pass the pointer instead of the pointer to pointer to function?

I have the following program that wants to modify s such that we can print out "Hello World!" at the end

#include <stdio.h>

// modify this function
void function(char** c)
{
    *c = "Hello World!";
}

int main()
{
    char* s;
//    function(&s); 
    function(s);
    puts(s);
    return 0;
}

Normally, we would just do function(&s) . However, my question is why can't we just use function(s) ? Of course, doing so will raise warning during the compile time but since s contains the memory address say 0xab . If we modify the content on 0xab from 0x00 to "hello world!" , the address hold by s won't change and we should still see "Hello World!" message.

I'm wondering why function(s) won't work in this case? I compile the program on Mac.

Reference:

Since s is uninitialized, its contents are unknown (and using it is invalid according to the C standard). Suppose it did contain some value and the C implementation did pass that value to function for the parameter c . Then function attempts to write the address of "Hello World" to the place where c points. But where is that place?

We supposed s contained some value. But it is quite likely an address that is not mapped in your address space. Your small program likely does not use much of even a 32-bit address space, so most of that space will not be mapped to real memory by the operating system. So, if you pick a random address and try to write there, it is probably an invalid address, and your process will crash.

Another likely possibility is that s happens to contain zero because this is early in your program and nothing has written anything else to the place where the compiler put s , so it just contains the zeros that the operating system initialized your memory with. In many systems, the address zero is deliberately left unmapped in address spaces just for this purpose, so that uses of uninitialized pointers will crash.

More than that, a good compiler will see that s is used without being initialized and will warn you about that. If you force it to generate code anyway, the optimizer may, as aa result of its usual transformations, completely replace this broken code with something else.

If you are unlucky, then the uninitialized s will contain a value that happens to be a valid address in your address space, and then function may write the address of "Hello World!" into it. Now you are writing data into some place in your process that may be needed for another purpose, so it can break your program in a variety of ways. Note that this does not give the result you seem to think it would, that puts would write “Hello World!”. If function did write the address of "Hello World!" into *c , the address would be in memory at the place s happens to point to. Then you are passing to puts the address of a place where there is an address. However, puts expects the address of a place where there are characters. It will read the bytes of the address of "Hello World!" and print them, until it reaches a zero byte. Most often, the result is unprintable or at least unusual characters.

s is not initialized, so it holds some garbage address (probably an invalid one).

When you do *s = "Hello World!"; you are writing "Hello World!" (which is a pointer value) to some garbage address (probably an invalid one).

Let's say it doesn't crash though - then puts will read the bytes from that same garbage address (ie it will read the address of the string, not the string) and display them on the screen.

After running the incorrect code the memory might contain these values for example:

Address      Value (4 bytes at a time)
...
0x12345678   0x65401234      <- some important thing you just overwrote that is liable to make your program crash,
                                now it holds the address of the string literal
...
0x4000000C   0x12345678      <- variable 's' in main
0x40000010   0x12345678      <- variable 's' in function, copy of variable 's' in main
...
0x65401234   'H', 'e', 'l', 'l'  <- where the compiler decided to put the string literal
0x65401238   'o', ' ', 'W', 'o'
0x6540123C   'r', 'l', 'd', '!'
0x65401240   0

When you call puts(s); you would be calling puts(0x12345678); and it would print the bytes 0x65401234 (but it wouldn't print "0x65401234", it'd try to print the letters corresponding to those)

If you do it right, you end up with:

Address      Value (4 bytes at a time)
...
0x4000000C   0x65401234      <- variable 's' in main
0x40000010   0x4000000C      <- variable 's' in function, has address of variable 's' in main
...
0x65401234   'H', 'e', 'l', 'l'  <- where the compiler decided to put the string literal
0x65401238   'o', ' ', 'W', 'o'
0x6540123C   'r', 'l', 'd', '!'
0x65401240   0

Then puts(s) is puts(0x65401234) which prints the string.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM