为什么C中的绝对值函数不接受const输入?


23

在C语言中,绝对值函数(接受浮点数)的原型为

 float fabsf( float );

为什么这个原型不接受一个常量值,像这样:

 float fabsf( float const );

fabsf不会更改输入的值,对吗?

如果我有一个接受输入并调用fabsf的函数,我是否被迫避免将输入指定为const?

在这种情况下,处理const正确性的适当方法是什么?


26
const这里是多余的,您的意念正在发生什么?
MM

1
@MM我希望,如果我尝试更改函数内部输入的值,它将导致编译时错误。这不正确吗?
user24205 '19

16
由于函数内部的参数是本地副本,因此添加const完全没有意义。
隆丁

1
fabsf不会更改输入的值,是吗? ”您怎么知道?参数按值传递。
David Schwartz

以下代码是合法的C:float const x = -1.0; float y = fabsf(x);在我看来,它fabsf 确实接受const输入。无法说“您可以通过float值传递我,但不能传递值const float”。(正如我们在答案中看到的,C没有提供要求函数输入为a的方式float const。)
David K

Answers:


14

编辑

如MM所评论,原型中的参数const被忽略。原始答案的编辑源(如下所示)显示:

float correct(float const value);

float erroneous(float const value);

float changer(float value);

float correct(float value) {
  return -value;
}

float erroneous(float value) {
  value = -value;
  return value;
}

float changer(float value) {
    value = -value;
    return value;
}

没有错误信息。

无论如何,我会保留原件,希望对您有所帮助。


原版的

const在参数使得这个参数只读在函数内部。

例如:

float correct(float const value) {
  return -value;
}

float erroneous(float const value) {
  value = -value;
  return value;
}

float changer(float value) {
  value = -value;
  return value;
}

没有错误消息,该源将无法编译。

该函数correct()将读取给定值,更改其符号,然后返回取反的值。

该函数erroneous()看起来实际上是一样的,只是对参数进行了赋值。但是由于参数是const不允许的。

接下来,该函数changer()将与以前一样工作,但不会出错。

让我们看一下呼叫站点:

float f = 3.14159;
float g = correct(f); // or erroneous(f) or changer(f)

变量f作为参数给出将被复制到参数value。即使changer()被调用,它也永远不会改变。

您可能希望将参数视为某种局部变量。实际上,它们在生成的机器代码中大多是这样处理的。


那么,为什么const有时看到?如果将指针定义为参数,则会看到它。

当您不想更改所指向的值时,需要添加const; 但是要放在正确的位置!

void effective(int const * pointer);

void futile(int * const pointer);

void possible_but_overly_restricted(int const * const pointer);

问题是关于原型的,但是原型float fabsf( float const );与函数的实现无关(不必重复const),实际上const,原型中的功能被完全忽略了
MM

2
const可以不进入原型就进入函数定义吗?
user24205 '19

3
@ user24205是的,它可以
Daniel Jour

33

C使用按值传递。函数参数的值是您提供的参数的副本。

可以同时复制const和非const浮点数,结果是非const浮点数。

它类似于分配:

const float f = 5.5f;
float g = f;   // OK

实际上,该语言指定表达式的值永远不能为const,即,从变量中读取值时,const即使该变量为,该值也不会。


8

因为C语言使用按值传递语义,所以您传递给它的任何参数(尽管可以在内部进行修改)都不会直接影响您传递的值。

这意味着从来电者的角度说,float fabsf( float );并且float fabsf( const float );是相同的。因此,没有必要设置参数const

如果您传入的参数是指针,那么使用它的确有意义const,例如:

void print_string(char *str)

尽管顾名思义,该函数仍可以取消对给定指针的引用并修改其指向的内容,即str[0] = 'x'导致调用函数可以查看的更改。如果此函数的定义如下:

void print_string(const char *str)

确保调用者函数无法对str指向的内容进行任何修改。


“确保调用者函数无法执行任何修改……”不是正确的。该函数知道数据的地址,因此可以使用例如修改它((char*)str)[0] = 'f'。因此const ... *,参数列表中的仅是“意图声明”。
oromoiluig

5

要添加语言律师观点:

为了使两种功能类型兼容,两者都应指定兼容的返回类型。此外,参数类型列表(如果同时存在)应在参数数量和省略号终止符的使用上达成共识;相应的参数应该具有兼容的类型。[..]在确定类型兼容性和复合类型时,[..] 使用限定类型声明的每个参数均被视为具有其声明类型的非限定版本

N1570 6.7.6.3/15

这意味着这两个是兼容的:

void foo(int const);
void foo(int);

因此,您可以编写带或不带原型const(这意味着没有任何意义;键入/读取更少),并且可以添加const函数定义(如果要避免意外修改函数内部的(复制的-按值调用!)参数)。身体。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.