I'm having troubles when calling a function taking a pointer to a string as a parameter. I need to get an Element's name.
// method
void getStringFromCsv( char ** str );
Let me introduce the structures I'm working with (not written by me and part of a much bigger project, I can't modify them).
// typedefs
typedef char T_CHAR64[64];
typedef T_CHAR64 T_SYMBOL;
// generic element
typedef struct Element
{
T_SYMBOL name;
} T_Element;
// csv element
typedef struct CsvElement
{
Element * pElement;
int id;
} T_csvElement;
So, basically, I thought I would call the function like this :
T_Element * pData; // Not null, filled earlier
getStringFromCsv( &pData->pElement->name );
But this doesn't work (warning: passing argument 1 of ‘STR_toCsv’ from incompatible pointer type). I'm using gcc with NetBeans 6.8.
I tried many things...
T_SYMBOL foo = "foo";
T_SYMBOL * pFoo = &foo;
getStringFromCsv( pDef->name, &pFoo ); // error : passing from incompatible pointer type
T_CHAR * pBar = &foo; // error : init from incompatible pointer type
T_CHAR * pBaz = &(foo[0]); // OK
getStringFromCsv( pDef->name, &pBaz ); // OK
T_SYMBOL * pFooTest = &(foo[0]); // error : init from incompatible pointer type
...but ended up casting name to a char ** :
getStringFromCsv( (char**) &pData->pElement->name );
What is wrong with my code ?
Basically, SYMBOL = CHAR *, right ? Why is SYMBOL* != CHAR** ?
I'm pretty sure I'm missing something simple but right now... Nothing came.
EDIT
Here is getStringFromCsv :
void getStringFromCsv( char ** data )
{
// pDesc is defined and not null
csvDescriptorCat( pDesc, *data);
csvDescriptorCat( pDesc, "\t");
}
void csvDescriptorCat( CsvDescriptor * pDesc, char* str)
{
int len;
if( str != NULL)
{
len = strlen(str);
strcpy( &pDesc->line[pDesc->pos], str);
pDesc->pos += len;
}
}
If you wish to pass &pData->pElement->name to the function, the function must be declared as:
void getStringFromCsv(T_SYMBOL * str);
Alternatively you can use a temporary char * as Secure offered - but there's not much point in doing this, because any updates to that char *'s value can't be used - the ->name member can't be modified, as it's an array.
You might as well just declare the function as:
void getStringFromCsv( char * str );
...and call it as:
getStringFromCsv( pData->pElement->name );
(In this case, the function can still change the contents of the ->name array. What you can't do is to change the position of the array itself).
As well as Secure's option, there's another way if your compiler supports C99 compound literals:
getStringFromCsv( &(char *){ pData->pElement->name } );
name is an array of chars, so &name gives you a pointer to char[64], as Vicky already answered. But casting makes things worse, because it tells the compiler to treat the first chars of the array as a pointer to the real array.
See the C-FAQ: http://c-faq.com/aryptr/aryptr2.html
I think you can use a temporary char* here:
char *tmp = pData->pElement->name; // array decays to pointer
getStringFromCsv(&tmp);
If this is expected by the function. Expecting a char**, make sure that it doesn't try to reallocate the memory. For simply filling it, a char* would be enough.
Alas, one of the little secrets of C that people fail to tell you, an array is not the same thing as a pointer. if x is defined as int x[5] or whatever, &x == x. Try out this code below:
#include <stdio.h>
int main(int argc, const char *argv[])
{
char x [5];
char *y;
printf("%08x\n", x);
printf("%08x\n", &x);
printf("%08x\n", y);
printf("%08x\n", &y);
return 0;
}
Considering this : http://c-faq.com/decl/strlitinit.html
char a[4] = "hello";
char* p = "hello";
Aren't the same thing (even if they seem to be).
So my SYMBOL and CHAR* cannot be exchanged, right ?
Is there a workaround, or another solution ?
Yes, under the covers T_SYMBOL is handled like a char *. But you've declared it as a char[64], so you're passing in a pointer to a char[64] not a pointer to a pointer to a char. The compiler is keeping track of that for you.
Personally in this situation I would just cast it as you did at the end of your question.
Related
This question already has answers here:
How do I modify a pointer that has been passed into a function in C?
(7 answers)
Closed 1 year ago.
I have a question about pointer.
Recently, I was looking some system programming tutorial. The teacher is talking about asprintf() Function.
According to the man page, the definition of asprintf() is:
int asprintf(char **ret, const char *format, ...);
However, I found that the teacher tends to create a single pointer and then pass the address of that single pointer to the first parameter. Like this:
int main()
{
char *buffer;
int r;
r = asprintf(&buffer,"The total is %d\n",5+8);
puts(buffer);
printf("%d characters generated\n",r);
return(0);
}
I am wondering why don't we declare double pointer and then pass that double pointer to the function, just like the definition.
In addition, the teacher use the same tricks in other function too, such as: getline()
Therefore, is there any advantage that we would choose single pointer over double pointer, even the definition of the function is double pointer?
Thanks for everyone for replying in advanced. :))
asprintf will allocate some memory and write the printf result into that memory.
To get this resulting memory buffer, you pass a pointer to a local variable (in your case: buffer) as the first argument. This pointer is then dereferenced in the function, to change the value of your local variable, so the code in asprintf semantically does something like this:
int asprintf(char **ret, const char *format, ...) {
*ref = malloc(some_size);
sprintf(*ret, format, ...);
}
What would now happen if you didn't have a local variable of type char * and passed a pointer to this variable, but instead had a local variable of type char **, like you suggested:
char **buffer;
asprintf(buffer, "The total is %d\n", 5+8);
Well, asprintf would again just dereference this pointer you just passed. But in this code, it doesn't yet point to anything meaningful, so this would result in an error. We must have enough memory allocated at the address we are passing to asprintf so it can store a char* there for us.
Now, this would work:
char *buffer;
char **pointer_to_buffer = &buffer;
asprintf(pointer_to_buffer, "The total is %d\n", 5+8);
but it is just more complicated and verbose.
But why do we do this whole pointer-to-pointer thing? Well, you would certainly first try to simply pass the pointer, instead of a pointer-to-pointer, like this:
char *buffer;
asprintf(buffer, "The total is %d\n", 5+8);
with asprintf semantically doing something like this:
int asprintf(char *ret, const char *format, ...) {
ref = malloc(some_size);
sprintf(ret, format, ...);
}
However, it is critical that here, buffer is passed by value. This means that when asprintf locally modifies ret, it modifies a copy of the value of our buffer. We, the caller, will not be able to see this change in our variable buffer. This is why we pass a pointer-to our local variable buffer, not the value itself.
I am wondering why don't we declare double pointer and then pass that double pointer to the function, just like the definition.
Because you want to update the value stored in buffer. The type of the expression &buffer is char **.
Remember, in order for a function to write to any of its parameters, you must pass a pointer to that parameter:
void foo( T *ptr )
{
*ptr = new_T_value(); // writes a new value of type T to the thing ptr points to
}
void bar( void )
{
T var;
foo( &var ); // writes a new value to var
}
Thus, the following are true:
ptr == &var // T * == T *
*ptr == var // T == T
IOW, writing to the expression *ptr is the same as writing to var.
So, why not just create a pointer variable in bar and pass it to foo? If we wrote something like
void bar( void )
{
T *vptr;
foo( vptr );
}
the problem is that vptr doesn't point to anything meaningful - we have
ptr == vptr // T * == T *
*ptr == *vptr == ??? // T == T == ???
There's no object of type T for us to update. Now, we could create another object for vptr to point to:
void bar( void )
{
T var;
T *vptr = &var;
foo( vptr );
}
and that will work as expected, but the extra pointer variable is redundant in this case.
The same logic holds for pointer objects - let's replace T with a pointer type, P *. Then our code becomes:
void foo( P * *ptr ) // or P **ptr
{
*ptr = new_P_star_value(); // writes a new value of type P * to the thing ptr points to
}
void bar( void )
{
P * var; // or P *var
foo( &var ); // writes a new value to var
}
which gives us
ptr == &var
*ptr == var == some_P_star_value
**ptr == *var == *some_P_star_value == some_P_value
Same thing as above - we want to write a new value to var. To do that we pass a pointer to it, even though var is already a pointer type. This is why multiple indirection exists in the first place.
Most of the time when you see a function that has a pointer or pointer-to-pointer parameter, it's expecting you to pass the address of another object (obtained with the & operator), not a pointer variable as such.
If you want to change an object used as an argument in a function you need to pass it by reference.
In C passing by reference means passing an object indirectly through a pointer to it. Thus dereferencing the pointer a function can get a direct access to the original pointer.
Consider this demonstrative program and compare the result of calls of the two functions f and g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void f( char *s )
{
s = malloc( 14 );
strcpy( s, "Hello World!" );
}
void g( char **ps )
{
*ps = malloc( 14 );
strcpy( *ps, "Hello World!" );
}
int main(void)
{
char *s = NULL;
f( s );
if ( s != NULL ) puts( s );
else puts( "s is a null pointer." );
g( &s );
if ( s != NULL ) puts( s );
else puts( "s is a null pointer." );
free( s );
return 0;
}
The program output is
s is a null pointer.
Hello World!
The function f accepts the pointer s declared in main by value. It means that the function deals with a copy of the value of the pointer s. Changing the copy does not affect the original pointer s. So the function produces a memory leak because it allocates dynamically a memory and the address of the allocated memory stored in the local variable (function parameter) s will be lost after exiting the function.
The function g accepts the pointer s by reference through a pointer to it. So dereferencing its parameter the function has a direct access to the original pointer s and can change it.
I have a function whose argument is const char *array[]
array[0] is the path, the rest are the arguments and it ends with NULL.
However, if I try to do execv(array[0], array) I get expected char * const* but argument is of type const char *
How do I go about this, and what is the difference between char * const* and const char *?
void start(const char *array[]) {
execv(array[0], array);
}
First, the error message is not copied correctly. If I run your code in GCC it shows this message instead (note the final *):
note: expected ‘char * const*’ but argument is of type ‘const char **’
which makes more sense as the message you show in the question, does not match the code you show. There is a mismatch in level or indirection.
That said, let's look at this part:
and what is the difference between char * const* and const char *?
Actually it is
and what is the difference between char * const* and const char **?
The first is a pointer to a const pointer to a char. The char that is pointed to is not const and might in theory be changed by execv.
The latter is a pointer to a pointer to a const char. This means, the char that is pointed to mustn't be modified. It might be some read-only string literal in ROM. If you pass such a pointer to a function that will try to modify it, it will fail in one way or the other. Therefore you are not allowed to pass a "pointer to const" to a function that does not expect it to be const.
That is what the compiler is telling you.
Now, how can you get rid of that warning...
To silence your compiler you could try to use some cast and cheat about real nature of that parameter.
In the end the problem will stay the same. A function trying to modify your read-only memory will not be working properly.
Instead you need to make a copy of your data:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void start(const char *array[]) {
int i = 0;
// determine number of strings (including NULL)
while (array[i++] != NULL) ;
// Create an array able to hold pointers to copys
char *my_array[i];
// Copy strings into non-const memory
i = 0;
do
my_array[i] = array[i] ? strdup(array[i]) : NULL;
while (array[i++] != NULL);
execv(my_array[0], my_array);
// Free the memory for the copied strings
i = 0;
do
free(my_array[i]);
while (array[i++] != NULL);
}
int main(void)
{
const char *argv[] = {"ls", "ls", NULL};
start(argv);
return 0;
}
I was studying "C complete reference" by Herbert Schildt and got stuck on the "const" explanation due by the pointer * he used at the same time with the const explanation.
here is the code he used:
#include <stdio.h>
void dash(const char *str);
int main()
{
dash("this is a test");
return 0;
}
void dash(const char *str)
{
while (*str)
{
if (*str == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", *str);
}
str++;
}
}
I've tried to search about the pointer * and got some answers about adresses but why did he use it in this example? His book didn't explain this and i haven't found other examples with this kinda use of pointer *.
Other question is, why is the loop "while (*str)" correct if it has no condition?
const char *str in a parameter declaration indicates that the function will not try to modify the values that the str pointer points to. This means that you can call the function with a constant string. If you don't have const in the declaration, it means that the function might modify the string, so you can only call it with writable strings.
As an example, a function like strcpy() declares has const on the second parameter (the source string), but not on the first parameter (the destination). It can (and usually does) modify the destination, but not the source.
Many people are confused when start learning C
const char *ptr
It is a pointer which is referencing the const char. The pointer can be modified. But is you try to write to the referenced object the compiler will complain: https://godbolt.org/z/d9znF-
Example:
const char c;
const char *ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- legal
to declare the constant pointer to the not constant object:
char * const ptr;
now ptr cannot be changed but the referenced object can: https://godbolt.org/z/h7WWex
char c;
char * const ptr = &c;
*ptr = 'p'; // -- legal
ptr++; // -- illegal - the compiler will complain
to declare const pointer to const object
const char * const ptr;
now the pointer and the referenced object cannot be modified: https://godbolt.org/z/x2xBcZ
const char c;
const char * const ptr = &c;
*ptr = 'p'; // -- illegal - the compiler will complain
ptr++; // -- illegal - the compiler will complain
It's a way of promising that the content the pointer is pointing at will not be altered. It's also a way of suppressing warnings without explicit casts.
Consider this:
void dash(char *str) // Removed const
{
// Code
}
int main() {
const char p[] = "this is a test";
dash(p);
}
Now the compiler will emit this:
k.c: In function ‘main’:
k.c:23:10: warning: passing argument 1 of ‘dash’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
23 | dash(p);
| ^
k.c:4:17: note: expected ‘char *’ but argument is of type ‘const char *’
4 | void dash(char *str)
| ~~~~~~^~~
Since you're not writing to it, this warning is nothing to worry about. But it's good practice to avoid warnings. In this case, we have two alternatives. Either the function may modify the string or it may not. If there's no way it will modify it, then there's no reason to explain to the compiler and the reader that this indeed is the case.
Sidenote. String literals, like "this is a test" has undefined behavior if you modify them, so the program might crash (or not). However, their type is is of type (char*) with no const. The reason is backwards compability. In C++, their type is const char*
Note that the const is a promise by convention, not by the compiler. This code will modify the original string and also compile without warnings:
#include <stdio.h>
void foo(const char *str)
{
// Casting comes with great responsibility
// You're just saying to the compiler
// "Trust me and shut up"
char *ptr = (char*) str;
ptr[2]='A';
ptr[3]='T';
}
int main()
{
const char p[] = "this is a test";
foo(p);
puts(p);
}
output:
$ ./a.out
thAT is a test
As I said, the above will compile without warning. If you remove the cast, you'll get this:
k.c:5:17: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
5 | char *ptr = str;
| ^~~
Do note that since p is declared as const this is undefined behavior. However, you instead write main like this:
int main()
{
char p[] = "this is a test";
foo(p);
puts(p);
}
then, the program is completely valid. And even though you pass a writable string to the function foo, you'd expect it to not change, since foo takes a constant pointer as argument. But as you can see, such things can be bypassed.
Be very careful with void pointers
Note that this is perfectly valid for ANY type T:
T x;
T *p;
p = (void*) &x;
This is because you can safely cast a pointer to void and back. However, this is NOT valid in the general case:
T x;
Q *p;
p = (void*) &x;
However, because of the cast, you will not get a warning. But this code invokes undefined behavior.
Moral lesson
Casting is NOT the goto solution for warnings. Instead, you should REALLY carefully consider if your cast match your intentions. If you're intentions here is to just get rid of the warning, the right solution is to remove the const for the parameter. If you're intentions with adding the cast is "I know that this function promises to not modify the argument, but I have good reasons for both promising that and then instantly break that promise" then a cast is correct.
Real world example
Just to give a real world example of how it can go wrong. I looked in this question where I saw this:
void * func_return();
void (*break_ptr)(void) = (void *)func_return;
I told OP that the cast is wrong. I got the response that without a cast, the compiler complained. Well, it complained because the pointer is WRONG. The function prototype declares a function taking an unspecified number of arguments and returning a void pointer. The function pointer is a pointer to a function taking NO arguments returning nothing. So in this case, the proper pointer declaration and initialization would be this:
void * func_return();
void *(*break_ptr)() = func_return;
But this would probably be better:
void * func_return(void);
void *(*break_ptr)(void) = func_return;
Note that since a pointer of any type can be safely cast to void* and back. But in this case OP was not casting it back, but to another type. If OP had done it correctly, the cast would just be clutter, but in this case it did hide the REAL error.
In c we can manipulate an array like a pointer with the right pointer arithmatic like he used and we can manipulate it like an array!
const char *str
is a pointer to const char OR an array of const char data types!
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer". And when you compare an array to something else, again it "decays into a pointer"
so we can write the while loop again in different way:
void dash(const char *str)
{
int i = 0;
while (str[i])
{
if (str[i] == ' ')
{
printf("%c", '-');
}
else
{
printf("%c", str[i]);
}
++i;
}
}
Now, the first syntax (with the pointer deref operator * is more effecient than array syntax).
in general array name or the address of the first array element (of any type), can decays to a pointer of the same data type!
In his implementation, he behaves the str as a const char pointer, in the while loop he is derefrence the pointer (like str[i], with the brackets) and in the last line (str++) he is moving the pointer to points to the next char element (which usualy knwon as pointer arithmetics).
In this case, read the definition from right to left:
const char *str // str is a pointer to a const char
The address of str can change while the char it points to cannot.
To answer you other question, while (*str) will continue to interate until *str == '\0'. '\0' is used to mark the end of a string in C.
What the program does, if you're unsure, is print it, replacing ' ' with '-'. In your example, "this-is-a-test" would be printed. Note: the string "this is a test" is not modified.
The * is related to pointers but it has two uses.
In the declaration, * is used to declare the pointer type, as in:
const char *str;
Where str is a pointer to a const char (or multiple const char stored in sequence, C doesn't care about the difference).
In an expression, * is used to dereference a pointer, get the value it points to. As in:
printf("%c", *str);
Where *str is that const char itself that the pointer str is pointing to.
Related to pointers, there's also & that does the other way around. It gets the pointer of any value you have stored in memory.
The importance of const here is not related to pointers, it's related to the fact you're passing a string literal to dash(). Unlike strings that are stored in the heap or the stack, string literals cannot be modified and should be treated as const for their immutability.
I have a function that takes a void** argument and an integer that indicates its datatype
void foo (void** values, int datatype)
Inside the function, depending on the datatype, I malloc it this way:
if (datatype == 1)
*values = (int*) malloc (5 * sizeof(int));
else if (datatype == 2)
*values = (float*) malloc (5 * sizeof(float));
All is good upto now. However, when character strings come into the picture, things get complicated. The void** would need to be void***, since I will need to do something like this:
*values = (char**) malloc (5 * sizeof(char*));
for(i=0;i<5;i++)
(*values)[i] = (char*) malloc (10);
..
strncpy( (*values)[0], "hello", 5);
How should such a situation be handled?
Can I pass a char*** to the function that expects a void** but cast it correctly inside it?
void foo (void** values, int datatype) {
if(datatype == 3) {
char*** tmp_vals = (char***) values;
*tmp_vals = (char**) malloc (5 * sizeof(char*));
...
(*tmp_vals)[i] = (char*) malloc (10 * sizeof(char));
strncpy ( (*tmp_vals)[i], "hello", 5);
}
So I just cast the void** into a char***. I tried this and ignoring the warnings, it worked fine.
But is this safe? Is there a more graceful alternative?
How should such a situation be handled? Can I pass a char*** to the function that expects a void** but cast it correctly inside it?
No, that's technically Undefined Behavior. It may appear to work on your computer, but it may fail on some future computer that implements different pointer types with different representations, which is allowed by the C language standard.
If your function expects a void**, then you better pass it a void**. Any pointer type can be implicitly converted to void*, but that only works at the top level: char* can be converted to void*, and char** can be implicitly converted to void* (because char** is "pointer to char*"), but char** cannot be converted to void**, and likewise char*** also cannot be converted to void**.
The proper way to call this function is to pass it a proper void**, then cast the resulting void* pointer back to its original type:
void foo(void **values, int datatype)
{
if(datatype == 3)
{
char ***str_values = ...;
*values = str_values; // Implicit cast from char*** to void*
}
else
...
}
...
void *values;
foo(&values, 2);
char ***real_values = (char ***)values;
Assuming that *values was actually pointed to a char***, then this cast is valid and does not have any Undefined Behavior in any of the code paths.
A void * is just a pointer to an unspecified type; it could be a pointer to an int, or a char, or a char *, or a char **, or anything you wanted, as long as you ensure that when you dereference, you treat it as the appropriate type (or one which the original type could safely be interpreted as).
Thus, a void ** is just a pointer to a void *, which could be a pointer to any type you want such as a char *. So yes, if you are allocating arrays of some types of objects, and in one case those objects are char *, then you could use a void ** to refer to them, giving you something that could be referred to as a char ***.
It's generally uncommon to see this construction directly, because usually you attach some type or length information to the array, rather than having a char *** you have a struct typed_object **foo or something of the sort where struct typed_object has a type tag and the pointer, and you cast the pointer you extract from those elements to the appropriate types, or you have a struct typed_array *foo which is a struct that contains a type and an array.
A couple of notes on style. For one, doing this kind of thing can make your code hard to read. Be very careful to structure it and document it clearly so that people (including yourself) can figure out what's going on. Also, don't cast the result of malloc; the void * automatically promotes to the type its assigned to, and casting the result of malloc can lead to subtle bugs if you forget to include <stdlib.h> or your update the type declaration but forget to update the cast. See this question for more info.
And it's generally a good habit to attach the * in a declaration to the variable name, not the type name, as that's how it actually parses. The following declares one char and one char *, but if you write it the way you've been writing them, you might expect it to declare two char *:
char *foo, bar;
Or written the other way:
char* foo, bar;
You don't need to (and probably shouldn't) use a void ** at all - just use a regular void *. Per C11 6.3.2.3.1, "a pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer." A pointer variable, including a pointer to another pointer, is an object. void ** is not "a pointer to void". You can convert freely and safely to and from void *, but you're not guaranteed to be able to convert safely to and from void **.
So you can just do:
void foo (void* values, int datatype) {
if ( datatype == 1 ) {
int ** pnvalues = values;
*pnvalues = malloc(5 * sizeof int);
/* Rest of function */
}
and so on, and then call it similar to:
int * new_int_array;
foo(&new_int_array, 1);
&new_int_array is of type int **, which will get implicitly converted to void * by foo(), and foo() will convert it back to type int ** and dereference it to indirectly modify new_int_array to point to the new memory it has dynamically allocated.
For a pointer to an dynamic array of strings:
void foo (void* values, int datatype) {
/* Deal with previous datatypes */
} else if ( datatype == 3 ) {
char *** psvalues = values;
*psvalues = malloc(5 * sizeof char *);
*psvalues[0] = malloc(5);
/* Rest of function */
}
and so on, and call it:
char ** new_string_array;
foo(&new_string_array, 3);
Similarly, &new_string_array is type char ***, again gets implicitly converted to void *, and foo() converts it back and indirectly makes new_string_array point to the newly allocated blocks of memory.
There is a builtin mechanism to do this already with the added bonus that it allows a variable number of arguments. It is commonly seen in this format yourfunc(char * format_string,...)
/*_Just for reference_ the functions required for variable arguments can be defined as:
#define va_list char*
#define va_arg(ap,type) (*(type *)(((ap)+=(((sizeof(type))+(sizeof(int)-1)) \
& (~(sizeof(int)-1))))-(((sizeof(type))+ \
(sizeof(int)-1)) & (~(sizeof(int)-1)))))
#define va_end(ap) (void) 0
#define va_start(ap,arg) (void)((ap)=(((char *)&(arg))+(((sizeof(arg))+ \
(sizeof(int)-1)) & (~(sizeof(int)-1)))))
*/
So here is a basic example that you could use with a format string and variable number of args
#define INT '0'
#define DOUBLE '1'
#define STRING '2'
void yourfunc(char *fmt_string, ...){
va_list args;
va_start (args, fmt_string);
while(*fmt_string){
switch(*fmt_string++){
case INT: some_intfxn(va_arg(ap, int));
case DOUBLE: some_doublefxn(va_arg(ap, double));
case STRING: some_stringfxn(va_arg(ap, char *));
/* extend this as you like using pointers and casting to your type */
default: handlfailfunc();
}
}
va_end (args);
}
So you can run it as: yourfunc("0122",42,3.14159,"hello","world");
or since you only wanted 1 to begin with yourfunc("1",2.17); It doesn't get much more generic than that. You could even set up multiple integer types to tell it to run a different set of functions on that particular integer. If the format_string is too tedious, then you can just as easily use int datatype in its place, but you would be limited to 1 arg (technically you could use bit ops to OR datatype | num_args but I digress)
Here is the one type one value form:
#define INT '0'
#define DOUBLE '1'
#define STRING '2'
void yourfunc(datatype, ...){ /*leaving "..." for future while on datatype(s)*/
va_list args;
va_start (args, datatype);
switch(datatype){
case INT: some_intfxn(va_arg(ap, int));
case DOUBLE: some_doublefxn(va_arg(ap, double));
case STRING: some_stringfxn(va_arg(ap, char *));
/* extend this as you like using pointers and casting to your type */
default: handlfailfunc();
}
va_end (args);
}
With some tricks, you can do it. See example:
int sizes[] = { 0, sizeof(int), sizeof(float), sizeof(char *) }
void *foo(datatype) {
void *rc = (void*)malloc(5 * sizes[datatype]);
switch(datatype) {
case 1: {
int *p_int = (int*)rc;
for(int i = 0; i < 5; i++)
p_int[i] = 1;
} break;
case 3: {
char **p_ch = (char**)rc;
for(int i = 0; i < 5; i++)
p_ch[i] = strdup("hello");
} break;
} // switch
return rc;
} // foo
In the caller, just cast returned value to appropriate pointer, and work with it.
I have a simple struct:
typedef struct {
void *things;
int sizeOfThings;
} Demo;
things is intended to contain an array of individual "thing", like maybe strings or ints.
I create a pointer to it:
Demo * Create(int value) {
Demo *d = malloc(sizeof(Demo));
if (d != NULL) {
d->sizeOfThings = value;
d->things = malloc(20 * value); // We'll have a max of 20 things
}
}
value is sizeof(int) for an array of ints, for example.
If in another function I want to insert something into d->things (assuming at least for not that I'm just adding it to the first slot, position management done elsewhere):
char * thing = "Me!";
strncpy(d->things[0], &thing, d->sizeOfThings);
I get around the strncpy area
test.c:10: warning: pointer of type ‘void *’ used in arithmetic
test.c:10: warning: dereferencing ‘void *’ pointer
test.c:10: error: invalid use of void expression
I'm just trying to understand the use of void* as a way to generalize my functions. I suspect there's something wrong with d->things[0].
According to the C standard, void has no size-- sizeof(void) is undefined. (Some implementations make it sizeof(int) but this is non-compliant.)
When you have an array of type foo, this expression:
array[3]
Adds 3*sizeof(foo) to the address stored in array and then deferences that. That's because the values are all packed together in memory. Since sizeof(void) is undefined, you can't do that for void arrays (in fact you can't even have void arrays, only void pointers.)
You must cast any void pointer to another pointer type before treating it as an array:
d->things = malloc(20 * sizeof(int));
(int *)(d->things)[0] = 12;
However, keep in mind that you don't even have to do that to use strncpy on it. Strncpy can accept a void pointer just fine. But you were using strncpy incorrectly. Your strncpy invocation should look like this:
strncpy(d->things, thing, d->sizeOfThings);
What your version would have done was try to treat the first array member of d->things as a pointer when it's not, and would have treated &thing, which is a char **, as if it were just a char *.
Try to see if this fixes your problem:
char *thing = "Me!";
strncpy(&d->things[0], thing, d->sizeOfThings);
Then, cast the pointers to get rid of the warnings, but you have to make sure what you're going to do
char *thing = "Me!";
strncpy((char *) &d->things[0], (const char *) thing, d->sizeOfThings);
Demo *d = malloc(sizeof(Demo));
if (d != NULL) {
d->things = malloc(20 * sizeOfThings); // We'll have a max of 20 things
}
What is sizeOfThings initialized to ? Probably it might have garbage and is causing the error. Even if it is initialized to 0 by default, then malloc returns NULL( malloc( 20 * 0 ) ; ). And so, I suspect -
strncpy(d->things[0], &thing, d->sizeOfThings);
// ^^^^^^^^^^ causing the error.
Two things:
Firstly, there's definitely something wrong with using d->things[0]. d->things is actually a pointer and the convention is that pointers and arrays are basically interchangeable (with a few exceptions) and the array name will always point to the first element of the array.
Secondly, the functional signature of strncpy is char* strncpy(char* destination, const char* source, size_t num);. So to make this work, we have to cast d->thing from void* to char* and make sure that we pass thing as a char* (just thing) vs. a char** (which is thing&).
so we want this statement instead:
strncpy((char*)d->things, thing, d->sizeOfThings);
Once the changes are in place, the rest of the code compiles and runs as expected.