取消引用超出范围内的静态数据的指针C

问题描述:

免责声明:此问题严格学术。我将要提供的例子可能是糟糕的风格。取消引用超出范围内的静态数据的指针C

假设在CI写这种形式的子程序:

char *foo(int x) 
{ 
    static char bar[9]; 

    if(x == 0) 
     strcpy(bar, "zero"); 
    else 
     strcpy(bar, "not zero"), 

    return bar; 
} 

然后,在其他地方,我用foo如下:

printf("%i is %s\n", 5, foo(5)); 

我的指针和静态变量的心智模型预测,在练习,这个printf的输出将是

5不是零

......但是这实际上是由C标准要求的,还是我在鼻恶魔领土?

为了使事情更糟的是,怎么样像

strcpy(foo(5), "five"); 

我的心理模型认为这应该“工作”,除非它是明确违法的,虽然它有点毫无意义,因为它并不影响foo输出。但是,这又是由标准确定的吗?

+2

对于你的最后一个例子,它是“合法的”,但我不确定它是否是“道德的”。如果每个人都可以修改它,那么也可能有一个普通的旧的全球。 (并且要小心线程。) – Mat 2012-04-03 18:11:47

你写的是好的;有没有鼻恶魔在等着你。即使strcpy()示例也是'OK',因为您不会践踏数组的范围。 'OK'是用引号引起来的,因为它不是一个好主意,但是按照书面的说法,没有超出界限的内存访问,所以没有未定义的行为。该函数中的static数据在程序的整个生命周期中都存在,其中包含写入它的最后一个值。

如果你尝试可能有问题:

printf("%i is %s but %i is %s\n", 5, foo(5), 0, foo(0)); 

你会得到两个数字中的一个错误的答案,但它没有定义,这将是错误的答案。

+0

为什么它会不同?我没有看到它...... – 2012-04-03 18:09:48

+2

函数中的变量只能在调用printf()时调用'not zero'或'zero'(两次调用'foo()'后)完成)。因此,相同的字符串将被打印为0和5,并且由于其中一个是零而另一个不是,函数中的字符串是否包含“零”或“不为零”并不重要;这两个数字中的一个是错误的。如果你想要看起来并且让它们都正确,你可以修改这个函数:'static const char bar [9] =“not zero”; if(x == 0)return&bar [4];否则返回&bar [0];'突然都会好的。 – 2012-04-03 18:11:26

+1

@ 0A0D因为该功能有副作用。 – cnicutar 2012-04-03 18:12:30

我看到这段代码没有任何问题。

foo()返回的指针是一个有效的指针,您可以取消引用它。通过“没有错”我的意思是,一切都在语法上确定,但我同意其他答案,由于各种原因,这个代码是不好的任何意思的单词。根据C标准,这仅仅是正确的。

好,因为要从标准报价:

的对象的生存期是程序执行 期间存储是保证被保留用于它的一部分。

其identi音响ER被声明没有 存储类SPECI音响ER _Thread_local,并且用外部或内部联动 或与存储类SPECI音响ER静态,具有静态存储持续时间的对象。其 生命周期是程序的整个执行过程,其存储的值 仅在程序启动之前初始化一次。

在任何条件下,foo()的指针返回到一个静态变量,其寿命是永久性的(即,它从当控制经过它的第一次,直到节目结束住)。这很好。

您的代码的逻辑不太好;例如,该函数不可重入且远离线程安全,当然,无防备的字符串操作完全是自杀。

+0

那么,无防备的字符串操作有点邪恶,因为它可以很容易地破解以后的事情,但不应该这样做,因为我只用它的字面字符串?即使从函数外部,如果我已经知道'foo'返回一个指向我不必释放的字符串的指针,并且该字符串有时不是“0”,那么我可以推断出该字符串是静态分配,我可以在其中安装“五”。 – Ian 2012-04-03 19:22:18

+0

您必须确保您永远不会将超过7个字符的字符串写入字符串。也许不是“自杀”(我认为我误解你的代码为'strcat'而不是'strcpy'),但它的不必要的危险。你可以在'sizeof bar'中放入'strncpy'。 – 2012-04-03 22:02:38

没有问题。 更好的风格将是

const char *foo(int x); 

那么你就不能STRCPY进去没有虚掷的常量。如果你真的想打破它,没有什么能阻止你。

如果你想让它重新进入。

const char *foo(int x) 
{ 
return (x? "not zero" : "zero"); 
}