jruiz
jruiz

Reputation: 71

Is there any way of protecting a variable for being modified at runtime in C?

I was wondering if there is any way of protecting a variable for being modified once initialized (something like "constantize" a variable at runtime ). For example:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int v, op;
    scanf( "%d", &op );
    if( op == 0 )
        v = 1;
    else
        v = 2;
// here transform v to a constant...
    .
    .
// ...and that any attempt to modify v yields to an error.
    .
    .
    return EXIT_SUCCESS;
}

Upvotes: 0

Views: 2019

Answers (4)

too honest for this site
too honest for this site

Reputation: 12262

The only way to prevent a global variable from being modified after initialization is declaring it const:

const int i = 5;

Hoever, as shown above, this requires it to be initialized with the definition. There is no way of initialize it from program code or using other than a constant expression which is evaluated at compile-time.

Note that while you can cast the const away, this results in undefined bahaviour and very likely leads to problems as const variables might be put into a read-only memory area of your program.

If you do need access control to such a variable, you would have to put it either as static into a function (with initialization on first call), or - better a seperate module/compilation unit (also as static). Use explicit setter/getter function on this then:

"wrapper.h":

#ifndef WRAPPER_H
#define WRAPPER_H

extern void set_once(int v);
extern int get_value(void);

#endif

"wrapper.c":

#include <stdbool.h>
#include "wrapper.h"

static struct {
    bool is_set;    // guaranteed false on startup
    int value;
} my_var;

void set_once(int v)
{
    if ( !my_var.is_set )
        my_var.value = v;
    else
        ; // report an error, e.g. abort()
}

int get_value(void)
{
    if ( !my_var.is_set )
        return my_var.value;
    // report an error, e.g. abort()
}

"main.c"

#include "wrapper.h"

int main(void)
{
    set_once(5);
    int i = get_value();
}

This way you will can get a runtime error if you use the value uninitialized or tries to set it more than once. Note the flag relies on global variables to be initialized to 0 (which is guaranteed to evaluate false). Although you might just ignore multiple sets, is is good practice to catch and report, at least during debugging/testing (e.g. using assert).

Edit:

The approach above is for global variables. If multiple variables are to be protected, it can be modified such as the functions take a pointer to the struct. If different types are to be used, pack the flag into its own struct and add that as the first anonymous field to one struct type for each type to be protected. If gcc-extensions are available&allowed, have a look at -fplan9-extensions. Using opaque pointers can avoid unintended external modifications.

For local variables, however, use @MattMcNabb's version.

Upvotes: 1

M.M
M.M

Reputation: 141534

You can make the result of the input be const like this:

int func()
{
    int op = 0;
    scanf( "%d", &op );
    if( op == 0 )
        return 1;
    else
        return 2;
}

int main()
{
    const int v = func();
    // ...
}

NB. Of course, there is no way to prevent undefined behaviour happening later in the program which appears to change v (since undefined behaviour can, by definition, have any effect).

Upvotes: 3

Blindy
Blindy

Reputation: 67352

No, never.

You could make it const, but that's more of a light hint and can be cast away with barely any effort. Besides if you have a pointer to something you can always change it by direct memory access.

You could also hide it inside a class that manages write access to it, but you're in the same situation as the first case -- you have a pointer to it.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 476930

Yes: Factor your code:

foo.h:

void do_something(int arg);

foo.c:

#include "foo.h"

void do_something(int arg)
{
    // implement
    //
    // Here "arg" is completely under your control.
}

foo_test.c:

#include "foo.h"

// add unit tests for do_something

main.c:

#include <stdio.h>
#include <stdlib.h>
#include "foo.h"

int main(void)
{
    int op;
    if (scanf("%d", &op) != 1) { abort(); }

    do_something(op == 0 ? 1 : 2);
}

It is now completely clear from the code that the function argument of do_something is private and local to the function and cannot be affected by anything above the call.

Upvotes: 1

Related Questions