在 C 中,如何在没有指针的情况下检索超出范围的静态值?
How can i retrieve the value of an out of scope static without a pointer in C?
我正在尝试解决 Effective C 第 2 章的练习 1,它说:
"向清单 2-6 中的计数示例添加检索函数以检索计数器的当前值"
清单 2-6 中的代码是:
#include <stdio.h>
void increment(void) {
static unsigned int counter;
counter++;
printf("%d ", counter);
}
int main(void) {
for (int i = 0; i < 5; i++) {
increment();
}
return 0;
}
我尝试了几件事都失败了,我不明白如何检索计数器的值,因为在增量函数之外它超出了范围并且没有可以使用的指针。
使用 return 你可以这样做,
#include <stdio.h>
unsigned int increment(void)
{
static unsigned int counter;
counter++;
return counter;
}
int main(void)
{
for (int i = 0; i < 5; i++)
{
printf("%u ", increment());
}
return 0;
}
我将 counter
和检索或更新其值的函数分开。为此,我会将 counter
转移到文件范围并使其对其他翻译单元不可见(即 static
):
static unsigned int counter;
void increment(void) {
counter++;
}
unsigned int getCounter() {
return counter;
}
// usually in a separate translation unit
int main(void) {
for (int i = 0; i < 5; i++) {
increment();
printf("%d ", getCounter());
}
return 0;
}
我一直在阅读 Effective C 只是为了提神,这个问题让我有点措手不及,因为它太松散了。
你可以只提升计数器的范围,但这违背了目的并且不普遍适用。我认为一个好的解决方案不会改变计数器的范围。如果我们可以访问计数器,那么检索函数将是多余的。实际上,只需 return 来自 increment() 的该死的值并捕获它,没有理由不这样做。但是这个练习是为了让我们思考范围,所以让我们尝试在不修改计数器的范围或增量的 return 类型的情况下解决这个问题。
一个想法是创建一个辅助函数,我们可以在 increment() 中调用它来传递计数器的值。我们可以将该值保存在另一个静态局部变量中。我们可以写一个 if 语句,这样如果我们给它一个超出计数器范围的值(比如负数),它将 return 计数器最后保存的值。这很容易理解,但是太天真了。在为我们的辅助函数提供两种变量(计数器是无符号的,负整数本身是有符号的)时,我们必须将有符号的值转换为无符号的(坏主意),或者将无符号的值转换为有符号的。
将每个 arg 捕获为带符号的 int 并没有那么糟糕,因为它保留了负值,但这有点让人头疼。我们将不得不处理溢出,并且通过限制最大无符号值以支持能够表示所有负整数(我们实际上只关心 1),我们实际上会浪费存储空间。我们可以选择我们的“特殊”值作为最大的 unsigned int,只要我们处理计数器和我们的特殊值的冲突,这是一个安全的赌注。我认为有一个更好的解决方案,它清晰明了,主要是安全的,并且不会浪费我们的时间来处理转换类型。
让我们创建一个辅助函数,它接受一个指向 unsigned int 的指针。我们将拥有一个特殊的内存地址,而不是具有“特殊”值。如果我们传入的内容指向该地址,则计数为 return。否则,将计数分配给我们的 arg 的引用值。我们可以设置一个任意值的文件作用域的 unsigned int 变量,调用这个函数并将地址传递给 increment() 中的计数器,然后用我们的“全局”变量的地址检索调用这个函数。
但是,这只有效,因为计数器被定义为静态的。对 non-static 局部变量执行此操作是明确的 UB,但由于静态局部变量存在于它们的函数调用之外,因此这不是 UB。然而,这是有风险的。只要我们不将我们的 arg 分配给任何东西,就没有其他方法可以导致 UB。好消息是,这保留了 counter 的范围,清晰易读,勉强避免了 UB。
#include <stdio.h>
unsigned int check = 1;
unsigned int setcounter(unsigned int *c){
static unsigned int count = 0;
if (c == &check){
return count;
}else{
count = *c;
return count;
}
}
unsigned int retrieve(void){
return setcounter(&check);
}
void increment(void){
static unsigned int counter = 0;
counter++;
setcounter(&counter);
printf("%d ", counter);
}
int main(void){
for (int i = 0; i < 5; i++){
printf("Count: ");
increment();
printf("Retrieved: ");
printf("%d\n", retrieve());
}
return 0;
}
无论如何,我想这就是 Seacord 为这次练习准备的。 C 根本不是我的主要语言,所以如果我在这里有任何错误陈述或犯下任何 C 罪行,请纠正我。这是用 -Wall -Werror -pedantic 编译的,这让我很高兴。
我正在尝试解决 Effective C 第 2 章的练习 1,它说:
"向清单 2-6 中的计数示例添加检索函数以检索计数器的当前值"
清单 2-6 中的代码是:
#include <stdio.h>
void increment(void) {
static unsigned int counter;
counter++;
printf("%d ", counter);
}
int main(void) {
for (int i = 0; i < 5; i++) {
increment();
}
return 0;
}
我尝试了几件事都失败了,我不明白如何检索计数器的值,因为在增量函数之外它超出了范围并且没有可以使用的指针。
使用 return 你可以这样做,
#include <stdio.h>
unsigned int increment(void)
{
static unsigned int counter;
counter++;
return counter;
}
int main(void)
{
for (int i = 0; i < 5; i++)
{
printf("%u ", increment());
}
return 0;
}
我将 counter
和检索或更新其值的函数分开。为此,我会将 counter
转移到文件范围并使其对其他翻译单元不可见(即 static
):
static unsigned int counter;
void increment(void) {
counter++;
}
unsigned int getCounter() {
return counter;
}
// usually in a separate translation unit
int main(void) {
for (int i = 0; i < 5; i++) {
increment();
printf("%d ", getCounter());
}
return 0;
}
我一直在阅读 Effective C 只是为了提神,这个问题让我有点措手不及,因为它太松散了。
你可以只提升计数器的范围,但这违背了目的并且不普遍适用。我认为一个好的解决方案不会改变计数器的范围。如果我们可以访问计数器,那么检索函数将是多余的。实际上,只需 return 来自 increment() 的该死的值并捕获它,没有理由不这样做。但是这个练习是为了让我们思考范围,所以让我们尝试在不修改计数器的范围或增量的 return 类型的情况下解决这个问题。
一个想法是创建一个辅助函数,我们可以在 increment() 中调用它来传递计数器的值。我们可以将该值保存在另一个静态局部变量中。我们可以写一个 if 语句,这样如果我们给它一个超出计数器范围的值(比如负数),它将 return 计数器最后保存的值。这很容易理解,但是太天真了。在为我们的辅助函数提供两种变量(计数器是无符号的,负整数本身是有符号的)时,我们必须将有符号的值转换为无符号的(坏主意),或者将无符号的值转换为有符号的。
将每个 arg 捕获为带符号的 int 并没有那么糟糕,因为它保留了负值,但这有点让人头疼。我们将不得不处理溢出,并且通过限制最大无符号值以支持能够表示所有负整数(我们实际上只关心 1),我们实际上会浪费存储空间。我们可以选择我们的“特殊”值作为最大的 unsigned int,只要我们处理计数器和我们的特殊值的冲突,这是一个安全的赌注。我认为有一个更好的解决方案,它清晰明了,主要是安全的,并且不会浪费我们的时间来处理转换类型。
让我们创建一个辅助函数,它接受一个指向 unsigned int 的指针。我们将拥有一个特殊的内存地址,而不是具有“特殊”值。如果我们传入的内容指向该地址,则计数为 return。否则,将计数分配给我们的 arg 的引用值。我们可以设置一个任意值的文件作用域的 unsigned int 变量,调用这个函数并将地址传递给 increment() 中的计数器,然后用我们的“全局”变量的地址检索调用这个函数。
但是,这只有效,因为计数器被定义为静态的。对 non-static 局部变量执行此操作是明确的 UB,但由于静态局部变量存在于它们的函数调用之外,因此这不是 UB。然而,这是有风险的。只要我们不将我们的 arg 分配给任何东西,就没有其他方法可以导致 UB。好消息是,这保留了 counter 的范围,清晰易读,勉强避免了 UB。
#include <stdio.h>
unsigned int check = 1;
unsigned int setcounter(unsigned int *c){
static unsigned int count = 0;
if (c == &check){
return count;
}else{
count = *c;
return count;
}
}
unsigned int retrieve(void){
return setcounter(&check);
}
void increment(void){
static unsigned int counter = 0;
counter++;
setcounter(&counter);
printf("%d ", counter);
}
int main(void){
for (int i = 0; i < 5; i++){
printf("Count: ");
increment();
printf("Retrieved: ");
printf("%d\n", retrieve());
}
return 0;
}
无论如何,我想这就是 Seacord 为这次练习准备的。 C 根本不是我的主要语言,所以如果我在这里有任何错误陈述或犯下任何 C 罪行,请纠正我。这是用 -Wall -Werror -pedantic 编译的,这让我很高兴。