-3

I am calling a function that returns a variable through a pointer parameter. I do not care about the return value of this parameter nor do I want to make a dummy variable to pass to the function. For a simple example's sake, let's say the function is as follows and I don't care about, nor want to make a dummy variable for parameter "d".

void foo(int a, int b, int* c, int* d)
{
*c = a+b;
*d = a+b+*c;
}

I understand that a NULL pointer is in theory a pointer to a location that is not the address of any object or function. Would it be correct to pass NULL into "d" in this function if NULL was defined as the following? Or is this going to change whatever is at the 0'th element in memory?

#define NULL ((void *)0)

The target device is an MSP430 and I am using IAR C. No operating system is used therefore no memory management is implemented

EDIT: Please note that I do not want to create a dummy variable. Also if there was a way to fool the compiler into optimizing the "d" parameter out without altering the function definition, this is preferable.

EDIT#2: I would rather not use the & operator in the function call as it generates inefficient code that I do not want to generate

EDIT#3: For those who don't believe me when I am talking about the & operator... the compiler manual states "Avoid taking the address of local variables using the & operator. This is inefficient for two main reasons. First, the variable must be placed in memory, and thus cannot be placed in a processor register. This results in larger and slower code. Second, the optimizer can no longer assume that the local variable is unaffected over function calls."

Jeremy
  • 103
  • 12
  • 1
    You must either change the function (you say you can't), or you must use a dummy variable (if your example is realistic, this can probably be on the stack, so it will cost you next to nothing regardless of your concern about the & operator. – Will Dean Oct 19 '12 at 13:59
  • To whoever is voting this down, I am having a hard time seeing how my question is without research effort, unclear or not useful... – Jeremy Oct 23 '12 at 11:48
  • It is not useful because it is a narrow and incompletely specified situation that will not help anybody else. If your “solution” works, it is because the function was defined in the same compilation module (included in a header if not in the same source file) as the call, and the compiler recognized an optimization, which is unlikely to be related to your specific solution of using another variable to hold the address. The notion that `&` generates inefficient code is false on virtually every modern processor. This problem contains no general information that will be useful to others. – Eric Postpischil Oct 23 '12 at 12:19
  • @EricPostpischil IAR C EW430 Compiler Reference Page 182 Section: "Facilitating good code generation" Subsection: " Writing Optimization-Friendly Source Code": "Avoid taking the address of local variables using the & operator. This is inefficient for two main reasons. First, the variable must be placed in memory, and thus cannot be placed in a processor register. This results in larger and slower code. Second, the optimizer can no longer assume that the local variable is unaffected over function calls." – Jeremy Oct 23 '12 at 12:23
  • Neither of those reasons applies in this situation because your “solution” **does** take the address of `Dummy`. It evaluates `&Dummy` when initializing `Dummy_ptr`. Thus, if taking the address requires `Dummy` to be placed in memory, it is placed in memory with your solution. And, if taking the address prevents the optimizer from reasoning that it is unaffected over function calls, then the optimizer is prevented from so reasoning in your solution. Therefore, your solution has not benefitted from this advice in the manual. Regardless, this problem and solution is not useful to others. – Eric Postpischil Oct 23 '12 at 12:46
  • @EricPostpischil As I have stated before, i inspected the assembly code. While the dummy variable exists on the stack, as the function only returns the value *d and the &dummy is not actually required, there are no instructions that are related to the address of the dummy variable. There is no assembly that actually loads the address of the dummy variable into the pointer because the pointer is never passed to the function because the optimizer removed the extraneous code from the function. If this is unhelpful to you specifically, fine. you're not the only programmer on StackOverflow. – Jeremy Oct 23 '12 at 13:01
  • I did not downvote this because it is not useful to me. It is not useful generally: No insight has been illuminated about C, use of `&`, compilers, code optimization, or programming in general. Whatever benefit your “solution” gained appears to be due to a quirk in a specific version of a specific compiler for a specific processor, a quirk which caused it to optimize one specific code sample when the natural way of writing code could have been optimized equally well. Nobody can rely on it working for them. And there is misleading information here; notably the `&` operator is not at fault. – Eric Postpischil Oct 23 '12 at 13:44

10 Answers10

7

No, it is not correct.

The C standard does not define the behavior when you do this. On many systems, it will cause an error (some sort of memory fault) when foo attempts to store to address 0. If it does not, then you will have written data to address 0, presumably overwriting something else there that may have been needed, so your system may fail at a later time.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • I am not using an operating system, therefore no memory fault. – Jeremy Oct 19 '12 at 13:42
  • 7
    @Jeremy: Memory faults are initiated by hardware, not by the operating system. However, if you are configuring your hardware so that writing to address zero does not cause a memory fault, then you are essentially creating a dummy variable at address zero. Additionally, you are relying on compiler behavior that is not guaranteed by the C specification. This is common in embedded platforms, but you should be sure your compiler supports what you are doing. – Eric Postpischil Oct 19 '12 at 13:52
  • If there are memory-mapped registers in your CPU and there's one such register at address 0, you will corrupt it. The effect of this is going to depend on what register that is. If it's the instruction pointer (AKA program counter) or the stack pointer or the flags register (AKA program state word) or some system register or some general-purpose register, you can cause a hang of the program, a reset of the system, or corrupt memory elsewhere. If it's an I/O register, you can disrupt communication with the related device. You really don't want to have this bug in the code. – Alexey Frunze Oct 20 '12 at 10:31
  • @Jeremy If you are not running an operating system, then your function will most likely write something to memory address 0x0. That might initiate a hardware trap, even without an OS. Or it might simply write to memory address 0x0 if you pass it a NULL pointer, which could be quite fatal if something important is stored there - e.g. many processors store the interrupt vector table there until someone(such as a bootloader/an OS) moves it elsewhere. You don't want to damage that. – nos Oct 23 '12 at 13:08
  • @nos That is what I expected, as I stated in my question. Unfortunately the definition of the NULL pointer is a pointer that does not point to any object, function, etc. This can cause confusion as it does, in actuality, point to something. – Jeremy Oct 23 '12 at 13:13
2

You should change your function a bit to allow passing NULL

void foo(int a, int b, int* c, int* d)
{
    if(c != NULL)
    {
       *c = a+b;
       if(d != NULL) 
       {
          *d = a+b+*c;
       }
    }
}

Now you can safely pass NULL. Otherwise, as the other answers already state, you end up dereferencing a NULL pointer which results in undefined behavior.

Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • I cannot change the function, just the function call. – Jeremy Oct 19 '12 at 13:45
  • 3
    @Jeremy. Then you cannot pass NULL. period. Pass a dummy variable. – Armen Tsirunyan Oct 19 '12 at 13:45
  • as stated in the question, I do not want to pass a dummy variable nor do i want to use the & operator as this would generate inefficient code in my implementaion. – Jeremy Oct 19 '12 at 13:50
  • @nos as per my edit on the main post, I was looking for a method of optimizing out the d parameter, reducing code space, without altering the function definition. I apologize the original question was not clear. I have posted a solution that allowed me to reduce code space – Jeremy Oct 23 '12 at 13:07
1

In your example, if you don't care about the pointer d and you pass NULL as you defined then it'll probably crash due to dereferencing NULL. You should pass a valid pointer even if you don't care about the result.

Why not just declare a temporary and pass?

int tempd;    
foo(a,b,&c, &tempd);
P.P
  • 117,907
  • 20
  • 175
  • 238
1

There is no such thing as the 0th element in memory due to virtual memory. However, if you attempt this, your program will crash with a memory exception. I assume you want to ignore d if it's null so simply do this:

if(d != NULL) { *d = a+b+*c }

Flayneorange
  • 228
  • 1
  • 9
1

Since you don't want to create a dummy variable and can't change the function you'll most likely end up scribbling at the memory position 0 on your device whatever that means. Maybe it's a memory mapped hardware register, maybe it's just normal physical memory.

If it's a register, maybe it doesn't have any effect unless you write the magical value 4711 into it which will happen once every three months and the device halts and catches fire. (has happened to me, it's fun to overwrite the boot eeprom on a device)

Or if it's memory maybe you'll send a NULL pointer to a different function later and that function will happily read the value that this function wrote there and you'll end up at 5 in the morning tearing your hair out and yelling "this can't possibly affect that!". (has happened to me on some ancient unix that used to map the NULL page)

Maybe your compiler adds a safety net for you. Maybe it doesn't. Maybe the next version will. Maybe the next hardware revision will come with memory unmapped at address 0 and the device will halt.

I'd create a dummy variable in the calling function and move on to a more interesting problem, but if you're a stress junkie, pass NULL and see what happens (today or in 10 years).

Art
  • 19,807
  • 1
  • 34
  • 60
0

In that specific example code both passed in pointers are dereferenced. Dereferencing NULL is undefined behavior. Just go with the dummy variables.

In general: If a function accepts a pointer it should state if the null pointer is a valid value for the argument. If it doesn't say anything stay on the safe side and just assume it isn't. This will safe you a lot of grief.

pmr
  • 58,701
  • 10
  • 113
  • 156
0

Interesting question! Generally speaking, NULL is reserved as an "invalid" address. You shouldn't try to write to it, but I don't think the standard specifies what should happen if you do. On a Windows machine, this will generate an access violation exception. I don't know what will happen on your device.

In any case, passing NULL is the usual way to indicate that you're not interested in the value of an out parameter. But your function must be aware of this and act accordingly:

if( c ) {
    *c = a+b;

    if( d ) {
        *d = a+b+*c;  
    }
}

Edit

If you can't change the function definition, then you're probably out of luck. You can trick the compiler into not passing d if the calling convention is cdecl. Just declare the function without the d parameter:

extern void foo( int a, int b, int * c );

However, you're definitely into dangerous shenanigans territory here. The function definition will still expect the d paramater, so it will see random garbage.

The only other thing I can think of is passing a fixed address. Since you're writing for a specific device, there might be an address range that's safe to write to. (That is, it won't cause exceptions or corrupt actual memory.)

void * SafeAddress = (void *)0x12345678;

foo( a, b, &c, SafeAddress );

Of course, the easiest thing is to just use the dummy variable. I know you've said more than once that this generates inefficient code, but does that have to be the case? Does it make a difference if the dummy is a local variable versus a global one?

Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
0

The function tries to store value at the provided address. If the address is invalid, then there will be a malfunction of some sort -- whether you use an operating system or not is irrelevant.

Either you have to give the function some valid address (even if you don't care for the value in a particular case), or you have to change the function so that it does not store the value (and, probably, does not even even compute it), if the address for it is NULL (which may or may not be 0x0 on your platform, BTW).

You keep repeating, that you "don't want" to do the former and can not do the latter. Well, then you have an unsolvable dilemma. Maybe, there already exists some other address, where dummy values like this can be stored in your program (or on the platform) -- you can pass that.

If there is no OS involved, then you must be dealing with some funky programmable device, which means, there ought to be seasoned C-programmers around you. Ask them for confirmation of what you are told here -- clearly, you aren't trusting the answers given to you by several people already.

Mikhail T.
  • 3,043
  • 3
  • 29
  • 46
  • "clearly, you aren't trusting the answers given to you by several people already." It is not that I did not trust other answers, it is that none of the answers provided me with a method of reducing code space and increasing optimization. I have answered my own question, however. http://stackoverflow.com/questions/12975422/passing-a-null-pointer-to-in-a-function-in-c/12976359#12976359 – Jeremy Oct 19 '12 at 17:47
  • Well, in that answer you linked to, you use the method, that several people (myself included) have suggested here - a _dummy variable_... Though you started off here insisting, you don't want to do that, I'm glad, you finally understood, there is no other way. Congratulations. – Mikhail T. Nov 19 '12 at 14:43
-1

It will try to assign something at memory location 0x0, so I'd say it will crash

gogoprog
  • 613
  • 4
  • 7
  • The NULL pointer is treated specially in some compilers; it does not necessarily map to (physical or virtual) memory location 0. It's also not guaranteed that a crash will occur, that depends on the operating system (if any). – Fred Foo Oct 19 '12 at 13:41
-2

I was able to save code space without increasing memory usage on the stack by declaring a dummy variable as well as a pointer to the dummy variable.

int  Dummy;
int* Dummy_ptr = &Dummy;

This allowed the compiler to make optimizations on the function call as the & operator was not used in the function call.

The call is now

foo(a, b, c_ptr, Dummy_ptr);

EDIT: For those of you who don't believe me. I took a look at the assembler. The Dummy variable exists on the stack, though because it is not used later on, and because it is only a return from the function the address is never passed to the function and any use of that variable in the function is optimized out.

Jeremy
  • 103
  • 12
  • What makes you think this results in better machine code? Passing `&Dummy` to `foo` likely results in an instruction like `mov r4, sp, #offset`, so it is a single register-only instruction (add immediate to stack pointer, put result in register). Passing `Dummy_ptr` likely results in the same thing (if the compiler recognized an optimization) or an instruction like `mov r4, offset(sp)` (add immediate to stack pointer, load from memory at that address, put result in register). That requires a memory read, which the former did not. The former is a strict subset of the latter. – Eric Postpischil Oct 20 '12 at 11:51
  • @EricPostpischil To make a long story short, the code usage of this is smaller when compiled. The compiler manual states that the & operator method of taking an address results in more inefficient code. Perhaps the initialization of the Dummy_ptr as &Dummy allows the compiler to make more optimizations. – Jeremy Oct 22 '12 at 11:52
  • Have you inspected the generated code? How is it possible the compiler could easily prepare the address of Dummy (which is needed to load its contents) but not easily prepare the address of Dummy_ptr? – Eric Postpischil Oct 22 '12 at 13:11
  • Your edit indicates the generated code is satisfactory when your code in this “solution” is used. Is the generated code satisfactory or unsatisfactory in the alternative, when `&Dummy` is passed directly to the function, without using `Dummy_ptr`? In this case, it appears the optimizer has removed the passing of `Dummy_ptr` based on properties of the called function. These same properties would apply when passing `&Dummy`, so the optimizer should do the same thing. – Eric Postpischil Oct 23 '12 at 12:48
  • @EricPostpischil I apologize if my solution was unclear. The code generated by passing &Dummy > The code generated by passing Dummy_ptr. I do not know exactly what caused the compiler to do so. For that I would need the source code of the compiler. – Jeremy Oct 23 '12 at 13:19