tags:

views:

393

answers:

10

Suppose I have a constant defined in a header file

#define THIS_CONST 'A'

I want to write this constant to a stream. I do something like:

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

However, what I would like to do for brevity and clarity is:

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

Does anyone know of any macro/other trick for obtaining a pointer to a literal? I would like something which can be used like this:

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


Note: I realise I could declare my constants as static const variables, but then I can't use them in switch/case statements. i.e.

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

Unless there is a way to use a const variable in a case label?

A: 

There's no reason the compiler has to put the literal into any memory location, so your question doesn't make sense. For example a statement like

int a;
a = 10;

would probably just be directly translated into "put a value ten into a register". In the assembly language output of the compiler, the value ten itself never even exists as something in memory which could be pointed at, except as part of the actual program text.

You can't take a pointer to it.

If you really want a macro to get a pointer,

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

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

int main ()
{
    printf ("%d\n",GETAPOINTER('A'));
}
Kinopiko
If there's something wrong with my answer, please leave a comment to let me know what.
Kinopiko
Your answer makes no sense and you did not contribute anything to the actual problem, hence the downvotes.
Filip Ekberg
I understand a literal may not be ordinarily put into a memory location, for example it could be encoded directly into an instruction. However, I'm looking for a way to convenient _make_ the compiler put the literal into a memory location and give me a pointer to it, to make my code clearer and shorter.
James
Filip Ekberg
And since "char" actually is just a number, you won't have any "literals" in memory.
Filip Ekberg
Give the guy a break, his response is well inside the ballpark, +1
Chris McCauley
Kinopiko, your answer is just a little hard to read and understand, I think. The example you gave was so abstract that people may be having a hard time connecting it to James' problem. Also, notice that James already understood your point, and he made mention of using understanding the lack of memory storage in his question. So people may wish you has assumed more knowledge on his part. Nonetheless, your response was made in good faith and not incorrect, so I will give you an up vote also.
Heath Hunnicutt
It's ironic that I can post a 100% correct answer and pick up downvotes and comments like this, and post a 0% facetious, silly answer and get 62 upvotes for it.
Kinopiko
Never mind, Kinopiko. Your explanation absolutely makes sense, but only if you know Assembler. Not many people do these days. +1.
DevSolar
Thanks for your comment.
Kinopiko
+2  A: 

Since calling write() to write a single character to a file descriptor is almost certainly a performance killer, you probably want to just do fputc( THIS_CONST, stream ).

William Pursell
-1 That really isn't an answer to the question.
Kinopiko
Thanks, that's a good point, I hadn't thought of that. However, this does not extend to literal ints, floats, etc. Also, I can't actually use fputc in current situation - I didn't mention this in the question, but I am actually calling a function which is a wrapper around write(), and all data must pass through this wrapper function.
James
Sometimes answering the question doesn't solve the problem...
DevSolar
+2  A: 

The only way you can obtain a pointer is by putting the literal into a variable (your first code example). You can then use the variable with write() and the literal in switch.

DevSolar
Jeez. There *should* be a rule against downvotes-sans-comment...
DevSolar
Blah blah blah. I wrote this stupid comment because a comment is required in order to downvote.(I'm kidding! I didn't downvote, and comments are, of course, not required for a downvote).
JeffH
I was hoping for a macro to do the 'put the literal into variable and obtain pointer to it' part, but since no threadsafe solutions have been put forward, I'm accepting this as the simple answer to my question.
James
@ JeffH: I can understand you can't be bothered to explain why you downvoted a downright silly answer. But I don't think my answer qualifies as such, so it can't be "bad form", "bad language" or "doesn't apply at all", but someone took exception at the *content* of my answer. In such cases, I think a comment is in order...
DevSolar
+2  A: 
#define THIS_CONST 'a'

Is just a macro. The compiler basically just inserts 'a' everywhere you use THIS_CONST. You could try:

const char THIS_CONST = 'a';

But I suspect that will not work wither (don't have a c-compiler handy to try it out on, and it has been quite a few years since I've written c code).

sindre j
That's the best answer I've seen so far. It makes sense, solves the problem, and improves the code in other aspects, too.
sbi
sbi: except james himself already gave this solution in his question, and also gave the reason why he din't like it
Toad
Ah, I missed that. But now I wonder: C++ allows using consts as case labels. Doesn't C?
sbi
It doesn't. C doesn't have the weird C++ semantics where `const` mostly means "readonly", and sometimes means "compile-time literal constant". In C, it's always the former - a `const` variable is read-only from the point of initialization, but it is not a compile-time constant. Which is why all C libraries use `#define` for true constants.
Pavel Minaev
Thanks, Pavel, for the explanation!
sbi
+3  A: 

C simply does not allow the address of character literals like 'A'. For what it's worth, the type of character literals in C is int (char in C++ but this question is tagged C). 'A' would have an implementation defined value (such as 65 on ASCII systems). Taking the address of a value doesn't make any sense and is not possible.

Now, of course you may take the address of other kinds of literals such as string literals, for example the following is okay:

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

This is because the string literal "potato" is an array, and its value is a pointer to the 'p' at the start.

To elaborate/clarify, you may only take the address of objects. ie, the & (address-of) operator requires an object, not a value.

And to answer the other question that I missed, C doesn't allow non-constant case labels, and this includes variables declared const.

Chris Young
A: 

Ok, I've come up with a bit of a hack, for chars only - but I'll put it here to see if it inspires any better solutions from anyone else

static const char *charptr(char c) {
    static char val[256];
    val[(unsigned char)c] = c;
    return &val[(unsigned char)c];
}

...

write(fd, charptr(THIS_CONST), sizeof(char));
James
Why the downvote?
James
For starters, you'd want to replace 256 by UCHAR_MAX. I'm not entirely certain it's threadsafe, but it certainly uses a lot of memory. (guesses, not my downvote)
MSalters
It's ugly as hell, *and* using a magic number, *and* wasting 255 bytes of perfectly good memory worst-case, *and* adding a function call-and-return to what should be a next to trivial operation...
DevSolar
Fair enough. I think you missed the point though, I was just putting forward the concept. To avoid the function call and make it threadsafe you could prepopulate a static array and then just have a macro to get the correct offset into the array. And wasting 256 bytes of memory is not an issue in the _vast_ majority of cases. And yes it should be (UCHAR_MAX+1), fair enough - this was just a quick hack to make a point.
James
What was the point of using *an array*, when you re-initialize the value on every call anyway? Why not just a `static char val` variable? What was the point of that static array, can you explain?
AndreyT
It is not re-initializing the same location each time - the array index depends on the value itself. By using an array, the pointer returned by this function points to the correct value even if this function is called multiple times with different arguments. Simply using a `static char` would mean that this function would always return a pointer to the same location, which would contain the last value passed to this function. Re-initializing the value on every call was to avoid having to pre-fill the array.
James
A: 

I can see what you're trying to do here, but you're trying to use two fundamentally different things here. The crux of the matter is that case statements need to use values which are present at compile time, but pointers to data in memory are available only at run time.

When you do this:

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

you are doing two things. You are making the macro THIS_CONST available to the rest of the code at compile time, and you are creating a new char at runtime which is initialised to this value. At the point at which the line write(fd, &c, sizeof(c)) is executed, the concept of THIS_CONST no longer exists, so you have correctly identified that you can create a pointer to c, but not a pointer to THIS_CONST.

Now, when you do this: static const char THIS_CONST = 'A'; switch(c) { case THIS_CONST: // error - case label does not reduce to an integer constant ... }

you are writing code where the value of the case statement needs to be evaluated at compile time. However, in this case, you have specified THIS_CONST in a way where it is a variable, and therefore its value is available only at runtime despite you "knowing" that it is going to have a particular value. Certainly, other languages allow different things to happen with case statements, but those are the rules with C.

Here's what I'd suggest:

1) Don't call a variable THIS_CONST. There's no technical reason not to, but convention suggests that this is a compile-time macro, and you don't want to confuse your readers.

2) If you want the same values to be available at compile time and runtime, find a suitable way of mapping compile-time macros into run-time variables. This may well be as simple as:

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

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

Then you can do:

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

(Note also that sizeof(char) is always one, by definition. You may know that already, but it's not as widely appreciated as perhaps it should be.)

Tim
I can't the #define lines formatted correctly. And advice on what I'm doing wrong, please?
Tim
Thanks for your detailed response Tim. My question wasn't arising from a misconception of the difference between values of variables at runtime and compile time constants, but rather looking for syntactic sugar to give me both a compile time constant and a runtime variable containing that constant with minimal leg-work.
James
@Tim: Code formatting is broken within lists. Annoying, I know. The way to get around this is to insert a simple paragraph before the code.
sbi
Thanks. That fixed it.
Tim
A: 

For chars, you may use extra global static variables. Maybe something like:

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

write(fd,PTR_TO(THIS_CONST),sizeof(char));
eran
Yes, this is the sort of thing I was thinking of - only it's not threadsafe.
James
+3  A: 

There is no way to do this directly in C89. You would have to use a set of macros to create such an expression.

In C99, it is allowed to declare struct-or-union literals, and initializers to scalars can be written using a similar syntax. Therefore, there is one way to achieve the desired effect:

#include <stdio.h>

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

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