【问题标题】:Pointer to literal value指向文字值的指针
【发布时间】:2009-10-14 07:09:43
【问题描述】:

假设我在头文件中定义了一个常量

#define THIS_CONST 'A'

我想将此常量写入流。我会这样做:

char c = THIS_CONST;
write(fd, &c, sizeof(c))

但是,为了简洁明了,我希望 做的是:

write(fd, &THIS_CONST, sizeof(char)); // error
                                      // lvalue required as unary ‘&’ operand

有谁知道获取指向文字的指针的任何宏/其他技巧?我想要一些可以像这样使用的东西:

write(fd, PTR_TO(THIS_CONST), sizeof(char))

注意:我意识到我可以将我的常量声明为静态 const 变量,但是我不能在 switch/case 语句中使用它们。即

static const char THIS_CONST = 'A'
...
switch(c) {
  case THIS_CONST: // error - case label does not reduce to an integer constant
    ...
}

除非有办法在 case 标签中使用 const 变量?

【问题讨论】:

  • 唯一想到的是链接器技巧。你用的是什么编译器?如果您可以在链接器命令中引入一些绝对指针值,则可以使用 (void *)0x00000004 作为整数值 '4' 和字符。它不是便携式的。如果您使用的是 Windows,请查看名为 .cxa - .cxz 的 C++ 构造函数列表,该列表由 MS Visual C 标准 C 库在初始化期间使用。这类似于我正在谈论的那种链接器技巧。不同之处将导致输出图像包含一些不可重定位的地址,您可以将其转换为 int。;)
  • 有趣的想法 - 这比我想要的要多一点头骨,但仍然很有趣。我正在使用 gcc,我的代码必须在 Linux 和 Solaris 下编译:)
  • 除了想在case语句中使用它,为什么还需要它?在下面的一个答案中,您说您还希望它适用于浮点数,但无论如何您都不能将浮点数用作 case 语句表达式。
  • 关于浮点数的好点 - 但整数仍然适用。我唯一需要的是在案例陈述中。然而,这个问题的动机并不是因为“需要它”,而是只是想看看它是否可以通过一些 C 宏技巧(并且很清楚文字与变量不同并且不能获取其地址) .无论如何,谢谢大家的贡献-我想简短的回答是“不”,没有公认的技巧可以让编译器将文字值放入内存位置并获取指向它的指针,也没有干净和线程安全的方法在运行时执行。

标签: c


【解决方案1】:

在 C89 中无法直接执行此操作。您必须使用一组宏来创建这样的表达式。

在 C99 中,允许声明 struct-or-union 文字,并且可以使用类似的语法编写标量的初始化程序。因此,有一种方法可以达到预期的效果:

#include <stdio.h>

void f(const int *i) {
    printf("%i\n", *i);
}

int main(void) {
    f(&(int){1});
    return 0;
}

【讨论】:

  • 花了我一段时间才知道这个,但是是的,这正是我所说的!谢谢!
  • 这类结构称为复合文字ISO/IEC 9899:TC3, p75, §6.5.2.5, ¶4)。我已经设法通过 this 答案找到了这些信息。谢谢!我希望将来对 C99 规范的引用对某人有用!
  • "在 C99 中,允许声明 struct-or-union 字面量" --> 不,从 C99 开始,允许声明 复合字面量
【解决方案2】:

这些答案都已过时,除了评论之外,没有人提及最近的语言更新。

在使用复合文字 http://en.cppreference.com/w/c/language/compound_literal 的 C99-C11-C17 编译器上,可以创建 指向无名常量的指针,如:

int *p = &((int){10});

【讨论】:

  • 接受的答案明确引用 C99 并使用复合文字(尽管可以改进以使用正确的术语“复合文字”而不是“结构或联合文字”)
【解决方案3】:

获得指针的唯一方法是将文字放入变量中(您的第一个代码示例)。然后,您可以将变量与write()switch 中的文字一起使用。

【讨论】:

  • 我希望有一个宏来执行“将文字放入变量并获取指向它的指针”部分,但由于没有提出线程安全的解决方案,我接受这个作为简单的答案我的问题。
  • @JeffH:我可以理解你不会费心解释为什么你对一个彻头彻尾的愚蠢答案投了反对票。但我认为我的回答不符合此条件,所以它不能是“糟糕的形式”、“糟糕的语言”或“根本不适用”,但有人对 content表示异议> 我的回答。在这种情况下,我认为有必要发表评论...
【解决方案4】:

C 根本不允许像 'A' 这样的字符文字的地址。对于它的价值,C 中的字符文字类型是 int(C++ 中的 char 但这个问题被标记为 C)。 'A' 将具有实现定义的值(例如 ASCII 系统上的 65)。取值的地址没有任何意义,也不可能。

现在,你当然可以取其他种类的字面量的地址,例如字符串字面量,例如下面的就可以了:

write(fd, "potato", sizeof "potato");

这是因为字符串字面量“potato”是一个数组,它的值是一个指向开头“p”的指针。

为了详细说明/澄清,您只能获取对象的地址。即,&(地址)运算符需要一个对象,而不是一个值。

为了回答我错过的另一个问题,C 不允许非常量大小写标签,这包括声明为 const 的变量。

【讨论】:

  • 在 C 中,'A' 不是文字,而是常量。 C 有 2 种文字:字符串文字复合文字。与常量 'A' 不同,这两个都可以获取地址。
【解决方案5】:

由于调用 write() 将单个字符写入文件描述符几乎可以肯定是性能杀手,因此您可能只想执行 fputc( THIS_CONST, stream )。

【讨论】:

  • 谢谢,这是一个很好的观点,我没有想到这一点。但是,这并没有扩展到文字整数、浮点数等。另外,在当前情况下我实际上不能使用 fputc - 我没有在问题中提到这一点,但我实际上正在调用一个函数,它是 write 的包装器(),所有数据都必须经过这个包装函数。
【解决方案6】:
#define THIS_CONST 'a'

只是一个宏。编译器基本上只是在您使用 THIS_CONST 的任何地方插入“a”。 你可以试试:

const char THIS_CONST = 'a';

但我怀疑这不会起作用(没有方便的 c 编译器来试用它,而且我已经有好几年没写 c 代码了)。

【讨论】:

  • 这是迄今为止我看到的最好的答案。说的有道理,解决了问题,其他方面的代码也改进了。
  • sbi:除了詹姆斯本人在他的问题中已经给出了这个解决方案,并且还给出了他不喜欢它的原因
  • 啊,我错过了。但现在我想知道:C++ 允许使用 const 作为案例标签。不是吗?
  • 它没有。 C 没有奇怪的 C++ 语义,其中const 主要表示“只读”,有时表示“编译时文字常量”。在 C 语言中,它总是前者——const 变量从初始化的角度来看是只读的,但它不是编译时常量。这就是为什么所有 C 库都使用 #define 作为真正的常量的原因。
  • 谢谢帕维尔的解释!
【解决方案7】:

只用一个字符串常量,它是一个字符的指针,然后只写1个字节:

#define MY_CONST_STRING "A"

write(fd, MY_CONST_STRING, 1);

注意字符串末尾的'\0'字节没有写入。

您可以对各种常量值执行此操作,只需使用适当的十六进制代码字符串,例如

#define MY_CONST_STRING "\x41"

也会给出字符'A'。对于多字节内容,请注意使用正确的字节顺序。

假设您想要一个指向 INT_MAX 的指针,例如0x7FFFFFFF 在 32 位系统上。然后您可以执行以下操作:

#define PTR_TO_INT_MAX "\xFF\xFF\xFF\x7F"

您可以通过将其作为取消引用的指针传递给 printf 来看到它的工作原理:

printf ("max int value = %d\n", *(int*)PTR_TO_INT_MAX);

应该打印 2147483647。

【讨论】:

  • 我喜欢这里的想法,我没想过在这样的字符串中使用十六进制转义码。除了使用复合文字之外,我见过的最好的解决方案。感谢您的建议。
  • (int*)PTR_TO_INT_MAX 还有 2 个问题。转换为 int* 可能无法满足对齐要求。 "\xFF\xFF\xFF\x7F" 是字节序敏感的。
【解决方案8】:

对于字符,您可以使用额外的全局静态变量。可能是这样的:

#define THIS_CONST 'a'
static char tmp;
#define PTR_TO(X) ((tmp = X),&tmp)

write(fd,PTR_TO(THIS_CONST),sizeof(char));

【讨论】:

  • 是的,这正是我所想的——只是它不是线程安全的。
【解决方案9】:

编译器没有理由将文字放入任何内存位置,所以你的问题没有意义。例如像

这样的语句
int a;
a = 10;

可能会直接翻译成“将值 10 放入寄存器”。在编译器的汇编语言输出中,值 10 本身甚至永远不会作为内存中可以指向的东西存在,除非作为实际程序文本的一部分。

你不能把指针指向它。

如果你真的想要一个宏来获取指针,

#include <stdio.h>
static char getapointer[1];

#define GETAPOINTER(x)  &getapointer, getapointer[0] = x

int main ()
{
    printf ("%d\n",GETAPOINTER('A'));
}

【讨论】:

  • 如果我的回答有问题,请留言告诉我。
  • 您的回答毫无意义,您没有对实际问题做出任何贡献,因此投了反对票。
  • 我知道通常不会将文字放入内存位置,例如可以直接将其编码到指令中。但是,我正在寻找一种方便的方法 make 编译器将文字放入内存位置并给我一个指向它的指针,以使我的代码更清晰和更短。
  • 你的答案也是错误的。您的代码实际上转换为: * 在编译期间将 a 注册为符号。 * 每当引用 a 时,使用它所指向的值。 * 使用 & 引用时获取内存地址而不是值。
  • 而且由于“char”实际上只是一个数字,因此您不会在内存中包含任何“文字”。
【解决方案10】:

我可以看到您在这里尝试做什么,但您在这里尝试使用两种根本不同的东西。问题的症结在于case 语句需要使用在编译时存在的值,但指向内存中数据的指针仅在运行时可用。

当你这样做时:

#define THIS_CONST 'A'
char c = THIS_CONST;
write(fd, &c, sizeof(c))

你正在做两件事。您正在使宏 THIS_CONST 在编译时可用于其余代码,并且您正在创建一个新的 char 在运行时初始化为此值。在write(fd, &amp;c, sizeof(c))这行执行的那一刻,THIS_CONST的概念已经不存在了,所以你已经正确地识别出你可以创建一个指向c的指针,但不能创建一个指向THIS_CONST的指针。

现在,当你这样做时: 静态 const char THIS_CONST = 'A'; 开关(c){ case THIS_CONST: // 错误 - case 标签不会减少为整数常量 ... }

您正在编写代码,其中 case 语句的值需要在编译时进行评估。但是,在这种情况下,您指定 THIS_CONST 时它是一个变量,因此它的值仅在运行时可用尽管您“知道”它将具有特定值。当然,其他语言允许 case 语句发生不同的事情,但这些是 C 的规则。

以下是我的建议:

1) 不要调用变量THIS_CONST。没有技术上的理由不这样做,但惯例表明这是一个编译时宏,您不想让读者感到困惑。

2) 如果您希望相同的值在编译时和运行时可用,请找到将编译时宏映射到运行时变量的合适方法。这可能很简单:

#define CONST_STAR '*'
#define CONST_NEWLINE '\n'

static const char c_star CONST_STAR;
static const char c_newline CONST_NEWLINE;

那么你可以做:

switch(c) {
  case CONST_STAR:
    ...
    write(fd, &c_star, sizeof(c_star))
    ...
}

(另请注意,根据定义,sizeof(char)始终是一个。您可能已经知道这一点,但它并没有得到应有的广泛认可。)

【讨论】:

  • 我无法正确格式化#define 行。请就我做错的地方提出建议?
  • 感谢蒂姆的详细回复。我的问题不是由于对运行时变量值和编译时常量之间的差异的误解而产生的,而是寻找语法糖来给我一个编译时常量和一个包含该常量的运行时变量,并且工作量最少。
  • @Tim:代码格式在列表中被破坏。烦人,我知道。解决这个问题的方法是在代码前插入一个简单的段落。
【解决方案11】:

这是解决这个古老但仍然相关的问题的另一种方法

#define THIS_CONST 'A'

//I place this in a header file
static inline size_t write1(int fd, char byte)
{
    return write(fd, &byte, 1);
}

//sample usage
int main(int argc, char * argv[])
{
    char s[] = "Hello World!\r\n";
    write(0, s, sizeof(s));

    write1(0, THIS_CONST);

    return 0;
}

【讨论】:

    【解决方案12】:

    好的,我想出了一些技巧,仅用于字符 - 但我会把它放在这里看看它是否会激发其他人的任何更好的解决方案

    static const char *charptr(char c) {
        static char val[UCHAR_MAX + 1];
        val[(unsigned char)c] = c;
        return &val[(unsigned char)c];
    }
    
    ...
    
    write(fd, charptr(THIS_CONST), sizeof(char));
    

    【讨论】:

    • 对于初学者,您需要将 256 替换为 UCHAR_MAX。我不完全确定它是线程安全的,但它肯定会使用大量内存。 (猜测,不是我的反对意见)
    • 这太丑了,使用了一个幻数,在最坏的情况下浪费了 255 字节的完美内存,和 添加一个函数调用并返回到应该是一个微不足道的操作...
    • 很公平。我认为您错过了重点,我只是提出了这个概念。为了避免函数调用并使其成为线程安全的,您可以预先填充一个静态数组,然后只使用一个宏来获取数组中的正确偏移量。在大量大多数情况下,浪费 256 字节的内存不是问题。是的,它应该是 (UCHAR_MAX+1),这很公平——这只是一个快速的技巧。
    • 当您在每次调用时重新初始化值时,使用 数组 的意义何在?为什么不只是一个static char val 变量?那个静态数组有什么意义,你能解释一下吗?
    • ..实际上,我刚刚测试了代码,它工作正常(非多线程)。在后续调用中没有指针被不同的值覆盖。人们将其描述为“丑陋”和“怪物”——但实际上,每次你需要一个指向一个你知道将保持不变的值的指针时,这样的函数就不需要临时变量——这就是我所说的丑陋!无论如何,最好的解决方案是 Alek 的答案,它解决了所有数据类型的问题,无需函数调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-04
    • 2012-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-12
    相关资源
    最近更新 更多