Are there range operators in C like there are in, say, Swift (closed range, open range)?
For example, in a switch statement, in Swift I would write this:

switch num {
    case 144...168:
    print("Something between 144 and 168")

    case 120..<144:
    print("Something between 120 and 143")
    
    default: break
}

If I try something similar in C, using printf instead of print of course, I cannot use the same range operators. Is there something I can use instead?
Thank you

推荐答案

你可以使用if条语句.

当我进行范围匹配时,我会创建宏来简化代码.

而且,我发现一个do { } while (0);块的积木比一个大的if/else梯子可以简化事情.

以下是一些代码:

#define RNGE(_val,_lo,_hi) \
    (((_val) >= (_lo)) && ((_val) <= (_hi)))
#define RNGEM1(_val,_lo,_hi) \
    (((_val) >= (_lo)) && ((_val) < (_hi)))

do {
    if (RNGE(num,144,168)) {
        printf("Something between 144 and 168\n");
        break;
    }

    if (RNGEM1(num,120,144)) {
        printf("Something between 120 and 143\n")
        break;
    }

    // default ...
} while (0);

UPDATE:

美好的不太熟悉宏,但它们看起来像函数.这行末尾的反斜杠是什么?还有,为什么要加下划线?习俗偏爱第二行和第四行非常清晰

宏是"生成"代码的一种方式.它们[通常]在编译器的一个单独的第一阶段进行处理:C预处理器(又名cpp).它们的操作就像你用编辑器"插入"代码一样.

如果你想在多行上定义宏(就像我做的那样),反斜杠是必需的.

我们可以使用(例如)查看宏生成的源代码

cc -E -P -o foo.i foo.c

以下是该阶段的成果:

do {
 if ((((num) >= (144)) && ((num) <= (168)))) {
  printf("Something between 144 and 168\n");
  break;
 }
 if ((((num) >= (120)) && ((num) < (144)))) {
  printf("Something between 120 and 143\n")
  break;
 }
} while (0);

下划线是my个人习惯(40多年后形成;-).

大多数人将宏定义为:

#define RNGE(num,lo,hi) \
    (((num) >= (lo)) && ((num) <= (hi)))
#define RNGEM1(num,lo,hi) \
    (((num) >= (lo)) && ((num) < (hi)))

我用它们来区分宏参数和C符号.注意这里我把_val改成了num.我故意这么做是为了使用与你的例子相同的符号(即num).现在,生成的输出中有num个.但是,这是来自[function]作用域C变量directlynum(例如,我忘了在宏中定义参数)还是来自参数?

这有点争议,因为在C语言中,有一些库标准(例如POSIX)为标准库使用保留了所有以"x"开头的符号.

然而,这只是一个惯例.我用"u"定义的符号很少与任何内部库符号冲突.如果有人这样做,我完全愿意承担更改代码的责任.事实上,40年来,我有过never次这样的冲突,而且从来没有重新编写过代码.

对于宏参数,它们位于不同的命名空间中,到目前为止可能与[POSIX]库中的全局变量或函数名冲突.

此外,我认为POSIX为自己 Select 这样一个广泛的机制是极端傲慢的.在python中,"u"前缀是一种约定,即对象函数对对象是私有的.

如果你想在不"违反"公约的情况下享受"uu"的好处(例如):

#define RNGE(val_,lo_,hi_) \
    (((val_) >= (lo_)) && ((val_) <= (hi_)))
#define RNGEM1(val_,lo_,hi_) \
    (((val_) >= (lo_)) && ((val_) < (hi_)))

宏在函数无法工作的某些情况下很有用.考虑以下情况,我们用inline函数替换宏:

static inline int
RNGE(int val,int lo,int hi)
{
    return (val >= lo) && (val <= hi);
}

// this works ...
int num;
do {
    if (RNGE(num,144,168)) {
        printf("Something between 144 and 168\n");
        break;
    }

    // default ...
} while (0)

// this blows up at compile time ...
int arr[1000];
int *ptr = &arr[37];
do {
    if (RNGE(ptr,&arr[144],&arr[168])) {
        printf("Something between array elements 144 and 168\n");
        break;
    }

    // default ...
} while (0)

C不像某些语言(例如C++)那样具有重载函数.因此,我们必须为每种参数类型显式地生成单独的函数.

在本例中,宏适用于any种可比较的类型.


UPDATE #2:

有关使用do { ... } while (0)代替if/else梯形图逻辑或switch/case的更多信息,请参阅我的答案:How to properly make a counting algorithm to count from file?

谢谢你的额外解释.我记得在Swift中,这些范围运算符非常适合在switch 声明中使用.然而,在这里,如果我在switch 中使用这个宏,编译器仍然会告诉我"表达式不是整型常量表达式".我错过了什么吗?——

简单的回答是...

  1. 有了switch (switch_expression),那么switch_expression可以是任何东西.

  2. 但是,C和case case_expression的限制是case_expression可以是在compile次时计算为constant的值.

  3. 所以,处理这个问题的方法是使用我上面第一个例子中的代码.

Warning:这是你应该停止阅读的地方…:-)

答案很长...

那么,why C有这个限制吗?主要是历史.但是,C希望生成极快的代码.

许多switch/case个块在case表达式中使用consecutive个数字:

switch (num) {
case 1:
    x = 23;
    break;

case 2:
    y = 37;
    break;

case 3:
    z = 19;
    break;

case ...:
    ...;
    break;

case 100:
    q = 28;
    break;

default:
    abort();
    break;
}

简单/明显的代码是(使用if/else个梯形图逻辑,我在这里转换为使用我的do/while/0"技巧",代码是:

do {
    if (num == 1) {
        x = 23;
        break;
    }

    if (num ==  2) {
        y = 37;
        break;
    }

    if (num == 3) {
        z = 19;
        break;
    }

    // cases for 4-99 ...

    if (num == 100) {
        q = 28;
        break;
    }

    // default:
    abort();
} while (0);

然而这if条语句中有lot条.但是,由于case个表达式是consecutive,优化编译器可以检测到这一点,并生成进行范围判断的[asm]代码(即if num在范围1-100内),并且可以使用num值索引到直接跳到相应代码段的指针表中.

下面是一些伪代码,显示了这一点:

static void *switch_vector[] = {
    &&L_1,
    &&L_2,
    &&L_3,
    ...,
    &&L_100
};

if (RNGE(num,1,100))
    goto switch_vector[num - 1];
else
    goto L_DEFAULT;

L_1:
    x = 23;
    goto L_ENDSWITCH;

L_2:
    y = 37;
    goto L_ENDSWITCH;

L_3:
    z = 19;
    goto L_ENDSWITCH;

// more labels ...

L_100:
    q = 28;
    goto L_ENDSWITCH;

L_DEFAULT:
    abort();

L_ENDSWITCH:
    // code after switch

Side note:我称之为"伪"代码,但实际上这是valid C代码,使用了C中"计算"goto的非常高级用法.不要实际使用它,因为它实际上从来都不需要.编译器通常会使用上述更简单的 struct 生成更好的代码.记住,我警告你:-)

见:

  1. Can you make a computed goto in C++?
  2. https://eli.thegreenplace.net/2012/07/12/computed-goto-for-efficient-dispatch-tables

C++相关问答推荐

如何在C中只使用一个带双方括号([i][j])访问语法的malloc来分配动态大小的2d数组?

为什么在C中进行大量的位移位?

向上强制转换C中的数值类型总是可逆的吗?

使用scanf在C中读取和存储文件中的值

判断X宏的空性

为什么将函数名括在括号中会禁用隐式声明?

为什么指针运算会产生错误的结果?

C指针概念分段故障

在C中访问数组中的特定值

在C中包装两个数组?

将字符串数组传递给C中的函数:`str[dim1][str_size]`vs`*str[dim1]`

链接器脚本和C程序使用相同的头文件,这可能吗?

`预期说明符-限定符-列表在‘(三元运算符中的’token`‘之前

为什么Linux无法映射这个PT_LOAD ELF段?

具有正确标头的C struct 定义问题

解密Chrome加密密钥

既然我们在 if 中将 int 的值更改为 10,为什么在第二个 fork 后,子进程及其创建的子进程都会打印 33 ?

为什么<到达*时不会转换为>?

如何确定 C 程序中的可用堆内存

将字节/字符序列写入标准输出的最简单形式