1

在“理解 C 中的指针”一书中,在解释了参数后,有一些已解决的问题。第 22 页,问题 N.5 我将附上代码和解释。在那之后,会有我的问题。

#include <stdio.h>

int main()
{
    int *c;
    c = check(10, 20);
    printf("c = %p\n",c);
    return 0;
}

int * check(int i, int j)
{
    int *p, *q;
    p = &i;
    q = &j;
    if(i >= 45)
    {
        return p;
    }
    else
    {
        return q;
    }
}

输出:错误消息:main 中的非可移植指针分配

说明 错误的原因很简单。传递给 check() 的整数被收集在 i 和 j 中,然后将它们的地址分配给 p 和 q。然后在下一条语句中,根据 45 测试 i 的值,并返回存储在 p 中的地址或存储在 q 中的地址。看起来这个地址会在 main() 中的 c 中收集,然后他会打印出来。这就是错误所在。函数 check( ) 不能返回整数指针。它所能返回的只是一个普通的整数。因此仅将 c 声明为整数指针是不够的。我们必须在程序中进行以下修改才能使其正常工作

#include <stdio.h>

int * check(int, int);

int main()
{
    int *c;
    c = check(10, 20);
    printf("c = %p\n",c);
    return 0;
}
int *check(int i, int j)
{
    ......
    ......
}

在我看来,这没有意义。作者为了在第一段代码上故意出错,尝试在 main 中使用一个指向未分配内存区域的指针。但是他“试图”解决问题的方式根本不正确,他没有改变任何东西!相反,他应该在 check() 函数中分配一些内存区域。我对么?

4

7 回答 7

7

这实际上是关于尚未声明的函数的默认返回值。在您的第一个示例中,当编译器看到该行时c = check(10, 20);,它还不知道该函数将返回什么。该标准说编译器应该假设返回是一个int. 这就是全部内容:an 的大小int可能与 a int *(指针)的大小不同。为了使编译器始终为此发出正确的机器代码,它必须知道check返回一个指针,这就是需要声明它的原因。这就是第二个示例所做的:它告诉编译器“将有一个名为 'check' 的函数,它看起来像这样”

除此之外,这个例子真的很糟糕。的参数check被压入堆栈,然后函数返回指向这些堆栈位置的指针。但不能保证这些堆栈位置在函数退出后不再有效。这是未定义的行为,这意味着允许编译器做任何它喜欢的事情,甚至让你的计算机爆炸。这个特定示例实际上应该可以工作,因为它只打印指针(即它指向的地址),但实际上并没有取消引用它(即从指针指向的地址读取)。在大多数机器/编译器上,即使取消引用也应该“正确”工作,但您可能不依赖它。

于 2013-04-23T10:13:13.463 回答
2

如果在第一次使用函数之前没有函数原型,编译器会假定函数返回int.

因此编译器假定您将int变量分配给int*. int的大小int*可能会有所不同,具体取决于平台和编译器。

例如,有些嵌入式系统对无符号整数和指针具有相同的类型,因此在两种情况下您都会在代码中看到uint32_t.

于 2013-04-23T10:12:58.050 回答
2

你是对的,添加原型只能修复编译器警告/错误,但不能解决check函数将返回指向局部变量的指针的问题。

但是,在您尝试取消引用返回的指针之前,它不是未定义的行为。打印出来就好了。

于 2013-04-23T10:13:34.003 回答
1

有两个问题。

  1. 局部变量的返回地址。

    这个 scnario 被称为http://en.wikipedia.org/wiki/Dangling_pointer。当Check()执行完成时,分配给局部变量的内存可能会分配给其他函数。因此访问该内存会导致未定义的结果。

  2. 需要前向声明。默认情况下,返回类型将被视为int,但这里int*使用。所以添加int * check(int, int);将解决Error message: Non portable pointer assignment in main

于 2013-04-23T10:13:45.107 回答
1

我认为代码的主要“要点”是告诉您未声明的函数的返回类型默认为int. 这是真的。

然而,代码存在缺陷,因为该check函数返回一个指向局部变量的指针(参数ij,取决于i的值),这是一种未定义的行为(幸运的是,该指针永远不会被取消引用)。

于 2013-04-23T10:16:06.883 回答
1

您正在返回在函数堆栈中分配的局部变量的地址check()。当check()函数结束时,函数堆栈,您不能将地址返回给不再存在的变量!

您可以将代码更改为:

#include <stdio.h>
int * check(int* i, int* j);
int main()
{
    int *c;
    int n1,n2;
    n1=10;
    n2=20;
    c = check(&n1, &n2);
    printf("c = %p\n",c);
    return 0;
}

int * check(int* i, int* j)
{
    int *p, *q;
    p = i;
    q = j;
    if((*i) >= 45)
    {
        return p;
    }

    return q;

}
于 2013-04-23T10:18:27.810 回答
0

您正在返回局部变量的地址。取消引用这些是undefinedC 中的行为。pq都是指向函数的局部变量的指针check()。在函数check()将控制权返回到main()之后,然后pq指向未定义的值。此外,请对您的代码进行以下更正:

printf("c = %p\n",(void*)c);

嘿,卡特彼勒,关于那位作者的书,我已经警告过你了,不是吗?:-)

编辑您忘记了check()之前的函数声明main()。在我的编译器中,它总是抛出警告消息。在 main() 函数之前添加以下内容:

int *check(int,int);
于 2013-04-23T10:12:48.480 回答