C语言中的宏指令,我的代码示例不起作用 [英] Macro directives in C, my code example doesn't work

查看:101
本文介绍了C语言中的宏指令,我的代码示例不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想获得以下代码段工作:

I want to get the following code piece work:

#define READIN(a, b) if(scanf('"#%d"', '"&a"') != 1) { printf("ERROR"); return EXIT_FAILURE; }

int main(void)
{
    unsigned int stack_size;
    printf("Type in size: ");
    READIN(d, stack_size);
}

我不明白如何在#运算符中使用指令.我想多次将scanf与打印错误等一起使用,但是"'"#%d"'&我认为'"&a"'"是完全错误的.有什么办法可以运行它吗?我认为宏是最好的解决方案吗?

I don't get it, how to use directives with the # operator. I want to use the scanf with print ERROR etc. several times, but the "'"#%d"' & '"&a"'" is I think completely wrong. Is there any way to get that running? I think a macro is the best solution or not?

推荐答案

您应仅将宏的参数字符串化,并且它们必须在宏替换文本中的字符串或字符常量之外.因此,您可能应该使用:

You should only stringify arguments to the macro, and they must be outside of strings or character constants in the replacement text of the macro. Thus you probably should use:

#define READIN(a, b) do { if (scanf("%" #a, &b) != 1) \
                          { fprintf(stderr, "ERROR\n"); return EXIT_FAILURE; } \
                     } while (0)

int main(void)
{
    unsigned int stack_size;
    printf("Type in size: ");
    READIN(u, stack_size);
    printf("You entered %u\n", stack_size);
    return(0);
}

有很多更改. do { ... } while (0)习惯用法可防止您在以下情况下出现编译错误:

There are many changes. The do { ... } while (0) idiom prevents you from getting compilation errors in circumstances such as:

if (i > 10)
    READIN(u, j);
else
    READIN(u, k);

通过宏,您会得到unexpected keyword 'else'类型的消息,因为第一个READIN()之后的分号将成为嵌入的if之后的空语句,因此else不能属于可见的if或宏内的if.

With your macro, you'd get an unexpected keyword 'else' type of message because the semi-colon after the first READIN() would be an empty statement after the embedded if, so the else could not belong to the visible if or the if inside the macro.

stack_size的类型是unsigned int;因此,正确的格式说明符为u(d用于带符号的int).

The type of stack_size is unsigned int; the correct format specifier, therefore, is u (d is for a signed int).

而且,最重要的是,宏中的参数a正确地进行了字符串化(相邻字符串文字的字符串串联-C89的一个非常有用的功能-为您处理了其余部分.参数b宏中的内容也不嵌入字符串中.

And, most importantly, the argument a in the macro is stringized correctly (and string concatenation of adjacent string literals - an extremely useful feature of C89! - takes care of the rest for you. And the argument b in the macro is not embedded in a string either.

错误报告已完成至stderr(用于报告错误的标准流),并且消息以换行符结尾,因此它将实际出现.我没有将return EXIT_FAILURE;替换为exit(EXIT_FAILURE);,但是如果宏将在main()之外使用,那将是一个明智的选择.假设首先出错,则终止"是适当的行为.它通常不适用于交互式程序,但要修复它会有点困难.

The error reporting is done to stderr (the standard stream for reporting errors on), and the message ends with a newline so it will actually appear. I didn't replace return EXIT_FAILURE; with exit(EXIT_FAILURE);, but that would probably be a sensible choice if the macro will be used outside of main(). That assumes that 'terminate on error' is the appropriate behaviour in the first place. It often isn't for interactive programs, but fixing it is a bit harder.

我也完全忽略了对使用scanf()的保留;我通常避免这样做,因为我发现错误恢复太难了.我仅用C进行编程已有28年的时间,但我仍然发现scanf()很难控制,因此我基本上从不使用它.我通常使用fgets()sscanf()代替.除其他优点外,我还可以报告引起麻烦的字符串;当scanf()可能吞噬了其中的一部分时,这很难做到.

I'm also ignoring my reservations about using scanf() at all; I usually avoid doing so because I find error recovery too hard. I've only been programming in C for about 28 years, and I still find scanf() too hard to control, so I essentially never use it. I typically use fgets() and sscanf() instead. Amongst other merits, I can report on the string that caused the trouble; that's hard to do when scanf() may have gobbled some of it.

我对scanf()的想法是,只能读正数,不能读字母.我的总体代码确实创建了一个堆栈,用户可以在其中键入内容,并且该类型只能是肯定的,否则将出错. [...]我只想知道是否有更好的解决方案来禁止用户输入非正数?

My thought with scanf() here is, to only read in positive numbers and no letters. My overall code does create a stack, which the user types in and the type should be only positive, otherwise error. [...] I only wanted to know if there's a better solution to forbid the user to type in something other than positive numbers?

我刚刚尝试了上面的代码(添加了#include <stdlib.h>#include <stdio.h>)并输入了-2,并被告知4294967294,这不是我想要的(%u格式不会拒绝-2,至少在MacOS X 10.7.2上).因此,很可能我会选择fgets()strtoul().但是,准确地检测出strtoul()的所有可能的问题是一种很好的练习.

I just tried the code above (with #include <stdlib.h> and #include <stdio.h> added) and entered -2 and got told 4294967294, which isn't what I wanted (the %u format does not reject -2, at least on MacOS X 10.7.2). So, I would go with fgets() and strtoul(), most likely. However, accurately detecting all possible problems with strtoul() is an exercise of some delicacy.

这是我想出的替代代码:

This is the alternative code I came up with:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <string.h>

int main(void)
{
    unsigned int stack_size = 0;
    char buffer[4096];
    printf("Type in size: ");
    if (fgets(buffer, sizeof(buffer), stdin) == 0)
        printf("EOF or error detected\n");
    else
    {
        char *eos;
        unsigned long u;
        size_t len = strlen(buffer);
        if (len > 0)
            buffer[len - 1] = '\0';  // Zap newline (assuming there is one)
        errno = 0;
        u = strtoul(buffer, &eos, 10);
        if (eos == buffer ||
            (u == 0 && errno != 0) ||
            (u == ULONG_MAX && errno != 0) ||
            (u > UINT_MAX))
        {
            printf("Oops: one of many problems occurred converting <<%s>> to unsigned integer\n", buffer);
        }
        else
            stack_size = u;
        printf("You entered %u\n", stack_size);
    }
    return(0);
}

strtoul()的规范在ISO/IEC 9899:1999§7.20.1.4中给出:

The specification of strtoul() is given in ISO/IEC 9899:1999 §7.20.1.4:

¶1[...]

¶1 [...]

unsigned long int strtoul(const char * restrict nptr,
char ** restrict endptr, int base);

unsigned long int strtoul(const char * restrict nptr,
char ** restrict endptr, int base);

[...]

¶2[...]首先, 他们将输入字符串分解为三部分:初始的(可能为空)序列 空格字符(由isspace函数指定),主题序列 类似于以base的值确定的某个基数表示的整数,以及 一个或多个无法识别的字符的最终字符串,包括终止null 输入字符串的字符.然后,他们尝试将主题序列转换为 整数,然后返回结果.

¶2 [...] First, they decompose the input string into three parts: an initial, possibly empty, sequence of white-space characters (as specified by the isspace function), a subject sequence resembling an integer represented in some radix determined by the value of base, and a final string of one or more unrecognized characters, including the terminating null character of the input string. Then, they attempt to convert the subject sequence to an integer, and return the result.

¶3[...]

¶4主题序列定义为输入字符串的最长初始子序列, 从第一个非空白字符开始,即预期的形式.主题 如果输入字符串为空或完全由白色组成,则序列不包含任何字符 空格,或者第一个非空格字符不是符号或允许的字母 或数字.

¶4 The subject sequence is defined as the longest initial subsequence of the input string, starting with the first non-white-space character, that is of the expected form. The subject sequence contains no characters if the input string is empty or consists entirely of white space, or if the first non-white-space character is other than a sign or a permissible letter or digit.

¶5如果主题序列具有预期的形式并且base的值为零,则该序列 第一位数字开头的字符被解释为一个整数常量,根据 6.4.4.1的规则如果主题序列具有预期的形式和base的值 介于2到36之间,用作转换的基础,并赋予每个字母其值 如上所述.如果主题序列以减号开头,则该值由 转换被否定(在返回类型中).指向最终字符串的指针存储在 如果endptr不是空指针,则由endptr指向的对象.

¶5 If the subject sequence has the expected form and the value of base is zero, the sequence of characters starting with the first digit is interpreted as an integer constant according to the rules of 6.4.4.1. If the subject sequence has the expected form and the value of base is between 2 and 36, it is used as the base for conversion, ascribing to each letter its value as given above. If the subject sequence begins with a minus sign, the value resulting from the conversion is negated (in the return type). A pointer to the final string is stored in the object pointed to by endptr, provided that endptr is not a null pointer.

¶6[...]

¶7如果主题序列为空或不具有预期形式,则不进行任何转换. 表演nptr的值存储在endptr指向的对象中,前提是 endptr不是空指针.

¶7 If the subject sequence is empty or does not have the expected form, no conversion is performed; the value of nptr is stored in the object pointed to by endptr, provided that endptr is not a null pointer.

¶8strtolstrtollstrtoulstrtoull函数返回转换后的 值(如果有).如果无法执行转换,则返回零.如果正确的值 超出可代表值的范围,LONG_MIN,LONG_MAX,LLONG_MIN, 返回LLONG_MAX,ULONG_MAX或ULLONG_MAX(根据返回类型) 和值的符号(如果有的话),宏ERANGE的值存储在errno中.

¶8 The strtol, strtoll, strtoul, and strtoull functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value is outside the range of representable values, LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX, ULONG_MAX, or ULLONG_MAX is returned (according to the return type and sign of the value, if any), and the value of the macro ERANGE is stored in errno.

我得到的错误来自64位编译,其中-2被转换为64位无符号长整型,并且超出了32位unsigned int可接受的范围(失败条件为).当我以32位模式(即sizeof(unsigned int) == sizeof(unsigned long))重新编译时,值-2再次被接受,再次被解释为4294967294.因此,即使这还不够精细...您可能必须手动跳过前导空格并拒绝一个负号(也可能是一个正号;您也需要#include <ctype.h>):

The error I got was from a 64-bit compilation where -2 was converted to a 64-bit unsigned long, and that was outside the range acceptable to a 32-bit unsigned int (the failing condition was u > UINT_MAX). When I recompiled in 32-bit mode (so sizeof(unsigned int) == sizeof(unsigned long)), then the value -2 was accepted again, interpreted as 4294967294 again. So, even this is not delicate enough...you probably have to do a manual skip of leading blanks and reject a negative sign (and maybe a positive sign too; you'd also need to #include <ctype.h> too):

        char *bos = buffer;
        while (isspace(*bos))
            bos++;
        if (!isdigit(*bos))
            ...error - not a digit...
        char *eos;
        unsigned long u;
        size_t len = strlen(bos);
        if (len > 0)
            bos[len - 1] = '\0';  // Zap newline (assuming there is one)
        errno = 0;
        u = strtoul(bos, &eos, 10);
        if (eos == bos ||
            (u == 0 && errno != 0) ||
            (u == ULONG_MAX && errno != 0) ||
            (u > UINT_MAX))
        {
            printf("Oops: one of many problems occurred converting <<%s>> to unsigned integer\n", buffer);
        }

正如我所说,整个过程相当重要.

As I said, the whole process is rather non-trivial.

(再来看一遍,我不确定u == 0 && errno != 0子句是否会捕获任何错误...可能不是因为eos == buffer(或eos == bos)条件捕获了情况而已完全可以转化.)

(Looking at it again, I'm not sure whether the u == 0 && errno != 0 clause would ever catch any errors...maybe not because the eos == buffer (or eos == bos) condition catches the case there's nothing to convert at all.)

这篇关于C语言中的宏指令,我的代码示例不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆