Pthreads and recursion - c

I'm struggling with one of my training tasks for getting in touch with a new language. Unfortunately, this time the new language is an old one, it is C. My programming Task for this is to generate Langford-Strings, which should not be the main problem.
My first attempt in C, with a recursive approach works like a charm:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int grade = 0;
const char* blank = "_";
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
void generate(int position, char* string) {
if (!string) {
string = calloc(grade*2+1, sizeof(char));
for (int i = 0; i < grade*2; i++) {
string = strcat(string, blank);
}
}
if (!strstr(string, blank)) {
printf("%s\n", string);
return;
}
if (position < strlen(string)) {
if (string[position] != *blank) {
char* nstring = calloc(grade*2+1, sizeof(char));
strcpy(nstring, string);
generate(position+1, nstring);
free(nstring);
return;
} else {
for (int i = 0; i<strlen(string); i++) {
if (strchr(string, alphabet[i])){
continue;
}
int index = strcspn(alphabet, &alphabet[i])+1;
if (position+index+1<strlen(string)) {
if (string[position]==*blank) {
if (string[position+index+1]==*blank) {
char* nstring = calloc(grade*2+1, sizeof(char));
strncat(nstring, string, position);
strncat(nstring, &alphabet[i], 1);
strncat(nstring, &string[position+1], index);
strncat(nstring, &alphabet[i], 1);
strcat(nstring, &string[position+2+index]);
if (position<strlen(nstring)) {
generate(position+1, nstring);
}
free(nstring);
}
}
}
}
}
}
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Missing parameter of langford strings grade!\n");
return 1;
}
grade = strtol(argv[1], NULL, 10);
if (grade % 4 != 0) {
if ((grade+1) % 4 != 0) {
printf("Grade must be multiple of 4 or one less\n");
return 1;
}
}
generate(0, NULL);
return 0;
}
That works great, giving me exactly the results I expected.
But when I try to do it threaded (old-style threaded, spawning a new thread on each level of the recursion), it not only ends with a seqfault every time. It does end in an seqfault in a not predictable time. That means, that it runs indefinitly, printing out doubled and trippled results and always a random number of results, before seqfaulting.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <errno.h>
size_t grade = 0;
const char* blank = "_";
const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
struct thread {
pthread_t* thread;
size_t position;
struct thread* threads[26];
char* string;
};
void* alloc_thread_data() {
struct thread* ret = calloc(1, sizeof(struct thread));
ret->thread = calloc(1, sizeof(pthread_t));
ret->position = 0;
ret->string = calloc((grade*2)+1, sizeof(char));
return (void*)ret;
}
void free_thread_data(struct thread* dst) {
free(dst->string);
free(dst->thread);
free(dst);
}
void assemble_string(char* dst, char* src, size_t pos, size_t index) {
strncat(dst, src, pos);
strncat(dst, &alphabet[index-1], 1);
strncat(dst, &src[pos+1], index);
strncat(dst, &alphabet[index-1], 1);
strncat(dst, &src[pos+2+index], (grade*2)-pos+index+2);
}
void* generate(void* data) {
struct thread* args = (struct thread*)data;
if (args->string && strlen(args->string)==0) {
for (size_t i = 0; i<grade*2; i++) {
strcat(args->string, blank);
}
}
if (args->string && !strstr(args->string, blank)) {
printf("%s\n", args->string);
return NULL;
}
if (args->string && args->position<strlen(args->string)) {
size_t sub = 0;
if (args->string[args->position]!=*blank) {
args->threads[sub] = alloc_thread_data();
strcpy(args->threads[sub]->string, args->string);
args->threads[sub]->position = args->position+1;
pthread_create(args->threads[sub]->thread, NULL, generate, (void*)args->threads[sub]);
sub++;
} else {
for (size_t i = 0; i<grade*2; i++) {
if (strchr(args->string, alphabet[i])){
continue;
}
int index = strcspn(alphabet, &alphabet[i])+1;
if (args->string[args->position] == *blank) {
if (args->string[args->position+index+1] == *blank) {
args->threads[sub] = alloc_thread_data();
assemble_string(args->threads[sub]->string, args->string, args->position, index);
args->threads[sub]->position = args->position+1;
pthread_create(args->threads[sub]->thread, NULL, generate, (void*)args->threads[sub]);
sub++;
}
}
}
}
for (size_t i = 0; i<sub; i++) {
if (args->threads[i]->thread!=NULL) {
if(pthread_kill(*args->threads[i]->thread, 0)==0) {
pthread_join(*args->threads[i]->thread, NULL);
}
free_thread_data(args->threads[i]);
}
}
}
return NULL;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
printf("Missing parameter of langford strings grade!\n");
return 1;
}
grade = strtol(argv[1], NULL, 10);
if (grade % 4 != 0) {
if ((grade+1) % 4 != 0) {
printf("Grade must be multiple of 4 or one less\n");
return 2;
}
}
struct thread* args = alloc_thread_data();
pthread_create(args->thread, NULL, generate, (void*)args);
if(pthread_kill(*args->thread, 0)==0) {
pthread_join(*args->thread, NULL);
}
free_thread_data(args);
}
So, as written before, I managed to get around C programming for my whole work-life and do this just for fun - so I do not expect my code to be somewhat comprehensive. Please help me finding out, what is wrong with the threaded approach (and if you see any well-known-codesmell in the first one as well of course). Any hints welcome.

In addition to the bad allocation that #EugeneSh. pointed out, this looks like a problem:
pthread_create(args->threads[sub]->thread, NULL, generate,
(void*)&args->threads[sub]);
Note the difference from this other call that also appears:
pthread_create(args->threads[sub]->thread, NULL, generate,
(void*)args->threads[sub]);
[newlines inserted and indentation normalized for clarity and ease of reading].
args->threads[sub] is a struct thread*. You want to pass that pointer itself to pthread_create(), as in the second case, not its address, as in the first case.
Overall, I'm inclined to agree with #MikeRobinson that yours is an inappropriate use of threads. It is never useful performance-wise to have more schedulable threads in your process than you have cores, and you scale up to many thousands of total threads very quickly. I doubt very much that the result will outperform your single-threaded solution -- the costs of the context switching and cache thrashing that surely result will likely swamp whatever speedup you get from parallel execution on the 4 - 12 cores you probably have.
Added:
Additionally, it is very important to check the values returned by your function calls for error codes, unless you don't care and don't need to care whether the calls succeed. In particular, you should check
the return values of your malloc() / calloc() calls -- these return NULL in the event of unsuccessful allocation, and with as many total allocations as you perform, it is plausible that some of these fail. Using the resulting NULL pointer could easily lead to a segfault
the return values of your pthread_create() calls -- these return a value different from 0 in the event of failure. It is not safe to afterward rely on pthread_kill() to determine whether the thread was created successfully, for a failed pthread_create() leaves the thread handle's contents undefined. Any subsequent evaluation that depends on the value of the handle therefore exhibits undefined behavior.
I'm also a little suspicious of all your strncat()ing, for this is a notorious source of string overruns. These are ok if the target strings have enough capacity, but it's difficult for me to tell whether they always do in your case.

May I cordially suggest that it makes absolutely no sense (to me, at least ...) to "spawn a new thread" here?
The only reason to "spawn a thread" is when you wish to perform two thereafter, independent activities, which this algorithm quite clearly does not.
The immediate reason for the segfault is that the various threads are all attempting to manipulate the same data without regard to one another, and without waiting for one another. But, IMHO, the root cause of the problem is ... that this entire scenario is nonsense. "Recursion" and "multi-threading" are not at all the same thing. If your objective here was to learn about threading, I'm afraid that you've just learned far more (the very-hard way) than you ever wished to know . . .

Related

After creating an array through dynamic allocation, a problem occurs when changing the memory size through realloc in C

I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)

c - unsetenv() implementation, is it necessary to free memory?

As TLPI exercise 6-3 required, I made an implementation of setenv() and unsetenv() using putenv(), getenv() and via modifing environ variable directly.
Code:
// setenv() / unsetenv() impl
// TLPI exercise 6-3
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define ENV_SEP '='
extern char **environ;
// setenv() impl using putenv() & getenv()
int setenv_impl(const char * name , const char * value , int overwrite ) {
if(!overwrite && getenv(name)) { // exists & don't overwrite
return 0;
} else {
// construct the new variable
char *env_var = malloc(strlen(name) + strlen(value) + 2);
strcpy(env_var, name);
env_var[strlen(name)] = ENV_SEP;
strcpy(env_var+(strlen(name)+1), value);
int result = putenv(env_var);
if(result==0) {
return 0;
} else {
errno = result;
return -1;
}
}
}
// unsetenv() impl via modifing environ directly,
int unsetenv_impl(const char * name ) {
char **ep, **sp;
size_t len;
len = strlen(name);
for(ep = environ; *ep != NULL;) {
if(strncmp(*ep, name, len)==0 && (*ep)[len] == ENV_SEP) {
// shift all successive elements back 1 step,
for(sp=ep; *sp != NULL; sp++) {
*sp = *(sp+1);
}
} else {
ep++;
}
}
return 0;
}
// setenv_impl() test
int setenv_impl_test() {
char *key = "name";
setenv_impl(key,"Eric", 1);
printf("%s\n", getenv(key));
setenv_impl(key,"Eric2", 0);
printf("%s\n", getenv(key));
setenv_impl(key,"Eric3", 1);
printf("%s\n", getenv(key));
return 0;
}
// unsetenv_impl() test
int unsetenv_impl_test() {
char *key = "name";
setenv_impl(key,"Eric", 1);
printf("%s\n", getenv(key));
unsetenv_impl(key);
char *val = getenv(key);
printf("%s\n", val==NULL?"NULL":getenv(key));
return 0;
}
int main(int argc, void *argv[]) {
// setenv_impl_test();
unsetenv_impl_test();
return 0;
}
In my setevn_impl(), I use malloc() to allocate memory for new environment variable.
But I don't know how the memory of process's default environment allocated.
My question is:
In my unsetenv_impl() implementation, is it necesary / proper to free the memory of removed environment string by free()?
If I don't free it, will it be a problem, or it won't take much memory thus could be ignored?
Tip:
putenv() won't duplicate the string, it just make global variable environ point to the string that pass to it.
In your case it is not necessary if you don't plan to set your environment variables very frequently leading to exhaust of your memory resources.
But it would be great if you always deallocate resources after you are done with using them, be it file handles/memory/mutexs. By doing so you will not make that sort of mistake when building servers.
Some servers are expected to run 24x7. In those cases, any leak of any sort means that your server will eventually run out of that resource and hang/crash in some way. A short utility program, ya a leak isn't that bad. Any server, any leak is death. Do yourself a favor. Clean up after yourself. It's a good habit

C, looping array of char* (strings) does't work. Why?

I have problem with my array of char*-
char *original_file_name_list[500];
while(dp=readdir(dir)) != NULL) {
original_file_name = dp->d_name;
original_file_name_list[counter] = original_file_name;
printf("%s\n",original_file_name_list[0]);
printf("%d\n",counter);
counter++;
}
The problem is, that it prints all files fine. It should print only first file, right?
And if I try printf("%s\n",original_file_name_list[1]); It doesn't work , which means that it is writing only in 1st string. Any idea why?
edit: There is no syntax error due to compiler.
You're not copying the string at all - also your file_name_list array hasn't enough space for a list of filenames - just for a list of pointers. But dp->d_name is just a pointer to a char* - you can't know for how long the memory behind the pointer is valid. Because of that you have to make a copy for yourself.
#include <string.h>
#include <dirent.h>
int main(int argc, char** argv){
char original_file_name_list[50][50];
size_t counter = 0;
while(dp=readdir(dir)) != NULL) // does work fine (ordinary reading files from dir)
{
size_t len = strlen(dp->d_name);
if(len >= 50) len = 49;
strncpy(original_file_name_list[counter], dp->d_name, len);
original_file_name_list[counter][len] = '\0';
printf("%d\n",counter);
counter++;
}
printf("%s\n",original_file_name_list[1]); // <- will work if you have at least 2 files in your directory
return 0;
}
I'm not sure about purpose of counter2 (I have replaced it with counter) but I can propose the following code with strdup() call to store the file names:
char *original_file_name_list[500] = {0}; // it is better to init it here
while(dp=readdir(dir)) != NULL) {
original_file_name_list[counter] = strdup(dp->d_name); // strdup() is ok to use
// here, see the comments
printf("%s\n%d\n",original_file_name_list[counter], counter);
counter++;
}
/* some useful code */
/* don't forget to free the items of list (allocated by strdup(..) )*/
for (int i = 0; i < 500; ++i) {
free(original_file_name_list[i]);
}

implementing strings queue in c: when do I have to use malloc

I have tried to implement a strings queue in c.
(Queue using an array)
But I get an unknown fly in my code.
1) I try to assign a string to the queue. Is my logic wrong?
static void enqueueInSearchEngineQueue(const char* res_name) {
if (searchEnginesNamesQueue_ItemsCount <= SEASRCH_ENGINES_QUEUE_MAX_SIZE) {
*searchEnginesNamesQueue[searchEnginesNamesQueue_ItemsCount] = malloc(sizeof(*res_name));
strcpy(searchEnginesNamesQueue[searchEnginesNamesQueue_ItemsCount] ,res_name);
searchEnginesNamesQueue_ItemsCount++;
}
else
{
// freeSearchEngingeQueue();
}
}
static int existInSearchEngingeQueue(const char* res_name) {
int i = 0;
int answer = 0;
for (i; i < searchEnginesNamesQueue_ItemsCount; i++) {
if (strcmp(searchEnginesNamesQueue[i], res_name) == 0) {
answer = 1;
break;
}
}
return answer;
}
static void freeSearchEngingeQueue() {
int i = 0;
for (i; i < searchEnginesNamesQueue_ItemsCount; i++) {
free(searchEnginesNamesQueue[i]);
}
searchEnginesNamesQueue_ItemsCount = 0;
}
static void searchEnginesIcons_download_callback(const char* res_name,
int success, void *context, char *last_modified) {
if (success) {
if (!existInSearchEngingeQueue(res_name)) {
enqueueInSearchEngineQueue(res_name);
#ifdef ANDROID
DriveToNativeManager_refreshSearchEnginesIconsOnSearchActivity(res_name);
#elif defined(IPHONE)
//TODO
refreshIconsOnSearchActivity();
#endif
}
}
}
2) callbacks from other part of my code fill the queue.
I have thought to use a memory on the stack, would it work or malloc is a must?
Yes, your code is broken.
You cannot check the length of a string passed to a function as a const char * using sizeof, you need to call strlen(), and add 1 for the terminator to figure out how memory to malloc().
The value of sizeof *res_name is constant, and simply sizeof (char), i.e. 1. So you are overwriting memory wildly, which causes undefined behavior.
This looks wrong:
*searchEnginesNamesQueue[searchEnginesNamesQueue_ItemsCount] = malloc(sizeof(*res_name));
You don't show the type definition, but the leading * is highly suspicious. Did you really want a dereference there? If that is deliberate, then it looks like it's missing on the following line, and elsewhere.
Also, that's not the way to get a length of a string. Use strlen instead.
Try this:
searchEnginesNamesQueue[searchEnginesNamesQueue_ItemsCount] = malloc(strlen(res_name)+1);

C interview question---run-length coding of strings [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
A friend of mine was asked the following question a Yahoo interview:
Given a string of the form "abbccc" print "a1b2c3". Write a function that takes a string and return a string. Take care of all special cases.
How would you experts code it?
Thanks a lot
if (0==strcmp(s, "abbccc"))
return "a1b2c3";
else
tip_the_interviewer(50);
Taken care of.
There's more than one way to do it, but I'd probably run over the input string twice: once to count how many bytes are required for the output, then allocate the output buffer and go again to actually generate the output.
Another possibility is to allocate up front twice the number of bytes in the input string (plus one), and write the output into that. This keeps the code simpler, but is potentially very wasteful of memory. Since the operation looks like a rudimentary compression (RLE), perhaps it's best that the first implementation doesn't have the output occupy double the memory of the input.
Another possibility is to take a single pass, and reallocate the output string as necessary, perhaps increasing the size exponentially to ensure O(N) overall performance. This is quite fiddly in C, so probably not the initial implementation of the function, especially in interview conditions. It's also not necessarily any faster than my first version.
However it's done, the obvious "special case" is an empty input string, because the obvious (to me) implementation will start by storing the first character, then enter a loop. It's also easy to write something where the output may be ambiguous: "1122" is the output for the input "122", but perhaps it is also the output for the input consisting of 122 1 characters. So you might want to limit run lengths to at most 9 characters (assuming base 10 representation) to prevent ambiguity. It depends what the function is for - conjuring a complete function specification from a single example input and output is not possible.
There's also more than one way to design the interface: the question says "returns a string", so presumably that's a NUL-terminated string in a buffer newly-allocated with malloc. In the long run, though, that's not always a great way to write all your string APIs. In a real project I would prefer to design a function that takes as input the string to process, together with a pointer to an output buffer and the length of that buffer. It returns either the number of bytes written, or if the output buffer isn't big enough it returns the number which would have been written. Implementing the stated function using this new function is easy:
char *stated_function(const char *in) {
size_t sz = new_function(in, NULL, 0);
char *buf = malloc(sz);
if (buf) new_function(in, buf, sz);
return buf;
}
I'm also confused what "print" means in the question - other answerers have taken it to mean "write to stdout", meaning that no allocation is necessary. Does the interviewer want a function that prints the encoded string and returns it? Prints and returns something else? Just returns a string, and is using "print" when they don't really mean it?
Follow the following algo and implement it.
Run a loop for all the letters in
string.
Store the first character in a temp
char variable.
For each change in character
initialize a counter with 1 and
print the count of previous
character and then the new letter.
This smells like a homework question, but the code was just too much fun to write.
The key ideas:
A string is a (possibly empty) sequence of nonempty runs of identical characters.
Pointer first always points to the first in a run of identical characters.
After the inner while loop, pointer beyond points one past the end of a run of identical characters.
If the first character of a run is a zero, we've reached the end of the string. The empty string falls out as an instance of the more general problem.
The space required for a decimal numeral is always at most the length of a run, so the result needs at most double the memory. The code works fine with a run length of 53: valgrind reports no memory errors.
Pointer arithmetic is beautiful.
The code:
char *runcode(const char *s) {
char *t = malloc(2 * strlen(s) + 1); // eventual answer
assert(t);
char *w = t; // writes into t;
const char *first, *beyond; // mark limits of a run in s
for (first = s; *first; first = beyond) { // for each run do...
beyond = first+1;
while (*beyond == *first) beyond++; // move to end of run
*w++ = *first; // write char
w += sprintf(w, "%d", beyond-first); // and length of run
}
*w = '\0';
return t;
}
Things I like:
No auxiliary variable for the character whose run we're currently scanning.
No auxiliary variable for the count.
Reasonably sparing use of other local variables.
As others have pointed out, the spec is ambiguous. I think that's fine for an interview question: the point may well be to see what the job applicant does in an ambiguous situation.
Here's my take on the code. I've made some assumptions (since I can't very well ask the interviewer in this case):
This is a simple form of run-length encoding.
Output is of the form {character}{count}.
To avoid ambiguity, the count is 1..9.
Runs of the same character longer than 9 are split into multiple counts.
No dynamic allocation is done. In C, it's usually better to let caller take care of that. We return true/false to indicate if there was enough space.
I hope the code is clear enough to stand on its own. I've included a test harness and some test cases.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void append(char **output, size_t *max, int c)
{
if (*max > 0) {
**output = c;
*output += 1;
*max -= 1;
}
}
static void encode(char **output, size_t *max, int c, int count)
{
while (count > 9) {
append(output, max, c);
append(output, max, '0' + 9);
count -= 9;
}
append(output, max, c);
append(output, max, '0' + count);
}
static bool rle(const char *input, char *output, size_t max)
{
char prev;
int count;
prev = '\0';
count = 0;
while (*input != '\0') {
if (*input == prev) {
count++;
} else {
if (count > 0)
encode(&output, &max, prev, count);
prev = *input;
count = 1;
}
++input;
}
if (count > 0)
encode(&output, &max, prev, count);
if (max == 0)
return false;
*output = '\0';
return true;
}
int main(void)
{
struct {
const char *input;
const char *facit;
} tests[] = {
{ "", "" },
{ "a", "a1" },
{ "aa", "a2" },
{ "ab", "a1b1" },
{ "abaabbaaabbb", "a1b1a2b2a3b3" },
{ "abbccc", "a1b2c3" },
{ "1", "11" },
{ "12", "1121" },
{ "1111111111", "1911" },
{ "aaaaaaaaaa", "a9a1" },
};
bool errors;
errors = false;
for (int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) {
char buf[1024];
bool ok;
ok = rle(tests[i].input, buf, sizeof buf);
if (!ok || strcmp(tests[i].facit, buf) != 0) {
printf("FAIL: i=%d input=<%s> facit=<%s> buf=<%s>\n",
i, tests[i].input, tests[i].facit, buf);
errors = true;
}
}
if (errors)
return EXIT_FAILURE;
return 0;
}
int priya_homework(char *input_str, char *output_str, int out_len)
{
char pc,c;
int count=0,used=0;
/* Check for NULL and empty inputs here and return*/
*output_str='\0';
pc=*input_str;
do
{
c=*input_str++;
if (c==pc)
{
pc=c;
count++;
}
else
{
used=snprintf(output_str,out_len,"%c%d",pc,count);
if (used>=out_len)
{
/* Output string too short */
return -1;
}
output_str+=used;
out_len-=used;
pc=c;
count=1;
}
} while (c!='\0' && (out_len>0));
return 0;
}
Damn, thought you said C#, not C. Here is my C# implementation for interest's sake.
private string Question(string input)
{
var output = new StringBuilder();
while (!string.IsNullOrEmpty(input))
{
var first = input[0];
var count = 1;
while (count < input.Length && input[count] == first)
{
count++;
}
if (count > input.Length)
{
input = null;
}
else
{
input = input.Substring(count);
}
output.AppendFormat("{0}{1}", first, count);
}
return output.ToString();
}
Something like this:
void so(char s[])
{
int i,count;
char cur,prev;
i = count = prev = 0;
while(cur=s[i++])
{
if(!prev)
{
prev = cur;
count++;
}
else
{
if(cur != prev)
{
printf("%c%d",prev,count);
prev = cur;
count = 1;
}
else
count++;
}
}
if(count)
printf("%c%d",prev,count);
printf("\n");
}

Resources