Variable not updating even with volatile, no threads - c

I have the following printf statement which prints the output of a function:
printf("\nStart-Time %s,End-Time %s",
get_time(myfields[i].start_mn),
get_time(myfields[i].end_mn));
In the statement the get_time function is called twice with different arguments. However, even though the function returns different strings on the two calls, the printf only prints the return from the first call for the second call also. I have tried the volatile keyword for the function return variable and the function itself, but the output is still the same.
But if I split the printf statement into two printf stataments to print the values separately,they are printing different values which is expected.
So can anyone point to me what is happening here and what is the correct way of doing this?
[Update from comment:]
get_time is returning a global char array. char *get_time(int tval) converts time in minutes to hour.

So can anyone point to me what is happening here and what is the correct way of doing this?
Each call to get_time() returns the same address to the same buffer, which are then passed to printf(), which finds in this very one buffer what had been put in last, with "last" in terms of "last in time".
To get around this create temporary buffers to pass to printf():
char * p1 = strdup(get_time(...));
char * p2 = strdup(get_time(...));
printf("\nStart-Time %s,End-Time %s", p1, p2);
/* Free the temporary buffers. */
free(p1);
free(p2);
A more simple approach would do if you know the maximum size of what get_time() returns in advance:
#define GET_TIME_LEN_MAX 32
...
char b1[GET_TIME_LEN_MAX];
char b2[GET_TIME_LEN_MAX];
strcpy(b1, get_time(...));
strcpy(b2, get_time(...));
printf("\nStart-Time %s,End-Time %s", b1, b2);
The safe version of the latter example:
#define GET_TIME_LEN_MAX 32
...
/* Define buffers and initialise them to ALL zeros. */
char b1[GET_TIME_LEN_MAX] = "";
char b2[GET_TIME_LEN_MAX] = "";
strncpy(b1, get_time(...), GET_TIME_LEN_MAX - 1);
strncpy(b2, get_time(...), GET_TIME_LEN_MAX - 1);
printf("\nStart-Time %s,End-Time %s", b1, b2);
As strdup() isn't Standard C, for completeness find a home grown implementation below:
#include <stdlib.h> /* for malloc() */
#include <string.h> /* for strcpy() */
char * strdup(const char * s)
{
char * p = NULL;
if (NULL != s)
{
p = malloc(strlen(s) + 1);
if (NULL != p)
{
strcpy(p, s);
}
}
return p;
}

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.

String operations cause segfault C [duplicate]

This question already has answers here:
Why do I get a segmentation fault when writing to a "char *s" initialized with a string literal, but not "char s[]"?
(19 answers)
Closed 1 year ago.
I'm trying to 'deep copy' a string so that I can perform operations on one copy, while retaining the original copy. This is the base example that I've got and for some reason the strncpy call causes a segfault. Please help
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main() {
char* stringA = "someVeryinTeresTingString";
char* stringB = malloc(sizeof(char) * strlen(stringA));
printf("A: %s, B: %s\n", stringA, stringB);
for (int i = 0; i < strlen(stringA); i++) {
stringB[i] = tolower(stringA[i]);
}
printf("A: %s, B: %s\n", stringA, stringB);
strncpy(stringA, stringB, strlen(stringA) - 1);
printf("A: %s, B: %s\n", stringA, stringB);
}
Easiest fix is to make a local copy of that string literal:
char stringA[] = "someVeryinTeresTingString";
Everything else works just the same.
Note that in the original code you have a pointer to immutable memory, while in this version you have a local (stack) array that is initialized with a copy of that string.
Another thing to note is if you're copying and manipulating C strings, do things like this:
char* stringB = strdup(stringA);
for (int i = 0; i < strlen(stringB); ++i) {
stringB[i] = tolower(stringB[i]);
}
Or even more efficiently by avoiding all these expensive strlen() calls:
char* stringB = strdup(stringA);
for (char* p = stringB; *p; ++p) {
*p = tolower(*p);
}
This line:
char* stringB = malloc(sizeof(char) * strlen(stringA));
shuld be like this:
char* stringB = malloc(sizeof(char) * (strlen(stringA) + 1));
then you will able to copy the \0 in the end of stringA
also, you want to copy to literal string - that is segmentation fault
char *strncpy(char *dest, const char *src, size_t n)
I'll try to comment and correct in your own code the mistakes I've seen:
(I will not correct things that can be eliminated or better done in another way, but are correct or not harmful, so you'll see only what must be corrected because of programming errors, and not questions about style or programming uses)
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int main() {
char* stringA = "someVeryinTeresTingString";
/* you need to consider the space for the final null character in the malloc() call */
char* stringB = malloc(sizeof(char) * (strlen(stringA) + 1));
/* you don't need to use sizeof(char) as it is always equal to one.
* Multiplying by one is not necessary, but you'll probably know.
* char is warranteed by C standard that its sizeof is one. */
/* you need to copy the string *before* printing, or you will print an
* uninitialized string. Or at least initialize stringB to zeros, so you can
* use it with printf like functions (I do initialize the first char position to
* zero to make it appear as a length zero "" string)
* You will incurr in undefined behaviour if you don't do this. */
stringB[0] = '\0';
printf("A: %s, B: %s\n", stringA, stringB);
/* you need to copy the strings, so you can do it better if you test when
* stringA[i] == '\0', so you don't calculate the length of a string that is
* not going to change at every loop iteration. I will not change your
* code, because this is not an error. But strlen() searches from the
* beginning of the string for the '\0' char, character by character,
* and this test is done at every loop iteration. With the expression
* stringA[i] == 0 you do only a test per loop iteration to see if
* the char at position i in stringA is the null character. */
int i;
for (i = 0; i < strlen(stringA); i++) {
stringB[i] = tolower(stringA[i]);
}
/* you have not copied the final '\0', so I do it now. I need to move the
* declaration of i outside of the loop to be able to use it's value. */
stringB[i] = 0; /* you can use 0 or '\0' interchangeably */
printf("A: %s, B: %s\n", stringA, stringB);
/* nope. you need to copy the strings with a normal strcpy() as you know that
* both are the same length (better, you know that the space in stringB
* is the same as the length of stringA plus one). If you do this, you will not copy the last '\0' char, so wee need to append it.
* well, I don't know if that is what you want, so I don't actually touch anything here. */
strncpy(stringA, stringB, strlen(stringA) - 1);
/* stringB should be one char shorter than stringA */
printf("A: %s, B: %s\n", stringA, stringB);
}
by the way, you have been recommended to use strdup(3). This is a good idea, you don't need to be thinking on final nulls in this case, because strdup() takes care of it. Just remember that strdup(3) is not included in many C standard revisions, so you can get in trouble if you
move your program to a place lacking it (that should be very strange, anyway)

How does printf() function treat the strings returned by functions using global buffers?

I have written a small code that prints the current date with a specific format. To return each data of the date (day of the week, day of the month, name of the month, etc.), I use functions of type const char * that pass the data as a string to a variable that serves as a buffer and then pass the buffer to the main function.
The following code represents the operation of my program in a very simple way:
#include <stdio.h>
#include <string.h>
char
buff[16];
const char
*printStr1 (char *str1)
{
strncpy(buff, str1, sizeof(str1));
return buff;
};
const char
*printStr2 (char *str2)
{
strncpy(buff, str2, sizeof(str2));
return buff;
};
int
main()
{
printf("%s%s", printStr1("foo"), printStr2("bar"));
return 0;
}
My issue is this: when passing each data to the buffer and returning it to the main function, the last two data are the same (the last data is not printed, instead it is printed what was already in the buffer, that is, the second last data).
This only happens when I use a single printf() function to show all the data, as in the example above. If I do not use a buffer to return the data (I directly return the argument passed to the function) or use a printf() for every data , everything is executed correctly.
P.S.: I tried to solve this using functions like memset() to clean the buffer, but it does not work, the only thing I can do is use a different buffer for each data (for each function).
I hope I have expressed my problem well.
Besides the sizeof issue (that you're overlooking because your strings are small), your issue isn't related to printf
The code first evaluates the arguments, then passes them to printf. The last evaluated argument "wins" and printf gets the same argument twice.
To use only one local buffer, split your printf call:
printf("%s%s", printStr1("foo"), printStr2("bar"));
could be rewritten to:
printf("%s", printStr1("foo"));
printf("%s", printStr2("bar"));
once printed the value can change it doesn't matter :)
Since C doesn't have a garbage collector or string objects, you cannot just allocate separate buffers and return them to pass to printf else you'd get memory leaks, so in C there's no smart & readable solution for such issues.
That said, in one C project I made, I used a revolving list of strings (several buffers, first in, first out). Make it 10 buffers, and you can use up to 10 arguments in the same function and it will work properly.
This code:
printf("%s%s", printStr1("foo"), printStr2("bar"));
is equivalent to this:
const char *p1 = printStr1("foo");
const char *p2 = printStr2("bar");
printf("%s%s", p1, p2);
or this (depending on the compiler):
const char *p2 = printStr2("bar");
const char *p1 = printStr1("foo");
printf("%s%s", p1, p2);
Now it should be clear why you're getting the output you get.
In addition to #Jean-François Fabre good answer, ...
To print printStr1("foo"), printStr2("bar") with 1 printf(), separate buffers are needed.
Since C99, code can use compound literals.
#include <stdio.h>
#include <string.h>
#define PRINT_N 16
const char *printStr1(char *dest, size_t n, const char *str) {
snprintf(dest, n, "%d %s\n", 1, str);
return dest;
}
const char *printStr2(char *dest, size_t n, const char *str) {
snprintf(dest, n, "%d %s\n", 2, str);
return dest;
}
// compound literals -------------v-----------------v
#define PRINT_STR1(src) printStr1((char [PRINT_N]){0}, PRINT_N, (src))
#define PRINT_STR2(src) printStr2((char [PRINT_N]){0}, PRINT_N, (src))
int main() {
printf("%s%s\n", PRINT_STR1("foo"), PRINT_STR2("bar"));
printf("%s%s%s%s\n", PRINT_STR1("foo"), PRINT_STR2("bar"), PRINT_STR2("foo"), PRINT_STR1("bar"));
}
Output
1 foo
2 bar
1 foo
2 bar
2 foo
1 bar

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

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

How to correctly prototype C functions

I'm learning the concept of prototyping in C, however I'm struggling with the correct syntax. I'm writing a function to strip all non-alphbetic characters from a c-string
#include <stdio.h>
#include <string.h>
char[30] clean(char[30] );
int main()
{
char word[30] = "hello";
char cleanWord[30];
cleanWord = clean(word);
return 0;
}
char[30] clean(char word[30])
{
char cleanWord[30];
int i;
for(i=0;i<strlen(word);i++)
if ( isalpha(word[i]) )
cleanWord[i]=word[i];
cleanWord[i]='\0';
return cleanWord;
}
How do I correctly prototype the function? What are the other syntax errors that are preventing my program from compiling?
Your problem is not with function prototyping (aka forward declaration). You just can't return an array from a function in C. Nor can you assign to an array variable. You need to make a couple of changes to get things working. One option:
change char cleanWord[30] in main to be char * cleanWord.
change the signature of clean to char *clean(char word[30])
use malloc to allocate a destnation buffer inside clean
return a pointer to that new buffer
free the buffer in main
And another:
change the signature of clean to void clean(char word[30], char cleanWord[30])
operate on the passed-in pointer rather than a local array in clean
change the call in main to be clean(word, cleanWord).
As Carl Norum said, you can't return an array. Instead, what you tend to do is supply the output:
void clean( const char word[30], char cleanWord[30] )
{
}
And you should remove the locally-scoped array from that function.
You will find that the function does not work correctly, because you only have one iterator i. That means if a character is not an alpha, you will skip over a position in the output array. You will need a second iterator that is incremented only when you add a character to cleanWord.
A couple of notes (was a bit late with writing up an answer, seems I've been beaten to them by the others )
C cannot return local (stack) objects, if you want to return an array from a function you have to malloc it
Even if you declare an array argument as (char arr[30]), (char* arr) is just as valid as arrays decay to pointers when passed as arguments to functions. Also, you won't be able to get the size correctly of such arrays by using sizeof. Even though it's 30, on my machine it returns 4 for word in clean, which is the size of the pointer for it.
You are missing an include, isalpha is part of ctype.h
I've updated your code, hopefully I've guessed your intentions correctly:
#include <stdlib.h> /* for malloc and free */
#include <string.h> /* for strlen */
#include <ctype.h> /* for isalpha */
#include <stdio.h> /* for printf */
/* Function declaration */
char* clean(char word[30]);
/* your 'main()' would now look like this: */
int main()
{
char word[30] = "hel1lo1";
char* cleanWord;
cleanWord = clean(word);
printf("%s\n", cleanWord);
free(cleanWord);
return 0;
}
/* Function definition */
char* clean(char word[30])
{
char* cleanWord = malloc(30); /* allocating dynamically an array of 30 chars,
* no need to use sizeof here as char is
* guaranteed to be 1 by the standard
*/
unsigned int i, j = 0; /* let's fix the problem with non-alpha chars already pointed out */
for (i = 0; i < (strlen(word)); i++)
if (isalpha(word[i]))
cleanWord[j++] = word[i];
cleanWord[j] = '\0';
return cleanWord;
/* return a pointer to the malloc`ed array, don't forget to free it after you're done with it */
}

Resources