在C语言中,为什么变量需要在使用之前声明,而函数不需要?

huangapple go评论112阅读模式
英文:

in C, why do variables need to be declared before use but functions dont?

问题

我正在阅读《C程序设计语言》(K&R),开始思考,为什么变量在使用之前需要声明,但函数不需要呢?

关于变量的部分:

> 在C中,所有变量在使用之前必须被声明,通常是在函数的开头,在任何可执行语句之前。

关于函数的部分:

> 声明
> int power(int base, int n);
> 就在 main 之前表示 power 是一个函数,它期望两个整数参数并返回一个整数。这个声明被称为函数原型,必须与函数的定义和使用相符。如果函数的定义或任何使用与其原型不一致,就会出现错误。

这是否与为变量分配内存有关?例如,编译器在使用变量时需要知道变量的大小,但它不能等到找到声明才知道吗?

英文:

I'm reading K&R and started wondering, why do variables need to be declared before they are used,
but functions dont?

> In C, all variables must be declared before they are used, usually at the beginning of the
> function before any executable statements.

Regarding functions it says:

> The declaration
> int power(int base, int n);
> just before main says that power is a function that expects two int arguments and returns an
> int. This declaration, which is called a function prototype, has to agree with the definition
> and uses of power. It is an error if the definition of a function or any uses of it do not agree
> with its prototype

Does it have something to do with allocating memory for variables?
e.g the compiler needs to know the variable size when it uses it, but couldnt it wait until it found the declaration?

答案1

得分: 3

> I'm reading K&R and started wondering, why do variables need to be declared before they are used, but functions dont?

我在阅读K&R,开始想知道,为什么变量在使用之前需要声明,但函数不需要呢?

The second (and last) edition of K&R is way out of date with respect to the current specifications of ISO C. The language it describes is aligned with C90. These days it has more historical value than technical value.

K&R的第二版(也是最后一版)在当前ISO C规范方面已经过时。它描述的语言与C90一致。如今,它更具历史价值而不是技术价值。

I don't see how the quotations you present support the idea that functions do not need to be declared before use, although that was in fact true in C90. It is not true in any of the versions of the C language since, however: C99, C11, C17, and (soon) C23 all require functions to be declared before use. Modern compilers may accept implicitly-declared functions for backward compatibility reasons, however.

我看不出你引用的文字支持函数在使用之前不需要声明的观点,尽管事实上在C90中确实如此。然而,在C语言的任何版本中都不是如此:C99、C11、C17和(很快)C23都要求在使用之前声明函数。然而,出于向后兼容的原因,现代编译器可能会接受隐式声明的函数。

Nevertheless,

尽管如此,

Does it have something to do with allocating memory for variables?

是否与为变量分配内存有关?

Not really. A compiler could support automatic variables without advance declaration -- Fortran compilers did, for example, even before C was a twinkle in Dennis Ritchie's eye.

不完全是这样。编译器可以支持自动变量而不需要提前声明,例如,Fortran编译器在C还未出现之前就支持这一功能。

My best guess at the practical reason is that it makes C compilers easier to write and simpler (and smaller, which was important at the time). It may also be drawn in part from C's predecessor, B, though I'm having trouble determining whether B actually required pre-declaration (it did allow that).

我对实际原因的最佳猜测是这样做使C编译器更容易编写、更简单(以及更小,这在当时很重要)。这也可能在某种程度上受到C的前身B的影响,尽管我很难确定B是否实际上要求提前声明(它允许这样做)。

With functions, on the other hand, a linking step was (and is) required whether functions are declared before use or not, so requiring declaration before use does not make compilers any easier, simpler, or smaller.

然而,对于函数来说,无论函数在使用之前是否已声明,都需要进行链接步骤,因此在使用之前要求声明并不会使编译器更加容易、简单或更小。

e.g the compiler needs to know the variable size when it uses it, but couldnt it wait until it found the declaration?

例如,编译器在使用变量时需要知道其大小,但它不能等到找到声明吗?

Nope. Implicit data typing was a known thing at the time of C's invention, and C had it. In early C, variable declarations did not need to specify a data type. It was sufficient to provide a storage-class specifier and name, in which case the type defaulted to int. And again, Fortran did not require variable declaration before use, so we can conclude that it would have been feasible for C not to require it, either.

不是的。在C语言诞生时,隐式数据类型是已知的事情,而C语言也支持它。在早期的C语言中,变量声明不需要指定数据类型。只需提供存储类别说明符和名称即可,此时类型默认为int。而且,正如前面提到的,Fortran在使用变量之前不需要声明,因此我们可以得出结论,C语言也可以不要求这一点。

英文:

> I'm reading K&R and started wondering, why do variables need to be declared before they are used, but functions dont?

The second (and last) edition of K&R is way out of date with respect to the current specifications of ISO C. The language it describes is aligned with C90. These days it has more historical value than technical value.

I don't see how the quotations you present support the idea that functions do not need to be declared before use, although that was in fact true in C90. It is not true in any of the versions of the C language since, however: C99, C11, C17, and (soon) C23 all require functions to be declared before use. Modern compilers may accept implicitly-declared functions for backward compatibility reasons, however.

Nevertheless,

> Does it have something to do with allocating memory for variables?

Not really. A compiler could support automatic variables without advance declaration -- Fortran compilers did, for example, even before C was a twinkle in Dennis Ritchie's eye.

My best guess at the practical reason is that it makes C compilers easier to write and simpler (and smaller, which was important at the time). It may also be drawn in part from C's predecessor, B, though I'm having trouble determining whether B actually required pre-declaration (it did allow that).

With functions, on the other hand, a linking step was (and is) required whether functions are declared before use or not, so requiring declaration before use does not make compilers any easier, simpler, or smaller.

> e.g the compiler needs to know the variable size when it uses it, but couldnt it wait until it found the declaration?

Nope. Implicit data typing was a known thing at the time of C's invention, and C had it. In early C, variable declarations did not need to specify a data type. It was sufficient to provide a storage-class specifier and name, in which case the type defaulted to int. And again, Fortran did not require variable declaration before use, so we can conclude that it would have been feasible for C not to require it, either.

答案2

得分: 0

以下是您提供的内容的翻译部分:

#include <stdio.h>

extern int x;
extern double d;

int main(void)
{
    printf("%d\n", x);
    printf("%f\n", d);
}

int x = 5;
double d = 3.5;

在C中,变量和函数声明相同(根据定义,函数是extern的)。

在C中,声明和定义之间存在差异。

甚至有隐式数据类型:

extern x;
extern d;

int main(void)
{
    printf("%d\n", x);
    printf("%d\n", d);
}

x = 5;
d = 3.5;
英文:

You can exactly the same with the variables (functions are extern by definition).

#include <stdio.h>

extern int x;
extern double d;

int main(void)
{
    printf("%d\n", x);
    printf("%f\n", d);
}

int x = 5;
double d = 3.5;

In C there is a difference between declaration and definition.

There is even implicit data typing:

extern x;
extern d;

int main(void)
{
    printf("%d\n", x);
    printf("%d\n", d);
}

x = 5;
d = 3.5;

https://godbolt.org/z/YhbqT4jrh

答案3

得分: 0

我明白了,这是你要的翻译内容,只翻译其中的代码部分:

I'm reading K&R and started wondering, why do variables need to be declared before they are used, but functions dont?

In C, all variables must be declared before they are used, usually at the beginning of the function before any executable statements.

Well, In C everything must be declared before use. The problem is that, when you introduce a new function to the compiler (a function that has no declaration) C assumes a default declaration for function my_function like:

int my_function();

This is an unknown parameter list, and an int return value.

This is very very trouble making, as it's most probable that your function doesn't have that prototype. Indeed, this is a legacy of old C language definition (what is commonly called K&R C style, and indeed, the book you are reading describes a very old version of the language, but still accepted by compilers)

It is there due to the necessity of compiling very old programs still.

Let's illustrate it with an example:

Assume you have a program that uses the cos(3) (the mathematical trigonometric function cosine), and you use it as this:

#include <stdio.h>
/* #include <math.h>  // we deliverately exclude the definition of double sin(double); */
int main()
{
    printf("The cosine of 1 radian is: %g\n", cos(1));
}

如果你有其他问题或需要进一步的翻译,请告诉我。

英文:

> I'm reading K&R and started wondering, why do variables need to be declared before they are used, but functions dont?
>>In C, all variables must be declared before they are used, usually at the beginning of the function before any executable statements.

Well, In C everything must be declared before use. The problem is that, when you introduce a new function to the compiler (a function that has no declaration) C assumes a default declaration for function my_function like:

int my_function();

This is an unknown parameter list, and an int return value.

This is very very trouble making, as it's most probable that your function doesn't have that prototype. Indeed, this is a legacy of old C language definition (what is commonly called K&R C style, and indeed, the book you are reading describes a very old version of the language, but still accepted by compilers)

It is there due to the necessity of compiling very old programs still.

Let's illustrate it with an example:

Assume you have a program that uses the cos(3) (the mathematical trigonometric function cosine), and you use it as this:

#include &lt;stdio.h&gt;
/* #include &lt;math.h&gt;  // we deliverately exclude the definition of double sin(double); */
int main()
{
    printf(&quot;The cosine of 1 radian is: %g\n&quot;, cos(1));
}

The compiler will assume that you have used a function cos(), that returns an int (this is a mistake the programmer should be aware of) and receives an undetermined number of parameters, so the compiler will not check the parameters you pass to the compiler and pass them as you do, without any automatic conversion (from integer to floating point) because the function call defines an undetermined parameter list), so it passes the integer as is.

Let's see what happens (need to say that the compiler will notice as a warning, as cos() is an intrinsic function ---the cosine function is a known function to the compiler, that it treates as special, not what happens with the others (and not all compilers do what is shown below)

GCC on Linux

$ make pru$$
cc     pru13204.c   -o pru13204
pru13204.c: In function ‘main’:
pru13204.c:5:43: warning: implicit declaration of function ‘cos’ [-Wimplicit-function-declaration]
    5 |         printf(&quot;The cosine of 1 is %g\n&quot;, cos(1));
      |                                           ^~~
pru13204.c:2:1: note: include ‘&lt;math.h&gt;’ or provide a declaration of ‘cos’
    1 | #include &lt;stdio.h&gt;
  +++ |+#include &lt;math.h&gt;
    2 | 
pru13204.c:5:43: warning: incompatible implicit declaration of built-in function ‘cos’ [-Wbuiltin-declaration-mismatch]
    5 |         printf(&quot;The cosine of 1 is %g\n&quot;, cos(1));
      |                                           ^~~
pru13204.c:5:43: note: include ‘&lt;math.h&gt;’ or provide a declaration of ‘cos’
$ pru$$
The cosine of 1 is 0.540302

Let's say we have included &lt;math.h&gt; (which has a declaration of cos() function) Uncomment the second program line and see what happens:

$ make pru$$
cc     pru13204.c   -o pru13204
$ pru$$
The cosine of 1 is 0.540302
$

This time, no warning has occured, and the program behaves correctly, giving the correct answer. It seems that the problem is not such a problem, as we have done things bad with no apparent difference. That assumption is not correct, continue reading.

We have been lucky, as the compiler has recognized the cos() function as one of it's favourites, and has repaired the problem on the fly, providing the correct declaration for it, instead of the default behaviour expected on the compiler. With the sample on CLANG we'll see it clearly.

This time, we are showing a complete example, run on CLANG on FreeBSD, and showing that CLANG (which is different than GCC but tries to emulate all GCC behaviour, including even GCC extensions to the C language)

The error with clang 64 bit is more significative, in the sense that it says that it has implicitly declared the cos library function as having prototype double cos(double), which is the correct prototype, but this should not happen, had we used a non-intrinsic function.

$ make pru$$
cc -O2 -pipe  pru26408.c  -o pru26408
pru26408.c:7:33: warning: implicitly declaring library function &#39;cos&#39; with type &#39;double (double)&#39; [-Wimplicit-function-declaration]
        printf(&quot;Cos of %d == %g\n&quot;, i, cos(i));
                                       ^
pru26408.c:7:33: note: include the header &lt;math.h&gt; or explicitly provide a declaration for &#39;cos&#39;
1 warning generated.

I have prepared another (it will be the last, I swear :)) example, to show that, on a non-intrinsic function (like the double Cos(double x) implementation I provide in the below example, the behaviour is clearly different:

Cos.h

This file will not be included in main.c so we don't get the proper
definition of it.

#ifndef _COS_H /* protection against double inclussion */
#define _COS_H

double Cos(double x);

#endif /* _COS_H */

Cos.c

This file makes a dumb implementation of cosine function, by calling the actual one, but this time, Cos() will not be the intrinsic function the compiler knows about.

#include &lt;math.h&gt;
/* dummy implementation of the function cosine, based on the actual
 * function double cos(double x) */
double Cos(double x)
{
	return cos(x); /* just call the right function and return the proper value */
}

main.c

#include &lt;stdio.h&gt;
/* again, don&#39;t #include &lt;math.h&gt; or &quot;Cos.h&quot; (which will be shown below)
 * is provided to show the undefined behaviour in action */
int
main()
{
	int i = 1;
	printf(&quot;Cos of %d == %g\n&quot;, i, Cos(i));
}

Makefile

targets = test_Cos
toclean = $(targets)

test_Cos_deps =
test_Cos_objs = main.c Cos.c
test_Cos_ldfl =
test_Cos_libs = -lm
toclean += $(test_Cos_objs)

all: $(targets)
clean:
    rm -f $(toclean)

test_Cos: $(test_Cos_deps) $(test_Cos_objs)
	$(CC) $(LDFLAGS) $($@_ldfl) -o $@ $($@_objs) $($@_libs) $(LIBS)

This compilation will generate a different warning (and this time it will not repair the code, as in this case the function is not an intrinsic one, so the compiler doesn't know how to do the repairment), and declares it as int Cos(), instead of double Cos(double):

$ make
cc  -O2 -pipe -c main.c -o main.o
main.c:8:33: warning: implicit declaration of function &#39;Cos&#39; is invalid in C99 [-Wimplicit-function-declaration]
        printf(&quot;Cos of %d == %g\n&quot;, i, Cos(i));
                                       ^
main.c:8:33: warning: format specifies type &#39;double&#39; but the argument has type &#39;int&#39; [-Wformat]
        printf(&quot;Cos of %d == %g\n&quot;, i, Cos(i));
                             ~~        ^~~~~~
                             %d
2 warnings generated.

You see, it treates it as a function accepting anything, and returning int (the warning checks that the format specifier corresponds to the type of the parameter passed, because printf() is also specified as an undetermined parameter list function.

Now, when we run it, we get:

freebsd@rpi-xterm:/tmp $ ./test_Cos 
Cos of 1 == 1

which is clearly incorrect.

If we now put the missing line in main.c:

#include &quot;Cos.h&quot;

to provide a correct declaration of the function, we get, on compiling:

freebsd@rpi-xterm:/tmp $ make
cc  -O2 -pipe -c main.c -o main.o
cc   -o test_Cos main.o Cos.o -lm 

(no errors, no warnings) and later, on running:

freebsd@rpi-xterm:/tmp $ ./test_Cos
Cos of 1 == 0.540302

(the correct result)

Now the compiler knows beforehand that the Cos() function returns a double (this eliminates de warning on the printf() format correspondence between the format specifier and the actual parameter passed ---which now is a double---, and the int i parameter is converted to a double before being passed to Cos().

huangapple
  • 本文由 发表于 2023年3月15日 21:32:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75745429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定