3

I was wondering and could not find an answer to my question anywhere. Lets say I do have two functions:

void function_B(int * data){
 // I am able to modify its content
}

void function_A(int * data){
 // I do not want to be able to modify the data nor its content here only pass the pointer to function B, so it can be changed there
    function_B(data); 
 // Nor I want to be able to modify it here
}

I am familiar with the const keyword, but if I make the data and the pointer constant for function A, it cannot be changed also by the function B. How could I write such a code? Is this even possible with c?

Najiva
  • 142
  • 8
  • It sounds like you want what object oriented data encapsulation is designed to provide (i.e. I can change the data, but only in ways allowed and provided by the encapsulating object, i.e. by the provided public functions. Would switching to C++ be an imaginable option? – Yunnosch Jan 30 '18 at 14:11
  • 3
    Even if `function_A` doesn't modify the data itself, it does it *indirectly* by calling `function_B`. Therefore the data can not be constant. – Some programmer dude Jan 30 '18 at 14:11
  • 1
    Why would you want to do that? If you need it then please replace your programmers. – Klas Lindbäck Jan 30 '18 at 14:23
  • Yunnosch, we cannot switch to c++. But you have a good point I think. Klas, we need to test everything. We have got a function passing pointer to another function. We need to test if function A did not modify (corrupt) the data between the time it called function B and returned. – Najiva Jan 30 '18 at 14:51
  • the real question is why you want to modify the pointer value? – Maksym Bodnar Jan 30 '18 at 14:51
  • 1
    Your requirements do not make sense. You cannot have a const-correct function which goes off to modify parameters, simple as that. – Lundin Jan 30 '18 at 14:52
  • Make a copy: `void function_A(const int * data){ int c = *data; function_B(&c); }`. – chux - Reinstate Monica Jan 30 '18 at 14:52
  • @chux I dont even need to work with it. I just have to pass it. – Najiva Jan 30 '18 at 15:00
  • 1
    @Najiva Passing the pointer is working with it. – chux - Reinstate Monica Jan 30 '18 at 15:03
  • If this is for some callback interface, would using `void *data` be an option? Then it is strictly not const but still cannot be changed in `function_A` – Gerhardh Jan 30 '18 at 15:53

4 Answers4

2

One thing you could do is factor out the parts of function_A that are not allowed to modify the data, something like this:

void func_A_preB(const int *data)
{
    // Do stuff with data but don't change it.
}

void func_A_postB(const int *data)
{
    // Do stuff with data but don't change it.
}

void function_B(int *data)
{
    // Can change data.
}

void function_A(int *data)
{
    func_A_preB(data);
    function_B(data);
    func_A_postB(data);
}
Ian Abbott
  • 15,083
  • 19
  • 33
  • This however will disconnect `preB` from `postB`. What if the OP had code post `function_B` dependent on code preceding `function_B`? – machine_1 Jan 30 '18 at 17:25
  • @machine_1 Then they'd have to pass extra state around via a pointer to a bespoke "working data" structure or something along those lines. – Ian Abbott Jan 30 '18 at 17:42
1

It is questionable design to forbid change in one function but allow it in a called function. But it is indeed allowed by the C standard. As others have already said, the trick is to change function_A. The simplest change would be:

void function_A(const int * data){
 // I do not want to be able to modify the data nor its content here only pass the pointer to function B, so it can be changed there
    function_B((int *) data); 
 // Nor I want to be able to modify it here
}

But it bad design because the prototype of function_A makes a promise to never change data and does change it. If you pass an array which was initially declare as const, you will even invoke undefined behaviour:

const int arr[] = {1, 2, 3};
function_A(arr);     // will compile because function_A declared a const param

In function_B you will try to change a const object which is explicitely UB.


The correct way (which may or not be acceptable for your use case) is to use an auxiliary variable:

void function_A(int * _data){
 const int *data = _data;   // always valid
 // data cannot be used to change anything, but of course _data could
 // data[0] = 1; would raise a compilation error
    function_B(_data); 
 // Nor I want to be able to modify it here
}
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
1

According to your problem statement, function_B() has to be called within function_A(), and it shall modify the content pointed to by the pointer data. This signify that the pointer passed to to function_B() must not be declared as const.

As such, if the pointer data has to come from the caller function_A(), then it is going to be able to modify what precedes the call to the function function_B(), and you can't do anything about it.

You can however declare a const pointer within function_A() that points to the content of the non-const pointer data, and give it the suffix _A to mean that this pointer shall be used in place of the original pointer data within function_A().

void function_B(int * data) {
    // I am able to modify its content
}

void function_A(int * data) {
    const int *data_A = data;
    // data_A can't change anything...


    // I do not want to be able to modify the data nor its content here only pass the pointer to function B, so it can be changed there
    function_B(data);
    // Nor I want to be able to modify it here
}
machine_1
  • 4,266
  • 2
  • 21
  • 42
0

The only thing you can do is this:

void function_B(int * data) {
  *data = 0;
}

void function_A(const int * data) { // << add the const keyword here
  *data = 0;               // this won't compile (which is what you intend)
  function_B(data);        // this won't compile either, but that's what you want to do

  function_B((int*)data);  // but this will compile
}

In function_A, data is a const pointer so you cannot modify the data it points to, but you cannot pass it to a function whose parameter is a non const (like function_B in this example) either.

But you can cast the const away like in the example above.

Keep in mind that this is still a very questionable design, as casting away a const defies it's very own purpose as it results in undefined behaviour if the data passed to function_A is constant.

const int a = 2;
int b = 2;

int main()
{
  function_A(&a);  // a is constant => results in undefined behaviour
                   // when function_B tries to modify the data

  function_A(&b);  // OK
}
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 4
    `function_B((int*)data);` does however invoke undefined behavior, even though it compiles. Bad idea, bad advice. – Lundin Jan 30 '18 at 14:38
  • @Lundin I didn't know that, but why? – Jabberwocky Jan 30 '18 at 14:42
  • @Lundin the accepted answer of [this SO question](https://stackoverflow.com/questions/9079104/is-it-undefined-behaviour-to-cast-away-the-constness-of-a-function-parameter) states that this (questionable) casting away of `const` yields in UB only if the data passed to `function_A` is not modifiable. – Jabberwocky Jan 30 '18 at 14:55
  • Yes, what matters is the so-called _effective type_ of the passed data. If it is `const` qualified, you invoke UB, if it is not, then the cast is ok (but bad practice). The actual pointer conversion is always ok, that's not the problem. – Lundin Jan 30 '18 at 14:58
  • @Lundin OK, thanks. So the answer remains valid, but it's bad practice (as I mentionned in the last sentence.) – Jabberwocky Jan 30 '18 at 15:01
  • 1
    We don't know the type used by the caller, and it is very strange to implement const correct function but always assuming non-const data to get passed to it. – Lundin Jan 30 '18 at 15:03
  • Irrespective of the *effective type* of the passed data, it is still breaking a contract with the caller of `function_A` that the data will not be modified. So it's bad code. – Ian Abbott Jan 30 '18 at 15:24
  • @IanAbbott I mentioned that this is a very questionable design. Actually what is questionable here in first place is the OP's requirement. – Jabberwocky Jan 30 '18 at 15:30