关于C语言中按值调用的问题

Question about call by value in C language

提问人:Thilina 提问时间:7/4/2023 最后编辑:chqrlieThilina 更新时间:7/5/2023 访问量:169

问:

当我运行这个程序时,它会输出(第一个代码片段)。10 20

#include <stdio.h>

int x = 10;
int y = 20;

int main()
{
    fun(x, y);
    printf("%d %d", x, y);
}

int fun(int x, int y)
{
    x = 30;
    y = 40;
}

但是当我这样写时:

#include <stdio.h>

int x = 10;
int y = 20;

int main()
{
    fun(x, y);
    printf("%d %d", x, y);
}

int fun(int n1, int n2)    // I include other variables except from global variable.
{
    x = 30;
    y = 40;
}

它输出 .我想知道为什么即使不使用指针,它也只修改全局变量的第二个代码片段?为什么第一个不是?如果有人建议我参考这个理论部分,我会很充实。30 40

我想正确地学习这些理论部分,因为我是编程新手。

c 函数 作用域 全局变量 按值传递

评论

0赞 463035818_is_not_an_ai 7/4/2023
阴影。很高兴知道,但最好不要使用全局变量,也不要对不同的变量使用相同的名称
1赞 Some programmer dude 7/4/2023
在第一个程序中,局部参数变量名称隐藏了同名的全局变量。当您分配给例如 分配给局部参数变量。在第二个程序中,当您赋值给全局变量时,您赋值给全局变量。在这两个程序中,您都打印了全局变量的值。xx
1赞 n. m. could be an AI 7/4/2023
您已经发现了全局变量是一大禁忌的几个原因之一。
0赞 12431234123412341234123 7/4/2023
“即使不使用指针?”我在您的代码中没有看到任何指针。

答:

1赞 Brigapes 7/4/2023 #1

在第一个示例中,您正在修改局部变量 xy,这些变量被创建为函数的参数fun

在第二个示例中,您将不理会这些参数,而是修改全局变量和xy

如果要修改第二个示例中的参数,则将

int fun(int n1, int n2)
{
    n1 = 30;
    n2 = 40;
}
2赞 Vlad from Moscow 7/4/2023 #2

首先,请注意,该函数应在 的主体中使用之前声明。funmain

在第一个函数中

int fun(int x, int y)
{
x = 30;
y = 40;
}

函数参数已更改,这些参数是函数的局部变量。参数由文件范围全局变量的值初始化,但全局变量本身未更改。xyxy

在函数中,全局变量 和 被同名的参数隐藏。因此,该函数处理的是参数而不是全局变量。xy

您可以使用以下技巧来更改函数中的全局变量

int fun(int x, int y)
{
    x = 30;
    y = 40;
    
    {
        extern int x;
        extern int y;

        x = 30;
        y = 40;
    }
}

在本例中,函数内部块中的声明

extern int x;
extern int y;

指具有外部链接的全局变量,全局变量已更改。

在第二个函数中,不使用参数。

int fun(int n1, int n2)    // I include other variables except from global variable.
{
x = 30;
y = 40;
}

在这种情况下,编译器会发出有关未使用参数的消息。

在函数中,直接更改了全局变量。文件作用域变量,在函数主体中可见且可访问。xy

您可以按以下方式考虑第二个函数的主体

int n1 = x;
int n2 = y;

x = 30;
y = 40;
-3赞 ryodes 7/4/2023 #3

在第一个代码片段中,fun 函数按值接收参数 x 和 y,这意味着该函数创建这些变量的本地副本。对这些本地副本所做的任何修改都不会影响在 main 函数外部定义的原始全局变量 x 和 y。因此,当你调用 fun 后在 main 函数中打印 x 和 y 时,它们的值保持不变,导致打印“10 20”。

在第二个代码片段中,您使用的变量名称 x 和 y 与 fun 函数中的全局变量相同。这会产生歧义,因为函数参数 n1 和 n2 与全局变量同名。在这种情况下,当您在 fun 函数中为 x 和 y 赋值时,它会修改全局变量本身,因为它们具有相同的名称。这称为重影,其中局部变量对具有相同名称的全局变量进行重影。因此,当您在调用 fun 后在 main 函数中打印 x 和 y 时,您会看到修改后的值“30 40”,因为全局变量是在函数中直接修改的。

为了避免混淆和潜在的问题,通常建议对函数中的局部变量使用不同的名称,以清楚地将它们与全局变量区分开来。这有助于提高代码的可读性,并避免对全局变量进行无意的修改。

要进一步了解变量范围和阴影,您可以参考 C 编程语言教科书或详细介绍这些主题的在线资源。一些推荐的资源包括 Brian Kernighan 和 Dennis Ritchie 的“The C Programming Language”,或者 tutorialspoint.com 或 cplusplus.com 等网站。

评论

0赞 ad absurdum 7/4/2023
“函数参数 n1 和 n2 与全局变量同名” -- 第二个示例中的函数参数具有名称,并且不隐藏任何全局变量。阴影在第一个示例中,而不是在第二个示例中。第一个示例创建隐藏全局变量的局部变量,并仅修改局部变量;第二个示例直接修改全局变量,甚至不使用传递给函数的参数。n1n2
1赞 ryodes 7/5/2023
y 你的权利,thks 进行更正
1赞 ad absurdum 7/5/2023
由于您的答案是公认的答案,因此您应该努力通过编辑使其正确。目前这里还有另外三个正确答案,没有一个被OP接受。
1赞 chqrlie 7/4/2023 #4

如您所见,函数参数在 C 中是按值传递的,因此在调用时,您将全局变量的当前值传递给函数。fun(x, y)xyfun

在第一个示例中,函数是用名称和函数参数定义的。在函数的主体中,当您引用 和 时,参数定义在作用域内,并且正在修改,并且仅修改函数的局部参数值。全局变量不受影响。当一个局部定义从外部范围隐藏另一个定义时,我们说外部定义被内部定义所掩盖。这有点令人困惑且容易出错:传递给 gccclang 编译器将在这种情况下启用警告。funxyxyxy-Wall -Wextra

在第二个示例中,函数参数定义为 和 ,因此函数体中的代码确实修改了全局变量 和 ,并且程序确实输出了修改后的值。n1n2xy

另请注意以下备注:

  • 没有参数的原型是 C 语言;mainint main(void)
  • 该函数应在使用前声明或定义。编译器宽容地接受此调用,并从函数参数推断原型,这与后面的函数定义一致。这种ANSI之前的行为是为了与旧代码兼容,例如原始K&R书中的示例,但现代C程序不应该依赖它,额外的警告级别会抱怨它。funint fun(int, int)
  • 该函数应返回成功,尽管 C99 已将此作为没有语句的默认行为。main0return
  • 您应该输出尾随换行符以确保在终端中正确显示。如果没有此换行符,shell 提示符可能会粘附在最后一个字符输出上,从而导致进一步的混淆,尤其是当提示符为 or 时。%$
  • 该函数被定义为返回一个 ,但该函数在没有语句的情况下退出。您应该定义函数以避免未定义的行为。funintreturnvoid

这是修改后的版本:

#include <stdio.h>

int x = 10;
int y = 20;

void fun1(int x, int y) {
    x = 30;
    y = 40;
}

void fun2(int n1, int n2) {
    x = 30;
    y = 40;
}

int main(void) {
    fun1(x, y);
    printf("%d %d\n", x, y); // outputs 10 20
    fun2(x, y);
    printf("%d %d\n", x, y); // outputs 30 40
    return 0;
}