Passing a pointer (string) to a C function - c

Please read until the end before you say: "Oh no, this question again..."
I am right now seating in a C course and the following example has been provided in course book:
#include <stdio.h>
#include <stdint.h>
void Terminal_PrintData(uint16_t * const Data);
int main(void){
uint16_t StringData[] = "MyData";
Terminal_PrintData(StringData);
}
void Terminal_PrintData(uint16_t * const Data)
{
printf("Value: %s", *Data);
}
When I compile this masterpiece, this is what I get:
F:\AVR Microcontroller>gcc -o test test.c
test.c: In function 'main':
test.c:7:26: error: wide character array initialized from non-wide string
uint16_t StringData[] = "MyData";
My questions are:
Is it correct to declare a string with uint16_t?
What is recommended way to pass a string to a function?

Your immediate questions:
Is it correct to declare a string with uint16_t?
No.
All strings are always char[]. There ar also wide strings (strings of wide characters), which have the type wchar_t[], and are written with an L prefix (e.g. L"hello").
What is recommended way to pass a string to a function?
As a pointer to the first character in the string, so either char * or wchar_t *. The const qualifier makes no sense here; it would mean that the pointer (not the string itself) is constant (see here). I'd recommend writing wchar_t const * or const wchar_t * instead; it has a different meaning (i.e. the string can't be changed), but it's correct and meaningful.
You mention that this code is from a course book. I'm not sure whether you mean that these are lecture notes handed out by your professor, or whether you bought a published book. In either case, if this snippet is representative of the quality of the book/notes, get a refund.
For now, let's make this code work.
There's an error in this code that will cause the program to crash:
printf("... %s ...", ..., *string, ...) when string is a char* is always wrong. Don't dereference that pointer.
If you insist on using "wide" characters (which is questionable), you're going to have to change a few things:
Rather than using char, you need to include <wchar.h> and use wchar_t instead. As far as I know, uint16_t and uint8_t won't work unless you use explicit casting everywhere. That's because char doesn't have 8 bits, but CHAR_BIT bits.
Wide character literals must start with an L prefix.
Rather than using printf, you need to use wprintf.
In the format string, use %ls rather than %s. (Unless you use Microsoft's compiler.)
Finally, some less grave errors:
As noted, T * const arguments are useless. The author probably meant T const * (or equivalently const T *).
You can remove <stdint.h> and <stdio.h>. We're no longer using uint16_t, and <wchar.h> declares some wide character <stdio.h>-like functions.
I end up with the following code:
#include <wchar.h>
void Terminal_PrintData(wchar_t const * Data);
int main(void){
wchar_t StringData[] = L"MyData";
Terminal_PrintData(StringData);
}
void Terminal_PrintData(wchar_t const * Data)
{
wprintf(L"Value: %ls", Data);
}
This compiles and runs as expected with both GCC and Clang on Linux x86-64.

There are two kinds of strings: "non-wide" ones which consist of chars and "wide" ones which consist of wchar_ts and are written as L"" (single wchar_ts can be written as L''). There are functions to convert between them; apart from these, you cannot intermix them.
Depending on the system, a wchar_t can be 16 or 32 bits wide.

You should view your string as an array of characters (in this case, 8-bit characters) so a uint8_t would suffice. What you normally do is pass the beginning of the string (which is the pointer to the array) to your function. To make it safer, you could also pass the length of the string as an argument, but normally your string will end with a delimiter (\0).
when you pass stringData to your function, you're actually saying &stringData[0], literally "the address (&) of the first element ([0]) of the array".

Related

C Aligning string literals for a specific use case

I'm trying to align string literals in a specific way, as how I'm using it in my code is fairly specific. I don't want to have to assign it to a variable, for instance many of my functions are using it as a direct argument. And I want it to work both in local scope or global scope.
Usage example:
char *str = ALIGNED_STRING("blah"); //what I want
foo(ALIGNED_STRING("blah")); //what I want
_Alignas(16) char str[] = "blah"; //not what I want (but would correctly align the string)
The ideal solution would be (_Alignas(16) char[]){ "blah" } or a worser case using the GCC/Clang compiler extensions for alignment (__attribute__((alignment(16))) char[]){ "blah" }, but neither works (they're ignored and the default alignment for the type is used).
So my next thought was to align it myself, and then my functions that use the string could then fix it up correctly. e.g. #define ALIGNED_STRING(str) (char*)(((uintptr_t)(char[]){ "xxxxxxxxxxxxxxx" str } + 16 - 1) & ~(16 - 1)) (where the string containing 'x' would represent data needed to understand where the real string can be found, that's easy but just for the example assume the 'x' is fine). Now that works fine in local scope, but fails in the global scope. Since the compiler complains about it not being a compile-time constant (error: initializer element is not a compile-time constant); I would've thought it would work but it seems only addition and subtraction are valid operations on the pointer at compile-time.
So I'm wondering if there's anyway to achieve what I want to do? At the moment I'm just using the latter example (padding and manually aligning) and avoiding to use it in the global scope (but I would really want to). And the best solution would avoid needing to make runtime adjustments (like using the alignment qualifier would), but that doesn't seem possible unless I apply it to a variable (but as mentioned that's not what I want to do).
Was able to get close to OP's need with a compound literal. (C99)
#include <stdio.h>
#include <stddef.h>
void bar(const char *s) {
printf("%p %s\n", (void*)s, s);
}
// v-- compound literal --------------------------v
#define ALIGNED_STRING(S) (struct { _Alignas(16) char s[sizeof S]; }){ S }.s
int main() {
char s[] = "12";
bar(s);
char t[] = "34";
bar(t);
bar(ALIGNED_STRING("asdfas"));
char *u = ALIGNED_STRING("agsdas");
bar(u);
}
Output
0x28cc2d 12
0x28cc2a 34
0x28cc30 asdfas // 16 Aligned
0x28cc20 agsdas // 16 Aligned

Pass char* to method expecting unsigned char*

I am working on some embedded device which has SDK. It has a method like:
MessageBox(u8*, u8*); // u8 is typedefed unsigned char when I checked
But I have seen in their examples calling code like:
MessageBox("hi","hello");
passing char pointer without cast. Can this be well defined? I am asking because I ran some tool over the code, and it was complaining about above mismatch:
messageBox("Status", "Error calculating \rhash");
diy.c 89 Error 64: Type mismatch (arg. no. 1) (ptrs to signed/unsigned)
diy.c 89 Error 64: Type mismatch (arg. no. 2) (ptrs to signed/unsigned)
Sometimes I get different opinions on this answer and this confuses me even more. So to sum up, by using their API the way described above, is this problem? Will it crash the program?
And also it would be nice to hear what is the correct way then to pass string to SDK methods expecting unsigned char* without causing constraint violation?
It is a constraint violation, so technically it is not well defined, but in practice, it is not a problem. Yet you should cast these arguments to silence these warnings. An alternative to littering your code with ugly casts is to define an inline function:
static inline unsigned char *ucstr(const char *str) { return (unsigned char *)str; }
And use that function wherever you need to pass strings to the APIs that (mistakenly) take unsigned char * arguments:
messageBox(ucstr("hi"), ucstr("hello"));
This way you will not get warnings while keeping some type safety.
Also note that messageBox should take const char * arguments. This SDK uses questionable conventions.
The problem comes down to it being implementation-defined whether char is unsigned or signed.
Compilers for which there is no error will be those for which char is actually unsigned. Some of those (notably the ones that are actually C++ compilers, where char and unsigned char are distinct types) will issue a warning. With these compilers, converting the pointer to unsigned char * will be safe.
Compilers which report an error will be those for which char is actually signed. If the compiler (or host) uses an ASCII or similar character set, and the characters in the string are printable, then converting the string to unsigned char * (or, better, to const unsigned char * which avoids dropping constness from string literals) is technically safe. However, those conversions are potentially unsafe for implementations that use different character sets OR for strings that contain non-printable characters (e.g. values of type signed char that are negative, and values of unsigned char greater than 127). I say potentially unsafe, because what happens depends on what the called function does - for example does it check the values of individual characters? does it check the individual bits of individual characters in the string? The latter is, if the called function is well designed, one reason it will accept a pointer to unsigned char *.
What you need to do therefore comes down to what you can assume about the target machine, and its char and unsigned char types - and what the function is doing with its argument. The most general approach (in the sense that it works for all character sets, and regardless of whether char is signed or unsigned) is to create a helper function which copies the array of char to a different array of unsigned char. The working of that helper function will depend on how (and if) you need to handle the conversion of signed char values with values that are negative.

How to tell if an optional argument was passed to a function C

Edit 3: For the code itself all together check the first answer or the end of this post.
As stated in the title I'm trying to find a way to tell if an optional argument was passed to a function or not. What I'm trying to do is something like how almost all dynamic languages handle their substring function. Below is mine currently, but it doesn't work since I don't know how to tell if/when the thing is used.
char *substring(char *string,unsigned int start, ...){
va_list args;
int unsigned i=0;
long end=-1;
long long length=strlen(string);
va_start(args,start);
end=va_arg(args,int);
va_end(args);
if(end==-1){
end=length;
}
char *to_string=malloc(end);
strncpy(to_string,string+start,end);
return to_string;
}
Basically I want to still be able to not include the length of the string I want back and just have it go to the end of the string. But I cannot seem to find a way to do this. Since there's also no way to know the number of arguments passed in C, that took away my first thought of this.
Edit:
new way of doing it here's the current code.
#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
#define substring_defarg_2 (0)
char *substring(char *string,unsigned int start, int end){
int unsigned i=0;
int num=0;
long long length=strlen(string);
if(end==0){
end=length;
}
char *to_string=malloc(length);
strncpy(to_string,string+start,end);
return to_string;
}
and then in a file I call test.c to see if it works.
#include "functions.c"
int main(void){
printf("str:%s",substring("hello world",3,2));
printf("\nstr2:%s\n",substring("hello world",3));
return 0;
}
functions.c has an include for functions.h which includes everything that is ever needed. Here's the clang output(since clang seems to usually give a bit more detail.
In file included from ./p99/p99.h:1307:
./p99/p99_generic.h:68:16: warning: '__error__' attribute ignored
__attribute__((__error__("Invalid choice in type generic expression")))
^
test.c:4:26: error: called object type 'int' is not a function or function
pointer
printf("\nstr2:%s\n",substring("hello world",3));
^~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from test.c:1:
In file included from ./functions.c:34:
In file included from ./functions.h:50:
./string.c:77:24: note: instantiated from:
#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
GCC just says the object is not a function
Edit 2: Note that setting it to -1 doesn't change it either, it still throws the same thing. The compile options I'm using are as follows.
gcc -std=c99 -c test.c -o test -lm -Wall
Clang is the same thing(whether or not it works with it is another question.
ANSWER HERE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include "p99/p99.h"
#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
#define substring_defarg_2() (-1)
char *substring(char *string, size_t start, size_t len) {
size_t length = strlen(string);
if(len == SIZE_MAX){
len = length - start;
}
char *to_string = malloc(len + 1);
memcpy(to_string, string+start, len);
to_string[len] = '\0';
return to_string;
}
You will need p99 from there. It is by the selected answer. Just drop into your source directory and you should be OK. Also to summarize his answer on the license. You're able to use it however you want, but you cannot fork it basically. So for this purpose you're free to use it and the string function in any project whether proprietary or open source.
The only thing I ask is that you at least give a link back to this thread so that others who happen upon it can learn of stack overflow, as that's how I do my comments for things I've gotten help with on here.
In C, there's no such thing as an optional argument. The common idiom for situations like this is to either have two functions; substr(char *, size_t start, size_t end) and substr_f(char *, size_t start) or to have a single function where end, if given a special value, will take on a special meaning (such as in this case, possibly any number smaller than start, or simply 0).
When using varargs, you need to either use a sentinel value (such as NULL) at the end of the argument list, or pass in as an earlier argument the argc (argument count).
C has a very low amount of runtime introspection, which is a feature, not a bug.
Edit: On a related note, the correct type to use for string lengths and offsets in C is size_t. It is the only integer type that is guaranteed to be both large enough to address any character in any string, and guaranteed to be small enough to not be wasting space if stored.
Note too that it is unsigned.
Other than common belief functions with optional arguments can be implemented in C, but va_arg functions are not the right tool for such a thing. It can be implemented through va_arg macros, since there are ways to capture the number of arguments that a function receives. The whole thing is a bit tedious to explain and to implement, but you can use P99 for immediate use.
You'd have to change your function signature to something like
char *substring(char *string, unsigned int start, int end);
and invent a special code for end if it is omitted at the call side, say -1. Then with P99 you can do
#include "p99.h"
#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
#define substring_defarg_2() (-1)
where you see that you declare a macro that "overloads" your function (yes this is possible, common C library implementations use this all the time) and provide the replacement with the knowledge about the number of arguments your function receives (3 in this case). For each argument for which you want to have a default value you'd then declare the second type of macro with the _defarg_N suffix, N starting at 0.
The declaration of such macros is not very pretty, but tells at least as much what is going on as the interface of a va_arg function would. The gain is on the caller ("user") side. There you now can do things like
substring("Hello", 2);
substring("Holla", 2, 2);
to your liking.
(You'd need a compiler that implements C99 for all of this.)
Edit: You can even go further than that if you don't want to implement that convention for end but want to have two distinct functions, instead. You'd implement the two functions:
char *substring2(char *string, unsigned int start);
char *substring3(char *string, unsigned int start, unsigned int end);
and then define the macro as
#define substring(...) \
P99_IF_LT(P99_NARG(__VA_ARGS__, 3)) \
(substring2(__VA_ARGS__)) \
(substring3(__VA_ARGS__))
this would then ensure that the preprocessor chooses the appropriate function call by looking at the number of arguments it receives.
Edit2: Here a better suited version of a substring function:
use the types that are semantically correct for length and stuff like
that
the third parameter seems to be a length for you and not the end of the string, name it accordingly
strncpy is almost never the correct function to chose, there are situations where it doesn't write the terminating '\0' character. When you know the size of a string use memcpy.
char *substring(char *string, size_t start, size_t len) {
size_t length = strlen(string);
if(len == SIZE_MAX){
len = length - start;
}
char *to_string = malloc(len + 1);
memcpy(to_string, string+start, len);
to_string[len] = '\0';
return to_string;
}
Unfortunately, you cannot use va_arg like that:
Notice also that va_arg does not determine either whether the retrieved argument is the last argument passed to the function (or even if it is an element past the end of that list). The function should be designed in such a way that the amount of parameters can be inferred in some way by the values of either the named parameters or the additional arguments already read.
A common "workaround" is to give the other "overload" a nice mnemonic name, such as right_substr. It will not look as fancy, but it will certainly run faster.
If duplicating implementation is your concern, you could implement left_substr, substring, and right_substr as wrappers to a hidden function that takes start and length as signed integers, and interprets negative numbers as missing parameters. It is probably not a good idea to use this "convention" in your public interface, but it would probably work fine in a private implementation.
In standard C, when using variable argument prototypes (...), there is no way to tell directly how many arguments are being passed.
Behind the scenes, functions like printf() etc assume the number of arguments based on the format string.
Other functions that take, say, a variable number of pointers, expect the list to be terminated with a NULL.
Consider using one of these techniques.

"Incompatible pointer type" compiler warning for 4th argument of qsort

I'm trying to use the standard library's qsort to sort an array of wide characters:
wchar_t a = L'a';
wchar_t a1 = L'ä';
wchar_t b = L'z';
wchar_t chararray[] = {b, a, a1};
length = wcslen(chararray);
qsort(chararray, length, sizeof(wchar_t), wcscoll);
Now I think the functions involved have these prototypes:
int wcscoll(const wchar_t *ws1, const wchar_t *ws2);
void qsort(void *base, size_t num, size_t size, int (*comp_func)(const void *, const void *))
The results are completely as expected, but why am I getting the compiler warning "passing argument 4 of ‘qsort’ from incompatible pointer type"? And how can I cast wcscoll to fit the prototype?
The warning goes away if I define and pass in a separate comparison function:
int widecharcomp(const void *arg1, const void *arg2)
{
return wcscoll(arg1, arg2);
}
... but this one looks like it should have error handling for when the arguments are not of type wchar_t *.
You've done pretty much the right way. The gcc documentation for strcoll and wcscoll gives an example similar to this as the correct way to use strcoll or wcscoll with qsort.
/* This is the comparison function used with qsort. */
int
compare_elements (char **p1, char **p2)
{
return strcoll (*p1, *p2);
}
/* This is the entry point---the function to sort
strings using the locale's collating sequence. */
void
sort_strings (char **array, int nstrings)
{
/* Sort temp_array by comparing the strings. */
qsort (array, nstrings,
sizeof (char *), compare_elements);
}
This example actually does raise the warning that you want to get rid of, but again it can be gotten around by changing the char** to const void* in the arguments to compare_elements, and then explicitly casting to const char**.
You're right in observing that this is type-unsafe, but type safety is not exactly one of C's strong points. C doesn't have anything like generics or templates, so the only way that qsort can work on an arbitrary type is for its comparison function to accept void*s. It's up to the programmer to make sure that the comparison function is not used in a context where it may be passed arguments that are not the expected type.
That said, there is an error in your code. What the comparison function receives is not the elements to be compared, but rather pointers to the elements to be compared. So if the elements are strings, that means pointer-to-pointer. So when you write
return wcscoll(arg1, arg2);
You are actually passing wscoll a wchar_t** when it expects a wchar_t*. The correct way to do this, while suppressing the warning, would be:
int widecharcomp(const void *arg1, const void *arg2)
{
return wcscoll(*(const w_char_t**)arg1, *(const w_char_t**)arg2);
}
as ugly as that is.
Edit:
Just took another look at the top bit of your code. Your error is really twofold here. You're trying to use wcscoll to sort characters. It's a function meant to sort strings (which in C are pointers to nul-terminated sequences of characters). The above was written assuming you were trying to sort strings. If you want to sort characters, then wcscoll is not the appropriate function to use, but everything above regarding qsort still applies.
There are two problems: you've mixed up wchar_t and wchar_t*, and you've tried to pass off a wchar_t* as a void*.
First, you've told qsort to sort an array of wchar_t. But wcscoll doesn't compare wchar_t, it compares wide character strings which have the type wchar_t*. The fact that your comparison appears to have worked is due to your test data which just happens to work well under both interpretations.
If you wanted to sort characters, you need to call an appropriate function (I don't know the wide character API well enough to tell you which one). If you wanted to sort strings, you need to allocate an array of strings (of type wchar_t *).
Furthermore, even if you had an array of wchar_t*, you could not portably pass wcscoll as an argument to qsort. The issue is that there is no guarantee that wchar_t* and void* have the same representation. Some machines have word pointers that have a different representation from byte pointers; on such a machine, qsort would pass byte pointers to elements of the array to wcscoll, and this wouldn't work because wcscoll expects byte pointers. The solution is to write a trivial wrapper function that performs the conversion if necessary. A trivial wrapper is often necessary with qsort.
You've coded up your solution already (however, see other answers and edits at the end of this one about with the choice of the comparison function you're using and the data being passed to qsort()).
You could drop the wrapper function by casting the function pointer you pass to qsort() to the appropriate type, but I think using a wrapper is a better solution from a maintainability perspective. If you really want to avoid a wrapper function (maybe you're running into a measurable running into perf issue), you can cast like so:
qsort(chararray, length, sizeof(wchar_t), (int(*)(const void*,const void*))wcscoll);
Or make it arguably more readable using a typedef for the compare function type:
typedef
int (*comp_func_t)(const void *, const void *);
/* ... */
qsort(chararray, length, sizeof(wchar_t), (comp_func_t) wcscoll);
Unfortunately, the straight C qsort() can't be typesafe, so it can't have have "error handling for when the arguments are not of type wchar_t". You, the programmer, are responsible for ensuring that you're passing the correct data, sizes and comparison function to qsort().
Edit:
To address some of the problems mentioned in other answers about the types being passed ot the compare function, here's a routine that can be used to sort wchar_t using the current locale's collating sequence. The library might have something better, but I'm not aware of it at the moment:
int wchar_t_coll( const void* p1, const void* p2)
{
wchar_t s1[2] = {0};
wchar_t s2[2] = {0};
s1[0] = * (wchar_t*)p1;
s2[0] = * (wchar_t*)p2;
return wcscoll( s1, s2);
}
Also note, that the chararray you're passing to wcslen() isn't properly terminated - you'll need a 0 at the end of the initializer:
wchar_t chararray[] = {b, a, a1, 0};
You can't cast a function pointer to a different type, your current solution is as good it gets

Pointer to literal value

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?
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;
}
These answers are all outdated, and apart from a comment nobody refers to recent language updates.
On a C99-C11-C17 compiler using a compound literal, http://en.cppreference.com/w/c/language/compound_literal, is possible to create
a pointer to a nameless constant, as in:
int *p = &((int){10});
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.
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.
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 ).
#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).
just use a string constant, which is a pointer to a character, and then only write 1 byte:
#define MY_CONST_STRING "A"
write(fd, MY_CONST_STRING, 1);
Note that the '\0' byte at the end of the string is not written.
You can do this for all sorts of constant values, just use the appropriate hex code string, e.g.
#define MY_CONST_STRING "\x41"
will give also the character 'A'. For multiple-byte stuff, take care that you use the correct endianness.
Let's say you want to have a pointer to a INT_MAX, which is e.g. 0x7FFFFFFF on a 32 bit system. Then you can do the following:
#define PTR_TO_INT_MAX "\xFF\xFF\xFF\x7F"
You can see that this works by passing it as a dereferenced pointer to printf:
printf ("max int value = %d\n", *(int*)PTR_TO_INT_MAX);
which should print 2147483647.
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));
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'));
}
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.)
Here is another way to solve this old but still relevant question
#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;
}
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[UCHAR_MAX + 1];
val[(unsigned char)c] = c;
return &val[(unsigned char)c];
}
...
write(fd, charptr(THIS_CONST), sizeof(char));

Resources