to copy at the correct place but doesn't stop after the count is reached. I thought my code should work as follows
char har *orig, int start, int count, char *final);
int main(void)
{
const char source[] = "one two three";
char result[] = "123456789012345678";
printf("%s\n",GetSubstring(source, 4, 3, result));
return 0;
}
char r *orig, int start, int count, char *final)
{
char *temp = (char *)orig;
final = temp;
}
for ( ; *temp && (count > 0) ; count--)
{
rn final;
}
The first for loop doesn't check if temp array exists (how would it check for existence of an allocated memory without asking memory manager in some way?!). The temp is merely a pointer. What you're checking for is that the orig string doesn't have a zero within the first start bytes. That's OK, perhaps' that's what you meant by "existence".
Your intention is to copy from orig to final, yet you reset final to orig. That's where your error is. You must remove that line and it fixes the problem.
You don't need to create the temp pointer, you can use the orig pointer. You're free to modify it -- remember, function arguments are in effects local variables. Function arguments in C are pass-by-value, you implement pass-by-reference by passing pointers (which are values!) to data.
I should add perhaps that the premise of this function is somewhat broken. It "works", but it's not what one might reasonably expect. Notably:
There's no indication that the source string was shorter than start.
There's no indication that the source string was shorter than start + count.
Perhaps those are OK, but in cases where those conditions could be an error, it should be possible for the user of the function to get an indication of it. The caller would know what's expected and what's not, so the caller can determine it if only you'd provide some feedback to the caller.
You're returning the position that's one past the end of the output -- past the zero-termination. That's not very convenient. If one were to use the returned value to concatenate a subsequent string, it'd have to be decremented by one first.
Below is the fixed code, with sanely named variables.
char *GetSub(const char *src, int start, int count, char *dst)
{
for ( ; *src && (start > 0) ; start--)
{
src++; /* Note: *src++ works too, but is pointless */
}
for ( ; *src && (count > 0) ; count--)
{
*dst++ = *src++;
}
*dst++ = 0;
return dst; /* Notice: This returns a pointer to the end of the
memory block you just wrote. Is this intentional? */
}
There are several problems in what you have written. Let's enumerate:
char *temp = (char *)orig; - You're assigning a const char * (you promise not to modify) to a char * (you break that promise). Wrong thing to be doing.
final = temp. No this does not make the original final (the copy held by the caller) change at all. It achieves nothing. It changes your (function's) copy of final to point to the same place that temp is pointing.
*temp++; - There's no point de-referencing it if you're not going to use it. Incrementing it of course, is correct [see comment thread with KubaOber below].
final++ = *temp++; - This is just confusing to read.
*final++ = 0; return final; - You're setting the value at the address final to '0'. Then you're incrementing it (to point to somewhere in space, maybe towards a black hole). Then returning that pointer. Which is also wrong.
What you really should do is to wrap strncpy in a convenient way.
But if you insist to write your own, you'd probably want your function to be something as simple as:
char *GetSub(const char *orig, int start, int count, char *final)
{
int i;
for (i = 0; i < count; i++)
{
final[i] = orig[i+start];
if (final[i] == '\0')
break;
}
final[i] = '\0';
return final; /* Yes, we just return what we got. */
}
The problem is with the following line:
final = temp;
Remove it, and the problem should be resolved.
char *a="abcdefgh";
i want string "cde" to be copied into another.
index i got is 3(your start).
char *temp=malloc(3*sizeof(char))
strncpy(temp,a+3,3);
is this what you need?
Change your GetSubfunction:
char *GetSub(const char *orig, int start, int count, char *final)
{
char *temp = (char *)orig;
// with original final = temp and final++ you loose final valid pointer
char *final2 = final;
for ( ; *temp && (start > 0) ; )
{
start--;
// you don't need to dereference temp
temp++;
}
for ( ; *temp && (count > 0) ; count--)
{
*final2++ = *temp++;
}
*final2 = 0;
// return a valid pointer
return final;
}
you have some mistakes on your code :
char *GetSub(const char *orig, int start, int count, char *final)
{
char *temp = (char *)orig;
//final = temp; /* Why this? */
for ( ; *temp && (start > 0) ; )
{
start--;
temp++; /* Instead of *temp++ */
}
for ( ; *temp && (count > 0) ; count--)
{
*final++ = *temp++;
}
*(final+count) = '\0';
return final;
}
Hope this help.
Related
char *ft_between(char *str, size_t from, size_t to)
{
char *between;
between = malloc(16);
while ((from >= 0) && (from < to) && (to < ft_strlen(str)))
{
*(between++) = str[from++];
}
*between = '\0';
printf("%s\n", between); // print nothing
printf("%s\n", between - 16); // print between but never had to do this before...
return (between);// even on calling function the pointer still at end of string
}
I think it's because I changed the address of between using ++ but I usually do that and never had this behavior... is that because of malloc ???
Is there someting I missed ?
Is thear a way to "rewind" the string lol
If I do it via a counter ie. between[counter++] = str[from++]; it works but I wanted to do via pointers as it's faster... from what I've red !
In this example str is iterate with ++ until the end to add char
but when return in calling function a printf will print all str
void ft_nbr2str(char *str, size_t nbr, char *base, size_t base_len)
{
if (nbr >= base_len)
{
ft_nbr2str(str, (nbr / base_len), base, base_len);
while (*str != '\0')
str++;
*str = base[nbr % base_len];
}
else
*str = base[nbr];
}
I think it's because I changed the address of between using ++
It's because you modified the value of between via the ++ operator. That value is the address of something else. The address of between or any other object cannot be modified.
but I usually do that and never had this behavior.
The behavior you describe is absolutely normal, so either no, you don't usually do that, or yes you did have that behavior. In your code, you will observe the same effect on from. I really don't fathom why immediately after you execute *between = '\0'; you would expect printf("%s\n", between) to print a non-empty string. malloc has nothing in particular to do with it.
I speculate that in other cases you may have instead modified a copy of your pointer, which, naturally, does not modify the original pointer. Possibly you did this by passing your pointer (by value) to another function. Example:
void strcpy_range(char *dest, char *src, size_t from, size_t to) {
while ((from >=0) && (from < to) && (src[from] != '\0'))
{
*(dest++) = src[from++]; // dest is modified
}
*dest = '\0';
}
char *ft_between(char *str, size_t from, size_t to)
{
char *between = malloc(16);
strcpy_range(between, str, from, to);
printf("%s\n", between); // prints the extracted substring
return between; // returns a pointer to the extracted substring
}
If you want to rescue your original version without introducing a new function, then use a temporary variable to track the current location in the substring. For example,
char *ft_between(char *str, size_t from, size_t to)
{
char *between = malloc(16);
char *temp = between;
while ((from >=0) && (from < to) && (to < ft_strlen(str)))
{
*(temp++) = str[from++];
}
*temp = '\0';
printf("%s\n", between); // prints the extracted substring
return between; // returns the extracted substring
}
Addendum
The alternative example added to the question demonstrates exactly the form I speculated you might have used. The (non-)effect on the caller's copy of the pointer in that case is not analogous to or even related to the modification of the function parameter observed during execution of the first function presented in the question.
After you've incremented the pointer, it now points to a different region of memory. Since the pointer is of type char, summing one unit is the same as summing sizeof(char) units, which turns out to still be 1; to 'rewind' it, as you say, you'd just have to subtract 16 * sizeof(char) = 16 (notice you're dereferencing the pointer summed by 16, so it makes perfect sense to subtract 16 to get it back to its position, or subtract however many times you want so that it points to the location you expect it to)
After this statement
*between = '\0';
the pointer between points to an empty string. So this call of printf:
printf("%s\n", between); // print nothing
indeed will output nothing.
And this return statement
return (between);// even on calling function the pointer still at end of string
returns this pointer to an empty string.
Pay attention to that this condition
(from >=0)
does not make sense because objects of the unsigned type size_t can not be negative.
Also it is unclear why there is used the magic number 16
between = malloc(16);
and
printf("%s\n", between - 16); // print between but never had to do this before...
And the function should not output any message. It is the caller of the function that will decide whether to output something.
The function can be declared and defined the following way
char * ft_between( const char *str, size_t from, size_t to )
{
char *between;
size_t n = ft_strlen( str );
if ( n < to ) to = n;
if ( to <= from )
{
between = calloc( 1, sizeof( char ) );
}
else
{
between = malloc( to - from + 1 );
if ( between != NULL )
{
char *p = between;
while ( from != to ) *p++ = str[from++];
*p = '\0';
}
}
return between;
}
For some functions for string manipulation, I try to rewrite the function output onto the original string. I came up with the general scheme of
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *char_repeater(char *str, char ch)
{
int tmp_len = strlen(str) + 1; // initial size of tmp
char *tmp = (char *)malloc(tmp_len); // initial size of tmp
// the process is normally too complicated to calculate the final length here
int j = 0;
for (int i = 0; i < strlen(str); i++)
{
tmp[j] = str[i];
j++;
if (str[i] == ch)
{
tmp[j] = str[i];
j++;
}
if (j > tmp_len)
{
tmp_len *= 2; // growth factor
tmp = realloc(tmp, tmp_len);
}
}
tmp[j] = 0;
char *output = (char *)malloc(strlen(tmp) + 1);
// output matching the final string length
strncpy(output, tmp, strlen(tmp));
output[strlen(tmp)] = 0;
free(tmp); // Is it necessary?
return output;
}
int main()
{
char *str = "This is a test";
str = char_repeater(str, 'i');
puts(str);
free(str);
return 0;
}
Although it works on simple tests, I am not sure if I am on the right track.
Is this approach safe overall?
Of course, we do not re-write the string. We simply write new data (array of the characters) at the same pointer. If output is longer than str, it will rewrite the data previously written at str, but if output is shorter, the old data remains, and we would have a memory leak. How can we free(str) within the function before outputting to its pointer?
A pair of pointers can be used to iterate through the string.
When a matching character is found, increment the length.
Allocate output as needed.
Iterate through the string again and assign the characters.
This could be done in place if str was malloced in main.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *char_repeater(char *str, char ch)
{
int tmp_len = strlen(str) + 1; // initial size of tmp
char *find = str;
while ( *find) // not at terminating zero
{
if ( *find == ch) // match
{
tmp_len++; // add one
}
++find; // advance pointer
}
char *output = NULL;
if ( NULL == ( output = malloc(tmp_len)))
{
fprintf ( stderr, "malloc peoblem\n");
exit ( 1);
}
// output matching the final string length
char *store = output; // to advance through output
find = str; // reset pointer
while ( *find) // not at terminating zero
{
*store = *find; // assign
if ( *find == ch) // match
{
++store; // advance pointer
*store = ch; // assign
}
++store; // advance pointer
++find;
}
*store = 0; // terminate
return output;
}
int main()
{
char *str = "This is a test";
str = char_repeater(str, 'i');
puts(str);
free(str);
return 0;
}
For starters the function should be declared like
char * char_repeater( const char *s, char c );
because the function does not change the passed string.
Your function is unsafe and inefficient at least because there are many dynamic memory allocations. You need to check that each dynamic memory allocation was successful. Also there are called the function strlen also too ofhen.
Also this code snippet
tmp[j] = str[i];
j++;
if (str[i] == ch)
{
tmp[j] = str[i];
j++;
}
if (j > tmp_len)
//...
can invoke undefined behavior. Imagine that the source string contains only one letter 'i'. In this case the variable tmp_len is equal to 2. So temp[0] will be equal to 'i' and temp[1] also will be equal to 'i'. In this case j equal to 2 will not be greater than tmp_len. As a result this statement
tmp[j] = 0;
will write outside the allocated memory.
And it is a bad idea to reassign the pointer str
char *str = "This is a test";
str = char_repeater(str, 'i');
As for your question whether you need to free the dynamically allocated array tmp
free(tmp); // Is it necessary?
then of course you need to free it because you allocated a new array for the result string
char *output = (char *)malloc(strlen(tmp) + 1);
And as for your another question
but if output is shorter, the old data remains, and we would have a
memory leak. How can we free(str) within the function before
outputting to its pointer?
then it does not make a sense. The function creates a new character array dynamically that you need to free and the address of the allocated array is assigned to the pointer str in main that as I already mentioned is not a good idea.
You need at first count the length of the result array that will contain duplicated characters and after that allocate memory only one time.
Here is a demonstration program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * char_repeater( const char *s, char c )
{
size_t n = 0;
for ( const char *p = s; ( p = strchr( p, c ) ) != NULL; ++p )
{
++n;
}
char *result = malloc( strlen( s ) + 1 + n );
if ( result != NULL )
{
if ( n == 0 )
{
strcpy( result, s );
}
else
{
char *p = result;
do
{
*p++ = *s;
if (*s == c ) *p++ = c;
} while ( *s++ );
}
}
return result;
}
int main( void )
{
const char *s = "This is a test";
puts( s );
char *result = char_repeater( s, 'i' );
if ( result != NULL ) puts( result );
free( result );
}
The program output is
This is a test
Thiis iis a test
My kneejerk reaction is to dislike the design. But I have reasons.
First, realloc() is actually quite efficient. If you are just allocating a few extra bytes every loop, then chances are that the standard library implementation simply increases the internal bytecount value associated with your memory. Caveats are:
Interleaving memory management.Your function here doesn’t have any, but should you start calling other routines then keeping track of all that becomes an issue. Anything that calls other memory management routines can lead to the next problem:
Fragmented memory.If at any time the available block is too small for your new request, then a much more expensive operation to obtain more memory and copy everything over becomes an issue.
Algorithmic issues are:
Mixing memory management in increases the complexity of your code.
Every occurrence of c invokes a function call with potential to be expensive. You cannot control when it is expensive and when it is not.
Worst-case options (char_repeater( "aaaaaaaaaa", 'a' )) trigger worst-case potentialities.
My recommendation is to simply make two passes.
This passes several smell tests:
Algorithmic complexity is broken down into two simpler parts:
counting space required, and
allocating and copying.
Worst-case scenarios for allocation/reallocation are reduced to a single call to malloc().
Issues with very large strings are reduced:
You need at most space for 2 large strings (not 3, possibly repeated)
Page fault / cache boundary issues are similar (or the same) for both methods
Considering there are no real downsides to using a two-pass approach, I think that using a simpler algorithm is reasonable. Here’s code:
#include <stdio.h>
#include <stdlib.h>
char * char_repeater( const char * s, char c )
{
// FIRST PASS
// (1) count occurances of c in s
size_t number_of_c = 0;
const char * p = s;
while (*p) number_of_c += (*p++ == c);
// (2) get strlen s
size_t length_of_s = p - s;
// SECOND PASS
// (3) allocate space for the resulting string
char * dest = malloc( length_of_s + number_of_c + 1 );
// (4) copy s -> dest, duplicating every occurance of c
if (dest)
{
char * d = dest;
while (*s)
if ((*d++ = *s++) == c)
*d++ = c;
*d = '\0';
}
return dest;
}
int main(void)
{
char * s = char_repeater( "Hello world!", 'o' );
puts( s );
free( s );
return 0;
}
As always, know your data
Whether or not a two-pass approach actually is better than a realloc() approach depends on more factors than what is evident in a posting on the internet.
Nevertheless, I would wager that for general purpose strings that this is a better choice.
But, even if it isn’t, I would argue that a simpler algorithm, splitting tasks into trivial sub-tasks, is far easier to read and maintain. You should only start making tricky algorithms only if you have use-case profiling saying you need to spend more attention on it.
Without that, readability and maintainability trumps all other concerns.
So I've looked around on SO and can't find code that answers my question. I have written a function that is supposed to reverse a string as input in cmd-line. Here is the function:
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string)];
for (x = strlen(string) - 1; x > 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
string = line;
}
When I call my reverse() function, the string stays the same. i.e., 'abc' remains 'abc'
If more info is needed or question is inappropriate, let me know.
Thanks!!
You're declaring your line array one char shorter remember the null at the end.
Another point, it should be for (x = strlen(string) - 1; x >= 0; x--) since you need to copy the character at 0.
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string) + 1];
for (x = strlen(string) - 1; x >= 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
for(x = 0; x < strlen(string); x++)
{
string[x] = line[x];
}
}
Note that this function will cause an apocalypse when passed an empty string or a string literal (as Bobby Sacamano said).
Suggestion you can probably do: void reverse(char source[], char[] dest) and do checks if the source string is empty.
I think that your answer is almost correct. You don't actually need an extra slot for the null character in line. You just need two minor changes:
Change the assignment statement at the bottom of the procedure to a memcpy.
Change the loop condition to <-
So, your correct code is this:
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string)];
for (x = strlen(string) - 1; x >= 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
memcpy(string, line, sizeof(char) * strlen(line));
}
Since you want to reverse a string, you first must decide whether you want to reverse a copy of the string, or reverse the string in-situ (in place). Since you asked about this in 'C' context, assume you mean to change the existing string (reverse the existing string) and make a copy of the string in the calling function if you want to preserve the original.
You will need the string library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Array indexing works, and this version takes that approach,
/* this first version uses array indexing */
char*
streverse_a(char string[])
{
int len; /*how big is your string*/
int ndx; /*because 'i' is hard to search for*/
char tmp; /*hold character to swap*/
if(!string) return(string); /*avoid NULL*/
if( (len=strlen(string)) < 2 ) return(string); /*one and done*/
for( ndx=0; ndx<len/2; ndx++ ) {
tmp=string[ndx];
string[ndx]=string[len-1-ndx];
string[len-1-ndx]=tmp;
}
return(string);
}
But you can do the same with pointers,
/* this is how K&R would write the function with pointers */
char*
streverse(char* sp)
{
int len, ndx; /*how big is your string */
char tmp, *bp, *ep; /*pointers to begin/end, swap temporary*/
if(!sp) return(sp); /*avoid NULL*/
if( (len=strlen(bp=sp)) < 2 ) return(sp); /*one and done*/
for( ep=bp+len-1; bp<ep; bp++, ep-- ) {
tmp=*bp; *bp=*ep; *ep=tmp; /*swap*/
}
return(sp);
}
(No, really, the compiler does not charge less for returning void.)
And because you always test your code,
char s[][100] = {
"", "A", "AB", "ABC", "ABCD", "ABCDE",
"hello, world", "goodbye, cruel world", "pwnz0r3d", "enough"
};
int
main()
{
/* suppose your string is declared as 'a' */
char a[100];
strcpy(a,"reverse string");
/*make a copy of 'a', declared the same as a[]*/
char b[100];
strcpy(b,a);
streverse_a(b);
printf("a:%s, r:%s\n",a,b);
/*duplicate 'a'*/
char *rp = strdup(a);
streverse(rp);
printf("a:%s, r:%s\n",a,rp);
free(rp);
int ndx;
for( ndx=0; ndx<10; ++ndx ) {
/*make a copy of 's', declared the same as s[]*/
char b[100];
strcpy(b,s[ndx]);
streverse_a(b);
printf("s:%s, r:%s\n",s[ndx],b);
/*duplicate 's'*/
char *rp = strdup(s[ndx]);
streverse(rp);
printf("s:%s, r:%s\n",s[ndx],rp);
free(rp);
}
}
The last line in your code does nothing
string = line;
Parameters are passed by value, so if you change their value, that is only local to the function. Pointers are the value of the address of memory they are pointing to. If you want to modify the pointer that the function was passed, you need to take a pointer to that pointer.
Here is a short example of how you could do that.
void reverse (char **string) {
char line = malloc(strlen(*string) + 1);
//automatic arrays are deallocated once the function ends
//so line needs to be dynamically or statically allocated
// do something to line
*string = line;
}
The obvious issue with this is that you can initialize the string with static memory, then this method will replace the static memory with dynamic memory, and then you'll have to free the dynamic memory. There's nothing functionally wrong with that, it's just a bit dangerous, since accidentally freeing the string literal is illegal.
char *test = "hello";
reverse(test);
free(test); //this is pretty scary
Also, if test was allocated as dynamic memory, the pointer to it would be lost and then it would become a memory leak.
I am trying to make a function that removes double letters from a string. The function is only supposed to remove double letters next to each other, not in the whole string. e.g 'aabbaa' would become 'aba' (not 'ab'). Im a fairly new to c programming and dont fully understand pointers etc. and need some help. Below is what I have so far. It does not work at all, and I have no idea what to return since when I try and return string[] it has an error:
char doubleletter( char *string[] ) {
char surname[25];
int i;
for((i = 1) ; string[i] != '\0' ; i++) {
if (string[i] == string[(i-1)]) { //Supposed to compare the ith letter in array with one before
string[i] = '\0' ; //Supposed to swap duplicate chars with null
}
}
surname[25] = string;
return surname ;
Try the following. It is a clear simple and professionally-looked code.:)
#include <stdio.h>
char * unique( char *s )
{
for ( char *p = s, *q = s; *q++; )
{
if ( *p != *q ) *++p = *q;
}
return s;
}
int main(void)
{
char s[] = "aabbaa";
puts( unique( s ) );
return 0;
}
The output is
aba
Also the function can be rewritten the following way that to escape unnecassary copying.
char * unique( char *s )
{
for ( char *p = s, *q = s; *q++; )
{
if ( *p != *q )
{
( void )( ( ++p != q ) && ( *p = *q ) );
}
}
return s;
}
Or
char * unique( char *s )
{
for ( char *p = s, *q = s; *q++; )
{
if ( *p != *q && ++p != q ) *p = *q;
}
return s;
}
It seems that the last realization is the best.:)
First of all delete those parenthenses aroung i = 1 in for loop (why you put them there in the first place ?
Secondly if you put \0 in the middle of the string, the string will just get shorter.
\0 terminates array (string) in C so if you have:
ababaabababa
and you replace second 'a' in pair with \0:
ababa\0baba
effectively for compiler it will be like you just cut this string to:
ababa
Third error here is probably that you are passing two-dimensional array to function here:
char *string[]
This is equivalent to passing char **string and essentialy you are passing array of strings while you wanna only to pass a string (which means a pointer, which means an array: char *string or ofc char string[])
Next thing: you are making internal assumption that passed string will have less than 24 chars (+ \0) but you don't check it anywhere.
I guess easiest way (though maybe not the most clever) to remove duplicated chars is to copy in this for loop passed string to another one, omitting repeated characters.
One example, It does not modify input string and returns a new dynamically allocated string. Pretty self explanatory I think:
char *new_string_without_dups(const char *input_str, size_t len)
{
int i = 1;
int j = 0;
char tmpstr[len+1] = {0};
for (; i < len; i++) {
if (input_str[i] == input_str[i-1]) {
continue;
}
tmpstr[j] = input_str[i];
j++;
}
return strdup(tmpstr);
}
Don't forget to free the returned string after usage.
Note that there are several ways to adapt/improve this. One thing now is that it requires C99 std due to array size not being known at compile time. Other things like you can get rid of the len argument if you guarantee a \0 terminated string as input. I'll leave that as excercises.
Your idea behind the code is right, but you are making two fundamental mistakes:
You return a char [] from a function that has char as return type. char [], char * and char are three different types, even though in this case char [] and char * would behave identically. However you would have to return char * from your function to be able to return a string.
You return automatically allocated memory. In other languages where memory is reference counted this is OK. In C this causes undefined behavior. You cannot use automatic memory from within a function outside this very function. The memory is considered empty after the function exits and will be reused, i.e. your value will be overwritten. You have to either pass a buffer in, to hold the result, or do a dynamic allocation within the function with malloc(). Which one you do is a matter of style. You could also reuse the input buffer, but writing the function like that is undesirable in any case where you need to preserve the input, and it will make it impossible for you to pass const char* into the function i.e. you would not be able to do do something like this:
const char *str = "abbc";
... doubleletter(str,...);
If I had to write the function I would probably call it something like this:
int doubleletter (const char *in, size_t inlen, char *out, size_t outlen){
int i;
int j = 0;
if (!inlen) return 0;
if (!outlen) return -1;
out [j++] = in[0];
for (i = 1; i < inlen; ++i){
if (in[i - 1] != in[i]){
if (j > outlen - 1) return -1;
out[j++] = in[i];
}
}
out[j] = '\0';
return j - 1;
}
int main(void) {
const char *str1 = "aabbaa";
char out[25];
int ret = doubleletter(str1, strlen(str1), out, sizeof(out)/sizeof(out[0]));
printf("Result: %s", out);
return 0;
}
I would recommend using 2 indices to modify the string in-place:
void remove_doubles(char *str)
{
// if string is 1 or 0 length do nothing.
if(strlen(str)<=1)return;
int i=0; //index (new string)
int j=1; //index (original string)
// loop until end of string
while(str[j]!=0)
{
// as soon as we find a different letter,
// copy it to our new string and increase the index.
if(str[i]!=str[j])
{
i++;
str[i]=str[j];
}
// increase index on original/old string
j++;
}
// mark new end of string
str[i+1]='\0';
}
I'm new to C and having trouble wrapping my head around double pointers and keep getting segmentation fault errors. I've debugged the program a bit and located where things go wrong, but can't for the life of me figure out why. I'll post my code first:
int main() {
printf("Enter string to be split: \n");
a = readline();
String *st = newString(a);
String **split;
int num;
num = string_split(st, ',', split);
for (i=0; i<num; i++) { print_string(*(split+i)); }
}
readline() produces a pointer to an array of chars (entered by the user) and appends '\0' to it. newString and print_string definitely work. Here's the struct for string:
typedef struct {
char *chars;
int length;
int maxSize;
} String;
And here is the code for string_split which is causing me all this trouble.
int string_split(String *s, char delim, String **arrayOfStructs) {
char *c = getCharacters(s);
int len = length(s);
int begin = 0;
int end;
int arraycount = 0;
String **temp = (String**)malloc(sizeof(String*));
for (end=0; end<len+1; end++) {
if ((*(c+end) == delim || *(c+end) == '\0') && begin != end) {
String *st = substring(s,begin,end-1);
*(temp + arraycount) = st;
begin = end + 1;
arraycount++;
temp = (String**)realloc(temp, 1+arraycount*sizeof(String*));
}
}
arrayOfStructs = temp;
return arraycount;
}
In main, when I get back split, all the String*'s that it points too are gone. When print_string gets an individual String* and tries to grab one of its members, a segmentation fault occurs. I don't understand why, because I feel like I allocate memory every time it is necessary, but I feel like I'm missing something. Also, when debugging, if I step through string_split, temp is produced exactly like I expect, so I think I'm just not malloc'ing somewhere where I'm supposed to and it's not a problem with the logic of the function. Here is the code in substring, although I'm pretty sure it works since I've been able to return String* from substring and pass them to print_string just fine.
String *substring(String *s1, int begin, int end) {
String *s = (String*)malloc(sizeof(String));
int length = 0;
s->maxSize = 20;
char *temp = (char*)malloc(20*sizeof(char));
char *arr = s1->chars;
int i;
for (i=begin; i <= end; i++) {
*(temp+length) = *(arr+i);
length++;
if (length == s1->maxSize-1) {
s1->maxSize = s1->maxSize+20;
temp = (char*)realloc(temp, s1->maxSize*sizeof(char));
}
}
*(temp+length) = '\0';
s->length = length;
s->chars = temp;
return s;
}
Any help is greatly appreciated!
You need to pass the argument arrayOfStructs by reference and not by value. As C doesn't actually have proper references, you have to pass a pointer to the variable:
int string_split(String *s, char delim, String ***arrayOfStructs) {
...
*arrayOfStructs = temp;
return arraycount;
}
Call it using the address-of operator &:
num = string_split(st, ',', &split);
As it is now, you pass the argument by value, which means that the variable arrayOfStructs is just a local copy inside the function. Any changes to it is only made to the copy, and are lost once the variable goes out of scope when the function returns.
String **temp = (String**)malloc(sizeof(String*));
*(temp + arraycount) = st;
temp+arraycount is going to give you a random address in memory. temp contains the pointer you just malloced, which should point to another pointer.(which you have not initialised), but you are incrementing the pointer so you loose the location you just malloced.
temp is not pointing to consecutive memory, it specifically points to another pointer(which is 8bytes on a 64bit machine)