Amit Singh Tomar
Amit Singh Tomar

Reputation: 8610

What is wrong with this C code

I have a piece of code where I am trying to return the square of the value pointed to by *ptr.

int square(volatile int *ptr)
{
  int a,b;
  a = *ptr;
  b = *ptr;
  return a * b;
}

  main()
  {
    int a=8,t;
    t=square(&a);
    printf("%d",t);
  }

Its working fine for me but author of this code said it might not work because of following reason:
Because it's possible for the value of *ptr to change unexpectedly, it is possible for a and b to be different. Consequently, this code could return a number that is not a square!. The correct way to do is

long square(volatile int *ptr)
{
  int a;
  a = *ptr;
  return a * a;
}

I really wanted to know why he said like that?

Upvotes: 12

Views: 3616

Answers (9)

Alper Kultur
Alper Kultur

Reputation: 135

I see some answers with *ptr can be changed by other threads. But this cannot happen since *ptr is not a static data variable. Its a parameter variable and local and parameter variables being hold inside stack. Each thread has its own stack section and if *ptr has been changed by another thread, it should not effect the current thread's.

One reason why the result might not give the square can be an HW interrupt might happen before assigning b = *ptr; operation as indicated below:

int square(volatile int *ptr) {
    int a,b;
    a = *ptr; //assuming a is being kept inside CPU registers.

    //an HW interrupt might occur here and change the value inside the register which keeps the value of integer "a"

    b = *ptr;
    return a * b;
}

Upvotes: 0

Russell Borogove
Russell Borogove

Reputation: 19037

I don't think the value of *ptr can change in this code barring an extremely unusual (and non-standards-compliant) runtime environment.

We're looking at the entirety of main() here and it's not starting up other threads. The variable a, whose address we are taking, is a local in main(), and main() doesn't inform any other function of that variable's address.

If you added the line mysterious_external_function(&a); before the t=square(&a) line, then yes, mysterious_external_function could start a thread and diddle the a variable asynchronously. But there's no such line, so as written square() always returns a square.

(Was the OP a troll post, by the way?)

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

Since the question has an accepted and correct answer, I will be brief: here is a short program that you can run to see the incorrect behavior happening for yourself.

#include <pthread.h>
#include <math.h>
#include <stdio.h>

int square(volatile int *p) {
    int a = *p;
    int b = *p;
    return a*b;
}

volatile int done;

void* call_square(void* ptr) {
    int *p = (int*)ptr;
    int i = 0;
    while (++i != 2000000000) {
        int res = square(p);
        int root = sqrt(res);
        if (root*root != res) {
            printf("square() returned %d after %d successful calls\n", res, i);
            break;
        }
    }
    done = 1;
}

int main() {
    pthread_t thread;
    int num = 0, i = 0;
    done = 0;
    int ret = pthread_create(&thread, NULL, call_square, (void*)&num);
    while (!done) {
        num = i++;
        i %= 100;
    }
    return 0;
}

The main() function spawns a thread, and modifies the data being squared in a loop concurrently with another loop calling the square with a volatile pointer. Relatively speaking, it does not fail often, but it does so very reliably in less than a second:

square() returned 1353 after 5705 successful calls <<== 1353 = 33*41
square() returned 340 after 314 successful calls   <<== 340 = 17*20
square() returned 1023 after 5566 successful calls <<== 1023 = 31*33

Upvotes: 8

jsbueno
jsbueno

Reputation: 110301

The idea of the volatile keyword is exactly to indicate to the compiler that a variable marked as such can change in unexpected ways during the program execution.

However, that does not make it a source of "random numbers" - it just advises the compiler - what is responsible for actually changing the variable contents should be another process, thread, some hardware interrupt - anything that would write to the process memory but not inlined in the function where the volatile declaration finds itself. In "older times" (compilers with less magic) everything it did was preventing the compiler from caching the variable value in one of the CPU registers. I have no idea on the optimisations/de-optimistions strategies triggered by it by modern compilers - but it will at least do that.

In the absense of any such external factor, a "volatile" variable is just like any other. Actually - it is just like any other variable - as variables not marked as volatile can also be changed by the same external causes (but the compiled C code would not be prepared for that in this case, which might lead to incorrect values being used).

Upvotes: 10

aayoubi
aayoubi

Reputation: 12069

The author is correct (if *ptr will be changed by other threads)

int square(volatile int *ptr)
{
  int a,b;
  a = *ptr; 
  //between this two assignments *ptr can change. So it is dangerous to do so. His way is safer
  b = *ptr;
  return a * b;
}

Upvotes: 1

mlemay
mlemay

Reputation: 1637

Because the value of the pointer *ptr might change between the first affection and the second one.

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 612964

In the code you present then there is no way for the variable a that is defined in your main to be modified whilst square is running.

However, consider a multi-threaded program. Suppose that another thread modified the value to your your pointer refers. And suppose that this modification took place after you had assigned a, but before you had assigned b, in the function sqaure.

int square(volatile int *ptr)
{
  int a,b;
  a = *ptr;
  //the other thread writes to *ptr now
  b = *ptr;
  return a * b;
}

In this scenario, a and b would have different values.

Upvotes: 2

Azodious
Azodious

Reputation: 13872

First understand what's volatile: Why is volatile needed in C?

and then, try to find answer by yourself.

It's a game of volatile and hardware world. :-)

Read answer given by Chris Jester-Young:

volatile tells the compiler that your variable may be changed by other means, than the code that is accessing it. e.g., it may be a I/O-mapped memory location. If this is not specified in such cases, some variable accesses can be optimised, e.g., its contents can be held in a register, and the memory location not read back in again.

Upvotes: 5

jgroenen
jgroenen

Reputation: 1326

If there is more than one thread, the value the pointer points to might change inbetween statement "a = *ptr" and statement "b = *ptr". Also: you want the square of a value, why put it into two variables?

Upvotes: 2

Related Questions