-3

我正在调用一个通过指针参数返回变量的函数。我不关心这个参数的返回值,也不想制作一个虚拟变量来传递给函数。为了一个简单的例子,假设函数如下,我不关心,也不想为参数“d”做一个虚拟变量。

void foo(int a, int b, int* c, int* d)
{
*c = a+b;
*d = a+b+*c;
}

我知道 NULL 指针在理论上是指向不是任何对象或函数地址的位置的指针。如果 NULL 被定义如下,那么在这个函数中将 NULL 传递给“d”是否正确?或者这会改变内存中第 0 个元素的内容吗?

#define NULL ((void *)0)

目标设备是 MSP430,我使用的是 IAR C。没有使用操作系统,因此没有实现内存管理

编辑:请注意,我不想创建一个虚拟变量。此外,如果有一种方法可以欺骗编译器以优化“d”参数而不更改函数定义,那么这是可取的。

编辑#2:我宁愿不在函数调用中使用 & 运算符,因为它会生成我不想生成的低效代码

编辑#3:对于那些在我谈论 & 运算符时不相信我的人......编译器手册指出“避免使用 & 运算符获取局部变量的地址。这效率低下主要有两个原因。首先,变量必须放在内存中,因此不能放在处理器寄存器中。这会导致代码更大更慢。其次,优化器不能再假设局部变量不受函数调用的影响。

4

10 回答 10

7

不,这是不正确的。

当您执行此操作时,C 标准没有定义行为。在许多系统上,当尝试存储到地址 0 时会导致错误(某种内存故障)。foo如果没有,那么您将数据写入地址 0,可能会覆盖那里可能需要的其他内容,因此您的系统可能会在以后出现故障。

于 2012-10-19T13:38:10.847 回答
2

您应该稍微更改您的功能以允许通过NULL

void foo(int a, int b, int* c, int* d)
{
    if(c != NULL)
    {
       *c = a+b;
       if(d != NULL) 
       {
          *d = a+b+*c;
       }
    }
}

现在可以安全通过了NULL。否则,正如其他答案已经说明的那样,您最终会取消引用NULL导致未定义行为的指针。

于 2012-10-19T13:41:06.923 回答
1

由于虚拟内存,内存中没有第 0 个元素。但是,如果您尝试这样做,您的程序将因内存异常而崩溃。我假设您想忽略 d 如果它为空,那么只需执行以下操作:

if(d != NULL) { *d = a+b+*c }

于 2012-10-19T13:41:01.487 回答
1

由于您不想创建虚拟变量并且无法更改函数,因此您很可能最终会在设备上的内存位置 0 处乱涂乱画,无论这意味着什么。也许它是一个内存映射硬件寄存器,也许它只是普通的物理内存。

如果它是一个寄存器,那么它可能没有任何作用,除非你将魔法值 4711 写入其中,这将每三个月发生一次,并且设备会停止并着火。(发生在我身上,覆盖设备上的启动eeprom很有趣)

或者如果它是内存,也许你稍后会发送一个 NULL 指针到另一个函数,那个函数会很高兴地读取这个函数在那里写的值,你最终会在早上 5 点扯掉你的头发并大喊“这可以'不可能影响那个!”。(在一些用于映射 NULL 页面的古代 unix 上发生在我身上)

也许您的编译器为您添加了安全网。也许不是。也许下一个版本会。也许下一个硬件版本将在地址 0 处未映射内存,并且设备将停止。

我会在调用函数中创建一个虚拟变量,然后继续处理一个更有趣的问题,但如果你是压力瘾君子,请传递 NULL 并看看会发生什么(今天或 10 年后)。

于 2012-10-19T14:23:46.460 回答
1

在您的示例中,如果您不关心指针d并且按照定义传递 NULL,那么它可能会由于取消引用 NULL 而崩溃。即使您不关心结果,您也应该传递一个有效的指针。

为什么不只是宣布临时并通过?

int tempd;    
foo(a,b,&c, &tempd);
于 2012-10-19T13:40:01.870 回答
0

有趣的问题!一般来说,NULL 被保留为“无效”地址。你不应该尝试写信给它,但我认为标准没有规定如果你这样做会发生什么。在 Windows 机器上,这将生成访问冲突异常。我不知道你的设备会发生什么。

在任何情况下,传递 NULL 是表明您对 out 参数的值不感兴趣的常用方法。但是你的函数必须意识到这一点并采取相应的行动:

if( c ) {
    *c = a+b;

    if( d ) {
        *d = a+b+*c;  
    }
}

编辑

如果您无法更改函数定义,那么您可能不走运。如果调用约定是 cdecl,您可以欺骗编译器不传递 d。只需声明不带 d 参数的函数:

extern void foo( int a, int b, int * c );

但是,您肯定会在这里陷入危险的恶作剧领域。函数定义仍然需要 d 参数,所以它会看到随机垃圾。

我能想到的唯一另一件事是传递一个固定地址。由于您正在为特定设备编写内容,因此可能有一个可以安全写入的地址范围。(也就是说,它不会导致异常或损坏实际内存。)

void * SafeAddress = (void *)0x12345678;

foo( a, b, &c, SafeAddress );

当然,最简单的方法是只使用虚拟变量。我知道你不止一次说过这会产生低效的代码,但一定是这样吗?如果虚拟变量是局部变量还是全局变量,会有区别吗?

于 2012-10-19T13:41:32.633 回答
0

该函数尝试将值存储在提供的地址。如果地址无效,那么就会出现某种故障——与您是否使用操作系统无关

要么你必须给函数一些有效的地址(即使你不关心特定情况下的值),或者你必须更改函数以使其不存储值(甚至可能不甚至计算它),如果它的地址为 NULL(在您的平台上可能是也可能不是 0x0,顺便说一句)。

你不断重复,你“不想”做前者,不能做后者。那么,你有一个无法解决的困境。也许,已经存在一些其他地址,像这样的虚拟值可以存储在您的程序(或平台上)中——您可以传递它。

如果没有涉及操作系统,那么您必须处理一些时髦的可编程设备,这意味着您周围应该有经验丰富的 C 程序员。要求他们确认这里告诉你的内容——很明显,你已经不相信几个人给你的答案了。

于 2012-10-19T15:43:29.290 回答
0

在那个特定的示例代码中,两个传入的指针都被取消引用。取消引用NULL是未定义的行为。只需使用虚拟变量即可。

一般来说:如果一个函数接受一个指针,它应该说明空指针是否是参数的有效值。如果它没有说什么,请保持安全,并假设它不是。这会让你免于悲伤。

于 2012-10-19T13:38:22.167 回答
-1

它会尝试在内存位置 0x0 分配一些东西,所以我会说它会崩溃

于 2012-10-19T13:38:04.057 回答
-2

通过声明一个虚拟变量以及一个指向虚拟变量的指针,我能够在不增加堆栈内存使用量的情况下节省代码空间。

int  Dummy;
int* Dummy_ptr = &Dummy;

这允许编译器对函数调用进行优化,因为 & 运算符未在函数调用中使用。

电话是现在

foo(a, b, c_ptr, Dummy_ptr);

编辑:对于那些不相信我的人。我看了看汇编程序。虚拟变量存在于堆栈中,但因为它以后不使用,并且因为它只是函数的返回,所以地址永远不会传递给函数,并且函数中对该变量的任何使用都被优化了。

于 2012-10-19T14:26:44.023 回答