【问题标题】:About Use Of Storage Classes in C关于 C 中存储类的使用
【发布时间】:2013-07-23 11:36:30
【问题描述】:

如果函数int *check(register int,register int);的参数的存储类声明为其他存储类,则以下代码将显示错误。

我在 Code::Blocks 10.05 IDE 上使用 GNU GCC 编译器 编译了这段代码。错误背后的原因是什么?它是编译器特定错误还是一般错误? 代码部分从这里开始:

int *check(register int, register int);

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

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

【问题讨论】:

  • 你得到什么错误?这段代码应该做什么?为什么要使用register 声明?
  • 您的代码在 Ubuntu 13.04 上的 GCC 4.7.3 中编译并带有一些警告,并按预期输出 20
  • @Earth Engine,@interjay, 请再次查看问题...它指定代码在将参数的存储类声明为静态或自动或外部的情况下显示错误前面提到的编译器版本...不是寄存器存储类。
  • 该错误是否仅出现在您的 IDE 中?
  • 我已经在 Windows 8 上使用 Code::Block Nov 11 2012 进行了尝试,但我看不到您的错误。它编译并得到结果20

标签: c


【解决方案1】:
int *check(register int i,register int j)
{   
    int *p=i;
    int *q=j;

pqij 的类型不匹配。也许你想要的是:

int *check(int i, int j)
{   
    int *p=&i;
    int *q=&j;

更正:请注意register 不能与& 一起使用。此外,关键字register 几乎没有什么用处,因为编译器通常会忽略它并自己进行优化。

【讨论】:

  • 存储说明符是否改变类型?有趣的。这是为什么呢?
  • @sasha.sochka 没有。但是指针与它所指向的不同。
  • 这将返回一个指向局部变量的指针。而且我相信您不能获取register 变量的地址。
  • @interjay 谢谢你,我没注意到,修复了。
  • 我相信他要检查第一个参数是否为有效指针,如果是则返回指针值,否则返回第二个作为指针值。
【解决方案2】:
int *check(register int i,register int j)
{
int *p=i;
int *q=j;
if(i >= 45)
return (p);
else
return (q);
}

虽然在参数声明中允许使用 register 存储类说明符,但参数 ij 的类型为 int,而 pq 的类型为 int *。这使得pq 的声明无效。

您不能只将其更改为:

int *p=&i;
int *q=&j;

因为& 运算符不允许您拥有register 存储类的操作数。

您也不能将参数声明从register int iregister int j 更改为int iint j,然后返回ij 对象的地址,因为它们的生命周期在退出时结束函数。

在您的情况下,您不应该使用指针:使用 int 参数和 int 返回值。

【讨论】:

  • :-请仔细阅读问题。此代码不会产生错误,但任何代替 Register 的存储类声明(如 auto、static 或 extern )都会产生错误,并且问题中已经提到了这一事实确切地说。感谢您的努力...希望仔细研究这个问题。
  • @ChandanKumar 我仔细阅读了您的程序,建议您仔细阅读我的答案。您的编译器接受您的程序这一事实并不意味着不是一个错误的程序。首先,编译器会因为 int *int 之间的键入错误而拒绝您的程序,其次它会调用未定义的行为,因为您正试图访问一个生命周期已结束的对象。
  • 能否解释一下为什么这个程序没有使用任何其他关键字,如 static 或 auto 来代替此处的 register?
  • @ChandanKumar C 在 C99 中说,6.7.5.3p2 “唯一应该出现在参数声明中的存储类说明符是寄存器。”
  • 这就是我想通过这段代码知道的......非常感谢。它符合我的目的。
【解决方案3】:

第一个类型不匹配:

int *p=i;
int *q=j;

第二旁注 & 未应用/对寄存器变量有效。

发件人:address of register variable in C and C++

在 C 中,您不能使用 register 存储来获取变量的地址。参照。 C11 6.7.1/6:

具有存储类说明符register 的对象的标识符声明 建议尽可能快地访问对象。这种程度 建议是否有效由实现定义。

第三个: 本地对象的返回地址是未定义的行为(函数的参数计入本地变量)。在函数返回之前,不要返回生命存在的地址。

建议:

要返回地址,您需要对其进行动态分配和返回地址。例如:

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

注意返回的地址不是ij,而是动态分配内存的地址。不要忘记在main() 中致电free(a)

【讨论】:

  • @GrijeshChauhan 是的。必须在main 函数上完成。
  • @EarthEngine 我指出并纠正了。不仅是主要的,因为我们没有返回两者的地址,因此必须在函数本身中释放。谢谢!一年
  • @GrijeshChauhan 关键是他的奇怪代码被编译了!查看我的回答或我对他的帖子的评论。
  • @EarthEngine 你在这里!
【解决方案4】:

在我的 Ubuntu 13.04 上安装 GCC 4.7.3 的机器上,输出是

$gcc test.c
test.c: In function ‘main’:
test.c:7:3: warning: incompatible implicit declaration of built-in function ‘printf’ [enabled by default]
test.c:7:3: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat]
test.c: In function ‘check’:
test.c:13:12: warning: initialization makes pointer from integer without a cast [enabled by default]
test.c:14:12: warning: initialization makes pointer from integer without a cast [enabled by default]
$./a.out
20

它接受带有大量警告的程序。而且没有一个词说“存储类”。所以我想知道你使用的是什么版本的 GCC?

前两个警告可以通过#include <stdio.h> 修复,并将printf 函数调用中的%d 更改为%p。让我们暂时忽略这些并关注其余两个。取决于你想做什么,你可以有不同的选择来消除它们。

如果你想将ij的地址作为基于堆栈的变量返回(这很不寻常,因为返回给调用者后无效),你可以这样做

int *check( int i, int j)
{   
    int *p = &i;
    int *q = &j;
    ...

您无法获取寄存器变量的地址,因此您必须删除它们。在这种情况下,使用您的main 函数,您的程序将在我的机器上打印类似0x7fffc83021f8 的内容。那是指向变量j 的指针值,尽管在我们打印它时它是无效的,只要你不尝试取消引用它就可以了。

如果这不是你想要的,你可能想强制整数 ij 表示一个指针,那么你需要这样做

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

注意在这种情况下使用 register 关键字是可以的,尽管它的效果可能非常有限。此外,当您在某些机器(尤其是 64 位 GCC)上编译代码时,这仍然会警告您。

虽然很奇怪,但这段代码还是有一定道理的:通常太接近零的整数不是有效指针。

所以这段代码的作用是:如果i的值是一个有效的指针(值大于45),它会返回一个指针,或者返回js的值。这种情况下的结果是0x14(记住我们需要将%d替换为%p,所以输出是十六进制的)。

编辑

查看您的main 函数后,我相信这里想要的是

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

但无论如何,这段代码可以简化为

 int check(register int i,register int j)
 {
    if(i >= 45)
        return i;
    else
        return j;
 }

甚至

 int check(register int i,register int j)
 {
    return i>=45 ? i : j;
 }

在这些情况下,main 函数应该是

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

请注意,由于 c 的数据类型现在是 int,因此 printf%p 将恢复为 %d。输出与原代码相同:20

【讨论】:

  • int *p=(int *)i; 我不喜欢,因为在某些机器上sizeof(address) 可能不等于sizeof(int)
  • @GrijeshChauhan 原因。但我认为register 的使用意味着您希望将代码控制在非常低的级别。所以机器特定的代码是可以接受的。此外,使用 45 来检测指针验证是机器特定的。
  • @Grijesh Chauhan、@Earth Engine 和 @interjay:- 在将问题标记为无用之前,请仔细阅读问题。此代码不会生成错误,但会生成任何存储类声明来代替 Register(像 auto,static 或 extern ) 会产生错误,这个事实已经在问题中准确地提到了。感谢您的努力...希望仔细研究这个问题。
【解决方案5】:

好的,现在我明白你在问什么了。

如果你有这样的函数原型声明:

int *check(register int, register int);

一旦编译器看到这一点,它就可以强制执行一条规则,即没有代码会尝试获取参数的地址。基于这一事实,编译器需要考虑一种一致的方式来为整个程序中的函数调用生成代码。这可能与也可能不同

int *check(int, int);

它执行参数的默认处理(但编译器将再次确保它在整个程序中保持一致)。

但请考虑以下几点:

int *check(auto int, auto int);

编译器不知道参数的地址是否会被使用,除非它看到了实际的实现。所以它不能假设任何东西。程序员显然想要一些优化,但编译器没有任何进一步的信息可以这样做。

因此将参数指定为auto 是没有意义的。

如果函数定义的参数使用auto怎么办?这又没有什么意义。编译器可以有一个策略,如果没有尝试获取参数地址,则将其视为register 其他使用堆栈。但是对于不使用auto 的参数,规则是一样的。所以它不会给编译器任何进一步的信息。

其他存储类甚至对函数定义中的参数都没有意义。无法通过静态数据区或堆传递参数(您只能传递指针 - 实际上在堆栈或寄存器中)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-27
    • 2023-03-31
    • 1970-01-01
    相关资源
    最近更新 更多