指向字面值的指针。

25

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

#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))
注意:我意识到我可以将常量声明为静态常量变量,但这样我无法在 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变量?


1
唯一想到的是链接器技巧。您使用哪个编译器?如果您可以将一些绝对指针值引入到链接器命令中,例如(void *)0x00000004作为整数值“4”和字符使用。这不是可移植的。如果您在Windows上,请查看MS Visual C标准C库在初始化期间使用的C++构造函数列表名为.cxa-.cxz。这类似于我所说的链接器技巧。区别在于导致输出映像包含一些不可重定位地址,您可以将其强制转换为int。;) - Heath Hunnicutt
有趣的想法 - 这比我寻找的要复杂一些,但仍然很有趣。我正在使用gcc编译器,我的代码必须在Linux和Solaris下编译 :) - James
1
除了想在 case 语句中使用它之外,你还需要它的其他用途吗?在你下面的一个答案中,你说你也想将其应用于浮点数,但是无论如何你都不能将浮点数用作 case 语句表达式。 - Chris Young
关于浮点数的好处,但整数仍然适用。我唯一需要的是在一个case语句中使用它。然而,这个问题并不是因为“需要它”,而只是想看看是否可以通过一些C宏来实现(并且很清楚字面值与变量不同,并且不能获取其地址)。无论如何,感谢大家的贡献 - 我想简短的答案是“不行”,没有被接受的技巧可以使编译器将一个字面值放入内存位置并获取指向它的指针,也没有干净和线程安全的方法在运行时执行它。 - James
12个回答

45

在C89中没有直接实现这个的方法,你需要使用一组宏来创建这样的表达式。

在C99中,允许声明结构体或联合字面值,并且可以使用类似的语法编写标量的初始值。因此,有一种方式可以实现所需的效果:

#include <stdio.h>

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

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

1
我花了一些时间才弄明白这个问题,但是是的,这确实就是我所说的!谢谢! - James
9
这种构造被称为“复合字面量”(ISO/IEC 9899:TC3, p75, §6.5.2.5, ¶4)。我通过这个答案找到了这些信息。谢谢!我希望C99规范的参考对将来的某个人有所帮助! - Kohányi Róbert
不是,自C99以来可以声明复合文字而不是结构体或联合体字面量。 - chux - Reinstate Monica

15

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

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

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

6
接受的答案明确涉及C99,并使用了复合字面量(虽然可以改进为使用正确的术语“复合字面量”而不是“结构体或联合体字面量”)。 - James

5

你唯一能够获得指针的方式是将文本放入一个变量中(就像你的第一个代码示例)。然后,你可以在 write() 函数和 switch 语句中使用这个变量。


我本来希望有一个宏来完成“将文字放入变量并获取指向它的指针”的部分,但由于没有提出线程安全的解决方案,我接受这个作为我的问题的简单答案。 - James
1
@JeffH:我理解您可能不想解释为什么您会对一个非常愚蠢的回答进行投票。但我认为我的回答并不符合这种情况,因此它不能被归为“不当形式”、“不当语言”或“根本不适用”,但有人对我的回答内容提出了异议。在这种情况下,我认为有必要发表评论... - DevSolar

4

C语言不允许使用字符字面量(如'A')的地址。值得一提的是,在C语言中,字符字面量的类型为int(在C++中为char,但此问题标记为C)。'A'将具有实现定义的值(例如在ASCII系统上为65)。获取值的地址没有任何意义,也是不可能的。

当然,您可以获取其他类型的字面量的地址,例如字符串字面量,例如以下内容是可以的:

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

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

更详细地说,你只能获取对象的地址。也就是说,&(取地址)运算符需要一个对象,而不是一个值。

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


2
在 C 语言中,'A' 不是字面量,而是常量。C 语言有两种字面量:字符串字面量 和 _复合字面量_。这两种字面量都可以取地址,而常量 'A' 则不行。 - chux - Reinstate Monica

3

由于使用write()函数向文件描述符写入单个字符几乎肯定会影响性能,因此您可能只想使用fputc( THIS_CONST, stream )。


谢谢,这是一个很好的观点,我没有想到过。然而,这并不适用于字面整数、浮点数等。另外,在当前情况下,我实际上不能使用fputc - 我没有在问题中提到这一点,但我实际上正在调用一个包装器函数,该函数是write()的包装器,并且所有数据都必须通过此包装器函数传递。 - James

3
#define THIS_CONST 'a'

这只是一个宏。编译器会在你使用THIS_CONST时将'a'插入其中。你可以尝试:

const char THIS_CONST = 'a';

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


这是我迄今为止看到的最好的答案。它很有道理,解决了问题,并在其他方面也改进了代码。 - sbi
sbi:除了詹姆斯本人在他的问题中已经提供了这个解决方案,并且还给出了他不喜欢它的原因。 - Toad
啊,我错过了这个。但现在我想知道:C++允许将常量用作case标签。C语言不允许吗? - sbi
它并不是这样的。C语言没有奇怪的C++语义,其中“const”大多意味着“只读”,有时意味着“编译时字面常量”。在C中,它始终是前者 - 从初始化点开始,“const”变量是只读的,但它不是编译时常量。这就是为什么所有的C库都使用“#define”来表示真正的常量的原因。 - Pavel Minaev
谢谢 Pavel 的解释! - sbi

2

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

#define MY_CONST_STRING "A"

write(fd, MY_CONST_STRING, 1);

请注意,字符串末尾的'\0'字节不会被写入。
您可以针对各种常量值执行此操作,只需使用相应的十六进制代码字符串,例如:
#define MY_CONST_STRING "\x41"

将会同时给出字符'A'。对于多字节的东西,请注意使用正确的字节序。

假设你想要一个指向INT_MAX的指针,在32位系统上它可能是0x7FFFFFFF。那么你可以这样做:

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

你可以通过将它作为解引用指针传递给printf来查看它的工作原理:
printf ("max int value = %d\n", *(int*)PTR_TO_INT_MAX);

这应该打印出2147483647。


我喜欢这里的想法,我没想过在字符串中使用十六进制转义码。这是我看到的最好的解决方案,除了使用复合字面量。感谢您的建议。 - James
(int*)PTR_TO_INT_MAX 还有两个问题。将其转换为 int* 可能无法满足对齐要求。 "\xFF\xFF\xFF\x7F" 是端序敏感的。 - chux - Reinstate Monica

1

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

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

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

1
是的,这正是我想到的东西 - 只是它不是线程安全的。 - James

0

这里有另外一种解决这个经典但依然相关的问题的方式。

#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;
}

0

编译器没有必要将文字字面量放入任何内存位置,因此您的问题毫无意义。例如,像这样的语句

int a;
a = 10;

可能直接翻译为"将值十放入寄存器"。在编译器的汇编语言输出中,值10本身甚至从未作为内存中可指向的东西存在,除了实际程序文本的一部分。

您无法获取对它的指针。

如果您确实想要一个宏来获取指针,

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

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

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

如果我的答案有问题,请留下评论让我知道。 - user181548
你的回答毫无意义,也没有对实际问题做出任何贡献,因此被Downvote了。 - Filip Ekberg
我理解字面值通常不能被放置在内存位置中,例如它可以直接编码到指令中。然而,我正在寻找一种方便的方法来让编译器将字面值放置在内存位置中并给我一个指向它的指针,以使我的代码更清晰、更简短。 - James
给那个人一个机会,他的回应还算合理,+1。 - Chris McCauley
真讽刺,我可以发布一个100%正确的答案并获得负评和评论,而发布一个0%俏皮、愚蠢的答案却可以获得62个赞。 - user181548
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接