Call back programming w.r.t 'c' only, why we should use? - c

The point being saying w.r.t c only, as I am more comfortable in C.
I am not expecting a example which says this is how it works ... What I am expecting is why should we use the Call back function or some say it as function pointer.
I followed many blog and stack-overflow also, but not satisfied with any of those answers.
Let's say ( I am suggesting one scenario here, like sorting thing) we should use the call back thing, where a method/function will take more time for processing.
Let's say a process is there with one thread only, and the program is doing a sorting, which will take huge time ( let's assume > 1 min ). According to huge no of bloggers here we should use the function pointer. But how it would be useful ?
Any how we are having only one Program Counter and we will get some amount of time to process this process from CPU, then how it would be useful ?
If you think some other example is there to explain the function pointer concept please provide the example.
I saw some body suggesting like, if you will use function pointer, then the result u can collect later, but this sounds really awkward ! how is this even if possible ? How can u collect something from a function after returning from there ? the function would have been destroyed right !!!
In real time people use this for any change in events, so that they can get notification...( just adding a point )
I have seen some good programmer using this function pointer, I am dying to know why would I use this , surely there is something I am missing here...
Please reply, thanks in Advance.

Since there was still a bit of uncertianty in your last comment, perhaps a concrete example illustrating the points would help. Let's start with a simple example that takes a string as user input from the command line (you could prompt the user for input as well). Now let's say we want to give the user to option to tell us how they want to store the input. For purpose of this example, lets say the options are (1) to store the string normally, such that it prints on one line horizonally, (2) store the reverse of the string which will also print on one line, (3) store the string with newlines after each character so it prints vertically, and (4) store the string in reverse with embedded newlines.
In a normal approach to this problem, you would probably code a switch statement or a series of else if statements and pass the string to 4 different routines to handle the different cases. Function pointers allow you to approach the problem a little differently. Rather than 4 different input routines to handle each case, why not 1 input routine that takes a function pointer as it's argument and changes the way it handles the string based on the function passed as an argument. The input routine could be as simple as making a copy of the string (to prevent modifying argv[1], etc...) and then passing the string as an argument to a function represented by the pointer:
/* make copy of original string, pass to callback function */
char *input (char *d, char *s, char *(*cbf)(char *))
{
strcpy (d, s);
return (*cbf) (d);
}
The input function above takes as arguments the destination string d, the source string s and then a pointer to a funciton cbf (short for callback function). Let's look at the function pointer syntax quickly and digest what it tells us:
char *(*cbf)(char *)
| | \
return | argument
type | list
|
function pointer
name/label
Above, the function pointer named cbf has a return type of char *, and takes a single argument of type char *. (note: only the type is specified in the funciton pointer argument list, not both the type and argument name -- e.g. no char *str, just char *) Now that may seem like a lot to type each time you want to pass a function of that type as an argument or use it in an assignment. It is. There is an easy solution to reduce the typing required. You can use a typedef for the function pointer similar to how you use a typedef with a struct, etc. Creating a typedef of type cbf is equally easy:
typedef char *(*cbf)(char *);
The funciton pointer typedef above creates a type cbf that can be used in place of char *(*cbf)(char *) wherever the function pointer type is needed. When a typedef is used, you are relieved from specifying the return type and the argument list as well as not having to put the function pointer inside parenthesis. This reduces the original function declaration to:
char *input (char *d, char *s, cbf fname)
{
strcpy (d, s);
return fname (d);
}
Making use of a typedef for a function not only simplifies passing the functions as argument, but also simplifies creating arrays of funciton pointers as well. An array of funtion pointers can be used to simplify selecting and passing any one of a given number of functions, as needed. For our input function we create an array of function pointers each pointing to a different function that can be used to put the input string in the desired format. For example, let's say our 4 functions described above have declaration like this:
/* input processing callback functions */
char *hfwd (char *s);
char *hrev (char *s);
char *vfwd (char *s);
char *vrev (char *s);
note: each of the functions match our pointer definition of type char * and accept a single argument of type char *. Using our cbf typedef, we can easily create an array of function pointers called fnames as follows:
cbf fnames[] = { &hfwd, &hrev, &vfwd, &vrev };
The fnames array can then be used like any other array to select any one of our functions by array index. (e.g. fnames[0] is our function hfwd) This now gives us the ability to take a second input from our user, a number, to select the format for our input string. This provides the ability to use any one of our callback function by simply giving the array index for the desired function as the second argument on the command line. For example any one of the functions can be designated by calling out program with:
./progname my_string 1 /* to process the input with the hrev */
Now granted this example does not do much more than reformat a string, but from the standpoint of function pointer syntax, collecting function pointers in an array, and passing a function pointer as an argument to extend the capabilities of your code, it covers a great deal. Take a look at the following example, and let me know if you have any questions. (recall, the full function pointer syntax, in the absence of a typedef, is also included, but commented so you can compare/contrast typedef use)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXS 128
/* typedef for function pointer */
typedef char *(*cbf)(char *);
/* simple string reverse function */
char *strrevstr (char *str);
/* input processing callback functions */
char *hfwd (char *s);
char *hrev (char *s);
char *vfwd (char *s);
char *vrev (char *s);
/* input function, pointer to function will determine behavior */
// char *input (char *d, char *s, char *(*cbf)(char *));
char *input (char *d, char *s, cbf fn);
int main (int argc, char **argv) {
if (argc < 3 ) {
fprintf (stderr, "error: insufficient input, usage: %s string int\n", argv[0]);
return 1;
}
int idx = atoi(argv[2]);
if (idx > 3 || idx < 0) {
fprintf (stderr, "error: invalid input -- out of range, (0 !< %d !< 3)\n", idx);
return 1;
}
cbf fnames[] = { &hfwd, &hrev, &vfwd, &vrev };
// char *(*fnames[])(char *) = { &hfwd, &hrev, &vfwd, &vrev };
char string[MAXS] = {0};
input (string, argv[1], fnames[idx]);
printf ("\nProcessed input ('%s' '%s'):\n\n%s\n\n", argv[1], argv[2], string);
return 0;
}
/* strrevstr - reverse string, original is not preserved. */
char *strrevstr (char *str)
{
if (!str) {
printf ("%s() error: invalid string\n", __func__);
return NULL;
}
char *begin = str;
char *end = str + strlen (str) - 1;
char tmp;
while (end > begin)
{
tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
return str;
}
/* string unchanged - print horizontal */
char *hfwd (char *s)
{ return s; }
/* string reversed - print horizontal */
char *hrev (char *s)
{ return strrevstr (s); }
/* string unchanged - print vertical */
char *vfwd (char *s)
{
char *p = s;
static char buf[MAXS] = {0};
char *b = buf;
while (*p)
{
*b++ = *p++;
*b++ = '\n';
}
*b = 0;
b = buf;
while (*b)
*s++ = *b++;
*b = 0;
return buf;
}
/* string reversed - print vertical */
char *vrev (char *s)
{
char *p = strrevstr (s);
static char buf[MAXS] = {0};
char *b = buf;
while (*p)
{
*b++ = *p++;
*b++ = '\n';
}
*b = 0;
b = buf;
while (*b)
*s++ = *b++;
*b = 0;
return buf;
}
/* make copy of original string, pass to callback function */
char *input (char *d, char *s, cbf fn)
// char *input (char *d, char *s, char *(*cbf)(char *))
{
strcpy (d, s);
return fn (d);
// return (*cbf) (d);
}
Output
$ ( for i in {0..3}; do ./bin/fnc_pointer my_string $i; done )
Processed input ('my_string' '0'):
my_string
Processed input ('my_string' '1'):
gnirts_ym
Processed input ('my_string' '2'):
m
y
_
s
t
r
i
n
g
Processed input ('my_string' '3'):
g
n
i
r
t
s
_
y
m

Related

Null-terminating a list of arguments for a C variadic function

I'm messing around with variadic functions in C to learn how they work, and am trying to build a simple 'print lines' function without requiring manual counting of the lines. I'm doing this by wrapping the function in a macro that adds a null pointer to the end of a list of char * arguments, so the function can print line-by-line until a null argument is found.
I know I've avoided some common pitfalls, like forgetting to cast the null pointer in the argument list, but for whatever reason this code still isn't working. Calling the function with any number of parameters prints them properly, then fails to detect the null, prints a bunch of garbage data, and crashes.
int printline(const char *str) {
printf("%s\n", str);
}
#define printlines(...) _comments(__VA_ARGS__, (char*)0)
int _printlines(char* first, ...) {
if (first) {
printline(first);
va_list ptr;
va_start(ptr, first);
char *next;
do {
char *next = va_arg(ptr, char *);
if (next) {
printline(next);
}
} while(next);
va_end(ptr);
}
}
int main() {
printlines("hi");
//prints 'hi', then prints garbage data and crashes
printlines("how", "are", "you");
//prints 'how', 'are', and 'you', then prints garbage data and crashes
_printlines("help", (char *)0);
//prints 'help', then prints garbage data and crashes
_printlines("something", "is", "wrong", (char *)NULL);
//prints 'something', 'is', and 'wrong', then prints garbage data and crashes
}
If you take a look at this:
char* next;
do{
char* next = va_arg(ptr,char*);
if(next){ comment(next); }
}while(next);
You'll see that you have two separate variables called next, with the one inside of the do..while loop masking the one defined outside. You're assigning the result of va_arg to the inner next. Then when you get the while (next) condition, the inner next is out of scope and you're now reading the outer next which was never written to. This triggers undefined behavior.
You instead want:
char* next;
do{
next = va_arg(ptr,char*);
if(next){ comment(next); }
}while(next);
So that you only have a single variable called next that you're using.
Small rewrite. The macro has been modified with a +0 so it can take zero arguments.
#include <stdio.h>
#include <stdarg.h>
#define printlines(...) _printlines(__VA_ARGS__+0,(void*)0)
void _printlines(const char * first, ...)
{
const char * ptr;
va_list va;
va_start (va, first);
printf("---begin---\n");
for (ptr = first; ptr != NULL ; ptr = va_arg(va,char*) )
{
printf("%s\n", ptr);
}
printf("---end---\n");
va_end(va);
}
int main()
{
printlines(); // instead of: printlines(NULL);
printlines("hi");
printlines("how","are","you");
return 0;
}
Save time, enable all complier warnings.
warning: 'next' may be used uninitialized [-Wmaybe-uninitialized] } while(next); quickly gets to the key issue.
warning: control reaches end of non-void function [-Wreturn-type] in 2 places.
This is faster than posting on stack overflow.
The "rubbish" comes from not initialized object next. Another one next defined in the loop stops to exist when you exit the loop.
Removing strange functions and cleaning some mess you can get right.
int printline(const char* str){
printf("%s",str);
}
#define printlines(...) printlinesfunc(__VA_ARGS__,(char*)0)
int printlinesfunc(const char* first, ...){
if(first)
{
va_list ptr;
va_start(ptr,first);
char* next;
printline(first);
while((next = va_arg(ptr, char *)))
printline(next);
va_end(ptr);
}
}
int main(){
printlines("hi" , "\n");
printlines("how"," are"," you", "\n");
printlines("help", "\n");
printlines("something", " is", " wrong", "\n");
}
I highly recommend that you avoid variadic functions and use pointer arrays and variadic macros instead (with a terminator object).
Your function would have looked like this when using this approach:
void printline(const char *str) { printf("%s\n", str); }
int printlines(char **lines) {
if (!lines)
return -1;
while (*lines)
printline(*(lines++));
return 0;
}
#define printlines(...) printlines((char *[]){__VA_ARGS__, NULL})
Not only are variadic functions sometimes difficult to code, but the ABI for variadic functions is problematic to the point that different languages might treat it differently and C bindings between different languages might break your code.
Besides, when using this approach, things can become much more fun and interesting as well, allowing for easy type detection and multi-type arguments... this code from the facil.io CSTL library provides a good example for what I mean.
The function accepts an array of structs:
/** An information type for reporting the string's state. */
typedef struct fio_str_info_s {
/** The string's length, if any. */
size_t len;
/** The string's buffer (pointer to first byte) or NULL on error. */
char *buf;
/** The buffer's capacity. Zero (0) indicates the buffer is read-only. */
size_t capa;
} fio_str_info_s;
/** memory reallocation callback. */
typedef int (*fio_string_realloc_fn)(fio_str_info_s *dest, size_t len);
/** !!!Argument type used by fio_string_write2!!! */
typedef struct {
size_t klass; /* type detection */
union {. /* supported types */
struct {
size_t len;
const char *buf;
} str;
double f;
int64_t i;
uint64_t u;
} info;
} fio_string_write_s;
int fio_string_write2(fio_str_info_s *restrict dest,
fio_string_realloc_fn reallocate, /* nullable */
const fio_string_write_s srcs[]);
Then a macro makes sure the array's last element is a terminator element:
/* Helper macro for fio_string_write2 */
#define fio_string_write2(dest, reallocate, ...) \
fio_string_write2((dest), \
(reallocate), \
(fio_string_write_s[]){__VA_ARGS__, {0}})
Additional helper macros were provided to make the fio_string_write_s structs easier to construct. i.e.:
/** A macro to add a String with known length to `fio_string_write2`. */
#define FIO_STRING_WRITE_STR2(str_, len_) \
((fio_string_write_s){.klass = 1, .info.str = {.len = (len_), .buf = (str_)}})
/** A macro to add a signed number to `fio_string_write2`. */
#define FIO_STRING_WRITE_NUM(num) \
((fio_string_write_s){.klass = 2, .info.i = (int64_t)(num)})
And the function used the terminator element to detect the number of arguments received by the macro:
int fio_string_write2 (fio_str_info_s *restrict dest,
fio_string_realloc_fn reallocate, /* nullable */
const fio_string_write_s srcs[]) {
int r = 0;
const fio_string_write_s *pos = srcs;
size_t len = 0;
while (pos->klass) {
switch (pos->klass) { /* ... */ }
/* ... counts total length */
++pos;
}
/* ... allocates memory, if required and possible ... */
pos = srcs;
while (pos->klass) {
switch (pos->klass) { /* ... */ }
/* ... prints data to string ... */
++pos;
}
/* ... house-keeping + return error value ... */
}
Example use (from the source code comments):
fio_str_info_s str = {0};
fio_string_write2(&str, my_reallocate,
FIO_STRING_WRITE_STR1("The answer is: "),
FIO_STRING_WRITE_NUM(42),
FIO_STRING_WRITE_STR2("(0x", 3),
FIO_STRING_WRITE_HEX(42),
FIO_STRING_WRITE_STR2(")", 1));
This both simplifies the code and circumvents a lot of the issues with variadic functions. This also allows C bindings from other languages to work better and the struct array to be constructed in a way that is more idiomatic for the specific target.

Catenating chars in c: argument of type "char" is incompatible with parameter of type "const char*" [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 4 years ago.
Improve this question
I'm trying to write a program that catenate 2 values in C without using strcat().
char cat (char s1, char s2){
char s3[200];
strcpy(s3,s1);
strcpy(s3+strlen(s1),s2);
return s3;
}
This is my code but it's giving this error:
argument of type "char" is incompatible with parameter of type "const char*"
What should l do?
(I recently start to learn C so please answer me in an easy way)
In your function, the parameters s1 and s2 are of type char which means a single character. Inorder for them to be strings, they must be character arrays. So
char cat (char s1[], char s2[]){
or
char cat (char *s1, char *s2){
instead of
char cat (char s1, char s2){
After this correction, you could just use sprintf() if the destination string is large enough like
sprintf(s3, "%s%s", s1, s2);
And in your program,
s3 is allocated on the stack as it is an automatic variable.
It goes out of scope when the program control exits the cat() function. If you really need to return the string, either allocate memory for s3 on the heap using malloc() and return a pointer to that memory as in
char* cat (char s1[], char s2[]){
char *s3 = NULL;
if( (s3=malloc(sizeof(char)*( strlen(s1)+strlen(s2)+1 )))==NULL )
{
perror("Not enough memory");
return NULL;
}
sprintf(s3, "%s%s", s1, s2);
return s3;
}
or create the s3 character array in the calling function and pass it to cat() as in
char s3[200];
cat(s3, s1, s2);
........
void cat (char s3[], char s1[], char s2[]){
if( strlen(s1) + strlen(s2) < 200 )//where 200 is the size of s3
{
sprintf(s3, "%s%s", s1, s2);
}
else
{
printf("\nInput strings too large");
}
}
See Returning C string from a function.
You're taking chars as arguments and using it as a return value. You probably want s1, s2 to be const char *:
char *cat (const char *s1, const char *s2){
You will have to return a char * too, which means you will have to allocate something that isn't on the stack.
char *cat (const char *s1, const char *s2){
char *s3 = (char*)malloc(200);
strcpy(s3,s1);
strcpy(s3+strlen(s1),s2); // strcat(s3, s1);
return s3;
}
A char is a small integer type (a byte), which can hold just a single encoded character, not a string. Strings in C don't have a dedicated type, they're just defined as a sequence of characters, ending with a 0 byte. So, you naturally store strings in arrays of char. E.g. the array
char str[30];
Can store a string of up to 29 characters (one more is needed for the terminating 0).
Furthermore, you have to know that arrays can't be passed to or returned from functions in C. Instead, pointers are used. If you write arrays as function arguments or return values, these types are automatically adjusted to the corresponding pointer types. It's common to say the array "decays" as a pointer. So, in your code, you attempt to return an array. Instead, a pointer to the first array element is returned. Because this array is local to your function, it doesn't exist any more after the return, so you are returning an invalid pointer.
That's why the library function strcat expects the caller to give a pointer to the result, instead of returning the result. A typical simple strcat function could look like this (not the original, returning nothing here to make the code simple):
void mystrcat(char *s, const char *append)
{
while (*s) ++s; // search end of s
while ( (*s++ = *append++) ); // copy until end of append
}
To understand this code, you have to know that 0 is false in C when evaluated in a boolean context, and any other value is true. ++ increments, so applied to pointers, moves them to point to the next element. Therefore, this code examines each character in *s until it finds a 0 byte and then starts copying characters from *append there until the 0 byte in *append is hit.
If you absolutely want to return the result, you have to dynamically allocate memory for it in your function. This could look like the following:
char *concatenate(const char *s1, const char *s2)
{
size_t resultlen = strlen(s1) + strlen(s2) + 1; // one more for the 0 byte
char *result = malloc(resultlen);
if (!result) return 0; // allocation failed, out of memory
char *p = result;
while ( (*p = *s1++) ) ++p; // copy from s1 until 0
while ( (*p++ = *s2++) ); // copy from s2 until 0
return result;
}
Of course, in this case, the caller has to free() the result when it's no longer needed.

char and pointer working together gives warning: assignment from incompatible pointer type

I am trying to understand a bit about pointers and how to use them with char types. Here I am declaring a char and assigning it a value. Then I declare a pointer variable. Using the '&', I believe I am getting the address of the variable - I'm trying to dereference the pointer and set it so the *s1 variable will print out the value in x1. I know I can achieve this in other ways, but, I really want to understand how to pass the value from a char to a char pointer. I am getting an incompatible pointer type warning and I don't understand why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
/* Global variable: accessible to all threads */
int total = 0;
int n1,n2;
// char *s1,*s2;
FILE *fp;
/* Prototypes */
int num_substring(void); /* Given Substrings Function */
int readf(void); /* stand in for file read */
/* Input for testing - will be from readfile */
char x1[49] = "vgccgcporertfewjjqhjreuvpubfiterhmdxereotxmhcnsre";
char x2[2] = "re";
char *s1; /* A pointer to an char ("*s1" is a char, so s1
must be a pointer to an char) */
char *s2;
int main(int argc, char* argv[]) {
readf();
return 0;
} /* MAIN */
// make a function to return s1, s2, n1 ,n2 maybe fp
int readf(void){
s1 = &x1; /* Read it, "assign the address of x1 to s1*/
s2 = &x2;
/* Input for testing - will be from readfile */
n1=strlen(s1); /*length of s1*/
n2=strlen(s2)-1; /*length of s2*/
/* ----------------------------------------- */
return -1;
} /* readf */
s1 = &x1;
is not correct. From
char x1[49] = "vgccgcporertfewjjqhjreuvpubfiterhmdxereotxmhcnsre";
x1 is an array of characters. So &x1[0] is the address of the first character.
s1 = &x1[0]; // should get rid of that warning
Interestingly, you can interchange &x1[0] with x1 by convention (ie both means the same thing). So the below should also be true :
s1 = x1; // should get rid of that warning
But if you can write s1 = x1;, then you can't write s1 = &x1; for the obvious reason which is known to you.
Edit2
It is not safe to write
char x1[49] = "vgccgcporertfewjjqhjreuvpubfiterhmdxereotxmhcnsre";
"..." is a null terminated sequence of characters(sometimes conveniently called a string) which means a null character '\0' will be appended to what you put inside the double quotes. If you put the exact number of characters , as mentioned in the array index, or more inside the double quotes, then when the compiler append the '\0', then you access outside the boundary of the array. Luckily, C has a flexible mechanism whereby you can omit the array index and the compiler does the job of allocating a memory block large enough to hold your string. So change it to
char x1[] = "vgccgcporertfewjjqhjreuvpubfiterhmdxereotxmhcnsre";
Note Thanks #david-bowling for the [ hint ].

Concatenate anything in C ansi

I need to create a C-function to concatenate two of any type of data and return the string that is the result of concatenation. I have done this function below, but it does not work. Could somebody help me?
// void pointer does not store value, is just the address of a memory location
char* concatenate(void* varA, int tamA, void* varB, int tamB)
{
// char is 1 byte
char* result;
char* a,b; // helpers
result = malloc((tamA+tamB)*sizeof(char));
a = varA; // "a" receives the address pointed to by the pointer varA
b = varB; // "b" receives the address pointed to by the pointer varB
*result = *result << tamA + *a;
*result = *result << tamB + *b;
result = a; // let the results point to "a"
return result; // the result is the pointer "a"
}
In C, which is what you're asking about even though your code is C++, you can't do it like that.
There's no way to figure out from a bare void * how to convert it to a string.
You must add type information of some form, such as printf()'s string using e.g. %d for decimal integers and so on.
This would be a workable prototype, I think:
char * concat_any(const char *format1, const void *data1,
const char *format2, const void *data2);
I'm not saying "optimal" or even "suitable", but it would at least be possible to implement to that prototype. The format strings could be printf()-style, or whatever.
Note that for C, this would also be very impractical, since taking a void * implies that you need a pointer to the data, always. If you wanted to e.g. concatenate two numbers, you couldn't do it like this:
char *fortytwo = concat_any("%d", 4, "%d", 2); /* BROKEN CODE */
since that passes integers instead of void *, which is very ugly. You would have to do it like this:
const int four = 4, two = 2;
const char *fortytwo = concat_any("%d", &four, "%d", &two);
which is clearly not exactly convenient.
So, it would be better to use varargs, but then you get the problem of not being able to associate different varargs with different non-variable arguments, like so:
char * concat_anyv(const char *format1, ...,
const char *format2, ...); /* BROKEN CODE */
So, how about having two formatting strings first, then trusting the caller to pass the two arguments as varargs? That would give:
char * concat_anyv2(const char *format1, const char *format2, ...);
Now we're talking. This can be trivially implemented, even: internally concatenate the two formatting strings, and call vsnprintf() two times: once to figure out buffer size, then allocate, and call it again.
Usage would be like so:
char *fortytwo = concat_anyv2("%d", "%d", 4, 2);
Done.
If I understand correctly, what you are trying to do is copy the data that varA and varB point to into a new memory buffer, one after the other, and return a char-pointer to this buffer. You can achieve this easily with the memcpy function.
char *concatenate(void *varA, int tamA, void *varB, int tamB)
{
char* result = malloc(tamA + tamB);
// copy varA to "result"
memcpy(result, varA, tamA);
// copy varB to "result" after varA
memcpy(result+tamA, varB, tamB);
return result;
}
Note that whatever data varA and varB hold it is used as is and not converted to a human readable representation.
I have two little string functions in C that I use. The first is an adaptation others have made that uses the printf model as others mentioned, you have to know what the data types are going in:
char* str(const char *fmt, ...)
{
int size;
char *buff;
va_list argp1;
va_list argp2;
va_start(argp1, fmt);
va_copy(argp2, argp1);
//calling vsnprintf with a NULL buffer simply returns what would
//be the size of the resulting string but does not include space for nul byte
size = vsnprintf(NULL, 0, fmt, argp1) + 1;
va_end(argp1);
//now actually allocate a buffer of the correct size and then fill it
buff = calloc(1,size);
assert(buff != NULL);
vsnprintf(buff, size, fmt, argp2);
va_end(argp2);
return buff;
}
With this I can concat doing a simple
char *d = str("%s%s%d", s1, s2, 25);
I just have to remember to free the string that is returned as it is allocated memory.
I have a second routine that I use for simple string concatenations that I can nest in other calls as it does internal cleanup for me:
typedef enum {FREE_NONE, FREE_ONE, FREE_TWO, FREE_BOTH} CONCAT_FREE_FLAG;
char *concat(char *one, char *two, CONCAT_FREE_FLAG f)
{
int size = strlen(one) + strlen(two) + 1;
char *buff = calloc(1,size);
assert(buff != NULL);
strcpy(buff, one);
strcat(buff, two);
if( f == FREE_ONE || f == FREE_BOTH)
free(one);
if( f == FREE_TWO || f == FREE_BOTH)
free(two);
return buff;
}
This allows me to do things like:
char *s = concat(
concat("Static ",str("%dx%d", x, y), FREE_TWO),
"Other Static", FREE_ONE);
The reason I have this is really syntactic sugar so I can pass dynamically allocated strings in, get a new dynamically allocated string but not have to worry about cleaning up the input strings.

Is it possible to create a default value for an argument in a function with C?

Alright, so forgive me. I've started learning C, and coming from PHP, I hate the lack of explode() so I decided to create my own.
Here's what I have so far:
#include <stdio.h>
#include <windows.h>
char * explode(char * toExplode, char * delimiter) /* Need to add a buffer size here */
{
char * token;
token = strtok(toExplode, delimiter);
token = strtok(NULL, delimiter);
return token;
}
int main(void)
{
char string[] = "This is a string yaaaaay";
char * exploded;
exploded = explode(string, " ");
printf("%s\n", exploded); /* Should currently return 'is' */
return 0;
}
So far, it's working just as I expect it to. However, now I need to create a 2D array of variable size in the first dimension (actually, both dimensions.)
I was thinking of doing something like char * explode(char * toExplode, char * delimiter, int length = strlen(toExplode)) so that I could either specify the length or have it set it default. This of course doesn't work though, and I have no idea where to go from here.
Any ideas/help?
You can pass a known bad value (commonly a 0 or -1) as the length, have multiple similar functions, or use a macro.
If you go the bad value route, when you call your function you can give it a value you know isn't possible and check for such a bad value at the start of the function. Then automatically calculate the correct value and continue as usual. The problem with this is that you are now forced to have at least one bad value (not a problem in this case).
char * explode(char * toExplode, char * delimiter, int length){
if(length == 0)
length = ...;
In the multiple similar functions method, each function has a slightly different declaration. They cannot all have the same name because C does not support overloading the way that another language like C++ has. The printf() family of functions is a good example of this.
char * explodel(char * toExplode, char * delimiter, int length);
char * explode (char * toExplode, char * delimiter){
int length = ...;
return explodel(toExplode, delimiter, length);
}
The macro method is a bit of a hack, but it does work. It is a bit of a combination of the previous two methods, in which you have the two different functions you can call, but the first one gets preprocessed with a bad value automatically being passed to the other so it can figure out the correct length value.
#define explode (s, ...) explodel(s, __VA_ARGS__, 0)
char * explodel(char * toExplode, char * delimiter, int length, ...);
The way this works is that if you only give it the first two arguments, the 0 fall into place as the third argument. If you give it three arguments, all three are passed normally and the 0 is added as a forth invisible argument hidden away in the function stack. If you pass more than three, all the extra arguments will be hidden like the 0. If you try to pass only one argument, you will get the following error:
error: expected expression before ',' token
No you can't but that doesn't stop you from pushing in a dummy value (i.e. -1) and then first thing in the function if the value is -1 then change it to whatever value you want.
If you're stuck using C then the solution I'd recommend is rolling two functions; one with length, one without.
char * explode(char * toExplode, char * delimiter, int length)
{
...
}
char * explode(char * toExplode, char * delimiter)
{
int len = strlen(toExplode);
return explode(toExplode, delimiter, len);
}
So, the latter just works out the length for you and passes it to the former and returns the result.
In C you can't.
This is possible in C++, but with a constant value (-1), for example.
You can't overload a function in C, neither have default arguments, that's C++.
The only solution I can think of is having an static local variable, with the default value.
char * explode(char * toExplode, char * delimiter,unsigned int plen) /* Need to add a buffer size here */
{
static unsigned int slen = 100;
unsigned int len;
char * token;
if (plen!=0)
len = plen;
else
len = slen;
/*...*/
}

Resources