通过指针将值传递给函数。我们要在函数内部创建变量的副本吗?

时间:2019-02-21 07:59:27

标签: c pointers safety-critical

我们有两个简单的功能。

#include <stdio.h>

 /* first approach */
int power1(int *ptr)
{
     return *ptr * *ptr;    
}

/* second approach */
int power2(int *ptr)
{
    int tmp = *ptr;
    return tmp*tmp;    
}

int main()
{
    int val = 5;

    printf("%d\n", power1(&val));
    printf("%d\n", power2(&val));

    return 0;
}

哪个更好? power1快一点,但是我听说power2更安全。我不记得为什么吗?据我记得,有一种情况是power1(第一种方法)出现瓶颈。你能解释一下吗?安全关键系统是否使用第二种方法?

4 个答案:

答案 0 :(得分:5)

没有一个好。您想要这个:

#include <stdio.h>

/* second approach */
int power(int operand)
{
    return operand*operand;    
}

int main(void)
{
    int val = 5;    
    printf("%d\n", power(val));
}

现在讨论您的两种方法:

power2绝对比power1“更安全”。

顺便说一句:

声明main的正确方法是int main(void),并且如果return 0;不包含{ main语句,在main的末尾有一个隐式return

答案 1 :(得分:0)

  

哪个更好?

他们同样是好/坏

  

power1快一点

如果编译时没有进行任何优化,则“是的,power1可能会更快一些”,但是一旦打开编译器优化,它们(对于任何体面的编译器)将相等。

  

但是我听说power2更安全

错了。使用参数列表中的变量与使用局部变量一样安全。

  

安全关键系统是否使用第二种方法?

没有理由这样做。但是,在某些安全关键系统中禁止使用指针。在您的特定情况下,最好直接传递整数而不是传递指针。

与“安全”有关的另一件事是整数溢出。您的代码无法防止整数溢出,并且整数溢出是未定义的行为。因此,对于安全关键型系统而言,这可能是要考虑的事情。

答案 2 :(得分:0)

power1中将进行2次解引用-将进行2次与解引用有关的内存查找。

power2中将取消引用,但只有一次。仅在语句int tmp = *ptr;中。

因此,如果从速度的角度看,power1可能效率不高。

答案 3 :(得分:0)

我希望我知道这里所说的“安全”是什么意思(我看到你的评论是你从一次采访中得到的,而访调员没有解释他的意思)。

函数应该将指针作为参数接收的原因只有四个:

  1. 该函数用于更新参数;
  2. 参数是一个数组表达式,当作为函数参数传递时会自动转换为指针表达式;
  3. 该参数是非常大的struct或类似的聚合类型,创建本地副本被认为过于昂贵;
  4. 该参数是通过malloccallocrealloc创建的。

这些都不应该应用于您发布的代码段。对于他们来说,“最安全”的选择是根本不使用指针。

使用指针的一个“不安全”方面是,您可能打算将输入设为只读,但是由于已将指针传递给您,因此您可以修改输入。对于这些情况,您需要const限定该参数:​​

void foo ( const char *str ) // we cannot modify what str points to
{
  ...
}

使用指针的另一个“不安全”方面是不小心(或故意)更新了指针值本身以访问您不应该访问的内存:

while ( *ptr )
  do_something_with( ptr++ );

您可以通过将 pointer 声明为const来减轻这种情况:

void bar( int * const ptr ) // we cannot update the value in ptr

但这不会阻止您使用[]下标运算符,

while( ptr[i] )
  do_something_with( ptr[i++] );

现在,如果您的面试官考虑到有关中断或易失性的多个线程或某些机器级别的问题,那么也许他有一个要点-如果有些东西可以修改ptr指向外部的东西在执行控制的当前线程中,那么是的,第二种方法在这方面“更安全”(指向的值在计算过程中不会改变)。

但是,如果代码是多线程的,并且ptr可以在不同的线程中进行修改,则应通过互斥量或其他方式同步对其的访问。如果ptr可以在程序的控制范围之外进行更新,则应 声明为volatile

int power1( volatile int *ptr ) { ... }
int power2( volatile int *ptr ) { ... }