C问题:如果我将一个变量的地址传递给一个修改它的函数,是否可以保证该变量为;“重新加载”;回来后?

C问题:如果我将一个变量的地址传递给一个修改它的函数,是否可以保证该变量为;“重新加载”;回来后?,c,performance,volatile,C,Performance,Volatile,我有很多以前类似的代码: int num = 15; if(callback) callback(&num); /* this function may or may not change the value of num */ if(num == 15) /* I assumed num did not need to be volatile and is reloaded */ do_something(); else do_somethi

我有很多以前类似的代码:

int num = 15;  

if(callback)  
  callback(&num);  /* this function may or may not change the value of num */  

if(num == 15)  /* I assumed num did not need to be volatile and is reloaded */  
  do_something();  
else  
  do_something_else();  
但是,我现在有了更复杂的结构和指向它们的指针:

struct example { int x,y,z; };  
struct example eg, eg2, *peg;   
int whatever;  
char fun;  

struct variables { struct example **ppeg; int *whatever; char *fun; };  
struct variables myvars;  

peg = ⪚  
myvars.ppeg = &peg;  
myvars.whatever = &whatever;  
myvars.fun = &fun;  
[...]  
peg->x = 15;  

if(callback)  
  callback(&myvars);  /* this function may or may not change the variables */  

if(peg->x == 15)  /* Can I assume that x is "reloaded" ? */  
  do_something();  
else  
  do_something_else();  
信不信由你,这仍然过于简化。很明显,我可以使用volatile,我想我可以这样做来强制重新加载:

if(*(volatile int *)&peg->x == 15)  
这能保证重新加载吗?换句话说,如果(peg->x)知道一个volatile cast已经“重新加载”了变量,那么我以后是否能够简单地编写代码

问题是速度,因为我的函数可以被连续调用数百万次。当然,它比上面要复杂得多。我想知道如果回调信号修改变量,是否有必要或更可取,是否有某种方法来处理。我正在处理结构中的几十个变量,除非必要,否则我不希望它们被“重新加载”

此外,C99标准是否对我的两个伪样本中的任何一个都有洞察,例如,以确保在函数之后“重新加载”变量(我不知道正确的单词)。或者这个问题是特定于编译器和优化级别的?我确实用gcc在-O0和-O3测试了一些其他类似的稀释样本,我没有看到任何一种情况下的差异(在所有情况下,变量都有其适当的值)

谢谢大家



编辑2010年11月24日美国东部时间下午1点:为了说明我所说的“重新加载”的意思,我的意思是,如果编译器在函数调用之前缓存变量(例如在寄存器或其他内存空间中),那么它在函数调用之后仍然会访问相同的缓存变量,而不是(可能)更新的变量。
编译器还可以循环删除循环中不变的变量。例如,如果我有peg->x,而不是每次在循环中访问peg->x,那么即使传递了回调,编译器是否可以确定没有其他访问&peg?如果我有这样的代码:

peg = ⪚  
while(1)  
{  
  if(!peg->x)  
    if(callback)  
      callback(peg);  

  if(!peg->x)  
    peg->x = 20;  

  if(peg == &eg)  
    peg = &eg2;  
  else  
    break;  
}  
那么编译器是否可以像这样优化它,例如:

while(1)  
{  
  if(!peg->x)  
  {  
    if(callback)  
      callback(peg);  

    peg->x = 20;  
  }  

  if(peg == &eg)  
    peg = &eg2;  
  else  
    break;  
}  
或者它可以这样优化它:

{  
  int someregister;  
  peg = ⪚  
  someregister = peg->x;  

  if(!someregister)  
    if(callback)  
      callback(peg);  

  if(!someregister)  
    peg->x = 20;  

  peg = &eg2;  
  someregister = peg->x;  

  if(!someregister)  
    if(callback)  
      callback(peg);  

  if(!someregister)  
    peg->x = 20;  
}  


编辑2010年11月24日美国东部时间下午1:45:这里是另一个例子。回调可以更改指向自身的指针

if(psomestruct->callback)  
{  
  psomestruct->callback(psomestruct); /* this callback could change the pointer to itself */  
  if(psomestruct->callback) /* will the compiler optimize this statement out? */  
    psomestruct->callback(psomestruct);  
}  
没有“重新加载”变量这样的事情。您可以省略
volatile
,您仍然可以得到您想要的

我认为您看到了一个不存在的问题。

没有“重新加载”变量这样的事情。您可以省略
volatile
,您仍然可以得到您想要的


我认为您看到了一个不存在的问题。

编译器不允许跨函数调用“缓存”变量的值,只要您不调用任何未定义的行为,就可以修改它。在这种情况下,未定义行为的特别相关实例包括:

  • 修改声明为常量的变量;及
  • 通过非
    无符号字符
    的不兼容类型的左值修改变量
例如,如果您有:

void modifier(int *a)
{
    *a += 10;
}
那么这将始终有效:

int i = 5;
modifier(&i);
if (i == 15)
    puts("OK");
但是,此可能不

const int i = 5;
modifier((int *)&i);
if (i == 15)
    puts("OK");
这同样适用于更复杂的示例。正如您的示例所示,确定函数是否可以修改给定变量所需的静态分析可能非常复杂。在这种情况下,编译器需要保守,这通常意味着在实践中,一旦获取了变量的地址,就可以围绕该变量进行的优化非常有限


附录:

C标准涵盖了这一点,但它没有采取(徒劳的)方法,试图列出所有可能的优化,并声明是否允许。相反,C标准规定(C99中的§5.1.2.3):

本文中的语义描述 国际标准描述了 抽象机器在计算机中的行为 优化的问题是什么 无关紧要

换句话说,C标准描述了一个以简单方式工作的抽象机器,任何优化都必须确保在C抽象机器上正确运行的任何程序也能在这些优化下正确运行

修改对象(甚至通过函数调用中的深度嵌套指针)是一种副作用。在C抽象机器中,所有副作用都在下一个序列点完成(每个表达式的末尾都有一个序列点,例如在您的示例中调用
callback()
之后)。这也在§5.1.2.3中详细说明。由于C抽象机中的对象一次只有一个值,因此在修改完成后,对该对象的任何后续读取都必须读取新值

因此,不需要任何
忽略\u任何\u变量\u先前\u在\u registers()
-在C抽象机器下,没有在寄存器中缓存变量的概念(或者实际上根本没有“寄存器”)

如果您知道深入存储在嵌套结构中的特定变量不会被函数调用修改,并且您希望允许在该函数调用中缓存该变量,只需创建一个局部变量来显式缓存它-完全按照您的示例使用
someregister
。如果合适,编译器将为
someregister
使用寄存器

为了解决您的新示例,不允许编译器执行您建议的那些优化。如果它对分配给函数指针
->callback
的函数一无所知,则必须假定它可以更改其地址可能与
->callback()可用指针别名的任何变量。在实践中,这往往意味着只有地址未被采用的局部变量才能被认为是安全的。这个