这段C代码有什么问题

发布于 2024-12-27 01:24:57 字数 460 浏览 6 评论 0原文

我有一段代码试图返回 *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);
  }

它对我来说工作正常,但此代码的作者表示由于以下原因它可能无法工作:
由于 *ptr 的值可能会意外更改,因此 a 和 b 也可能不同。因此,此代码可能返回一个不是正方形的数字!。正确的做法是

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

我真的很想知道他为什么这么说?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

扭转时空 2025-01-03 01:24:57

volatile 关键字的目的正是向编译器指示标记为此类的变量在程序执行期间可能会以意想不到的方式发生变化。

然而,这并不使它成为“随机数”的来源 - 它只是建议编译器 - 负责实际更改变量内容的应该是另一个进程、线程、一些硬件中断 - 任何会写入进程内存的东西,但未内联到 易失性声明所在的函数中。在“旧时代”(魔法较少的编译器),它所做的一切都是阻止编译器将变量值缓存在 CPU 寄存器之一中。我不知道现代编译器触发的优化/去优化策略 - 但它至少会做到这一点。

在没有任何此类外部因素的情况下,“易失性”变量就像任何其他变量一样。实际上,它就像任何其他变量一样,因为未标记为 volatile 的变量也可以通过相同的外部原因进行更改(但在这种情况下,编译的 C 代码不会为此做好准备,这可能会导致使用不正确的值) 。

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).

云之铃。 2025-01-03 01:24:57

由于这个问题有一个被接受和正确的答案,我将简短地说:这是一个简短的程序,您可以运行它来查看自己发生的不正确行为。

#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;
}

main() 函数生成一个线程,并在一个循环中修改正在平方的数据,同时另一个循环使用易失性指针调用 square。相对而言,它并不经常失败,但在不到一秒的时间内就非常可靠地失败了:

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

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
北座城市 2025-01-03 01:24:57

首先了解什么是易失性:为什么 C 中需要易失性?

,然后尝试自己去寻找答案。

这是一个不稳定的硬件世界的游戏。 :-)

阅读 Chris Jester-Young 给出的答案:

易失性告诉编译器您的变量可以通过其他方式更改,而不是访问它的代码。例如,它可以是 I/O 映射的内存位置。如果在这种情况下没有指定,则可以优化某些变量访问,例如,可以将其内容保存在寄存器中,并且不会再次读回内存位置。

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.

忱杏 2025-01-03 01:24:57

如果有多个线程,则指针指向的值可能会在语句“a = *ptr”和语句“b = *ptr”之间发生变化。另外:你想要一个值的平方,为什么要把它放入两个变量中?

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?

笔芯 2025-01-03 01:24:57

在您提供的代码中,在 square 运行时,无法修改 main 中定义的变量 a

然而,考虑一个多线程程序。假设另一个线程修改了指针引用的值。并假设此修改发生在您在函数 sqaure 中分配 a 之后、分配 b 之前。

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

在这种情况下,ab 将具有不同的值。

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.

星軌x 2025-01-03 01:24:57

作者是正确的(如果*ptr会被其他线程改变)

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;
}

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;
}
迷荒 2025-01-03 01:24:57

因为指针 *ptr 的值可能会在第一个影响和第二个影响之间发生变化。

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

慕烟庭风 2025-01-03 01:24:57

我认为除非出现极其不寻常(且不符合标准)的运行时环境,否则 *ptr 的值不会在这段代码中发生变化。

我们在这里查看整个 main() ,它没有启动其他线程。我们正在获取其地址的变量 amain() 中的本地变量,并且 main() 不会通知任何其他函数该变量的地址。

如果您在 t=square(&a) 行之前添加了 mysterious_external_function(&a); 行,那么是的,mysterious_external_function 可以启动一个线程并异步调用 a 变量。但没有这样的行,所以编写的 square() 总是返回一个正方形。

(顺便问一下,OP 是一个巨魔帖子吗?)

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?)

牛↙奶布丁 2025-01-03 01:24:57

我看到一些带有 *ptr 的答案可以被其他线程更改。但这不可能发生,因为 *ptr 不是静态数据变量。它是一个参数变量,局部变量和参数变量保存在堆栈内。每个线程都有自己的堆栈部分,如果 *ptr 已被另一个线程更改,则它不应影响当前线程的堆栈部分。

结果可能不给出平方的原因之一可能是在分配 b = *ptr; 之前可能发生硬件中断。操作如下:

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;
}

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文