i feel kinda lost, since we started learning about pointers i kinda cant follow and i know its really important subject in C.
anyway!
so i got to make a recursive function, that will get 2 pointers:
1) pointer to index [0].
2) pointer 2 to the middle of the string.
now.. i gotta check if the first part from 0 to middle is equal from middle to end. like..... ADAMADAM.
before i transfer the string i changed entire lower letters to capitals to avoid case sensitivity... so i got something like this.. but its refusing to work.
also using constant is prohibited...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define SS 81
int CheckString(char *,int *);
int main() {
char str[SS];
int length,len,strcheck;
printf("Please enter a string:\n");
fgets(str,SS,stdin);
len=(strlen(str) - 1);
if((len>0)&&(str[len]=='\n')) // replacing '\n' key to '\0'
str[len]='\0';
length=len/2;
strcheck=CheckString(str,&length);
if (strcheck==FALSE)
printf("FALSE.\n");
else
printf("TRUE.\n");
return 0;
}
// function
int CheckString(char *str, int *i) {
if (*str != '\0')
if (*str == str[*i])
return CheckString(str+1,i++);
else
return FALSE;
return TRUE;
}
so i guess i got some problem with the pointers
It seems you mean the following
#include <stdio.h>
#include <string.h>
int CheckString(const char *s, size_t *i)
{
return s[*i] == '\0' || *s == s[*i] && CheckString(s + 1, i);
}
int main( void )
{
char *s = "ADAMADAM";
size_t i = strlen(s) / 2;
int result = CheckString(s, &i);
printf("%s\n", result ? "true" : "false");
return 0;
}
The program output
true
Note: maybe you should calculate the value for the second argument the following way
size_t i = ( strlen(s) + 1 ) / 2;
Think about this.
The outer condition in the loop inside CheckString() should be checking for *(str + *i) != '\0', or equivalently, for str[*i] != '\0'. Also, you do not need to increment *i, and certainly not i, since that is a pointer. The value *i is the distance between the characters being checked in the two halves of the string.
The modified function could look like:
int CheckString(char *str, int *i) {
if (str[*i] != '\0') {
if (*str == str[*i]) {
return CheckString(str+1,i);
} else {
return FALSE;
}
}
return TRUE;
}
The problem specification says (more or less):
I've got to make a recursive function that will get 2 pointers:
pointer 1 to index [0].
pointer 2 to the middle of the string.
I've got to check if the first part from 0 to middle is equal to the second part from middle to end, like: ADAMADAM.
As an exercise in recursion, this is fine; as a way of implementing the functionality, recursion is overkill (iteration is fine).
There is confusion (ambiguity) about the interface to the function — the wording of the question seems to suggest two char * values, but the code uses a pointer to an integer as the second argument. That's singularly peculiar. An integer value could make sense, but a pointer to an integer does not.
We need to define the conditions carefully. Taking the example string given (char str1[] = "ADAMADAM";), the two pointers might be char *p1 = &str1[0]; char *p2 = &str1[0] + strlen(str1) / 2; — meaning p1 points to the first A and p2 to the third A. Consider an alternative string: char str2[] = "MADAMADAM";; The equivalent formula would leave p1 pointing at the first M and p2 pointing at the second M.
Assuming p1 and p2 are incremented in lock-step, then:
The strings are different if, at any point before *p2 equals '\0', *p1 != *p2.
If *p2 equals '\0', then the strings are the same.
By definition, p1 and p2 point to the same array, so pointer differences are legitimate.
Further, p1 must be less than p2 to be useful; p1 equal to p2 means the strings are identical trivially.
There is a strong argument that the 'middle of the string' criterion means that either p2[p2 - p1] == '\0' or p2[p2 - p1 + 1] == '\0' (for even and odd string lengths respectively). That is, the distance between the two pointers indicates where the end of the string must be. It means that using p1 = &str[0] and p2 = &str[2] (on either of the sample strings) should fail because the end of string isn't in the right place. And if the string was "AMAMAMAM", using &str[0] and &str[2] should fail because the end of string isn't in the right place; ditto &str[0] and &str[6].
However, this 'strong argument' is also a design decision. It would be feasible to simply demand that the substring from p2 to EOS (end of string) is the same as the string from p1 for the same length. In that case, using &str[0] with either &str[2] or &str[6] (or, indeed, with the normal &str[4]) on "AMAMAMAM" would work fine.
Using some of these observations leads to this code. If you're really under instructions not to use const, simply remove the const qualifiers where they appear. The code will work the same.
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
static bool identical_halfstrings(const char *p1, const char *p2)
{
assert(p1 <= p2);
assert(strlen(p1) >= strlen(p2) + (p2 - p1));
if (*p2 == '\0')
return true;
if (*p1 != *p2)
return false;
return identical_halfstrings(p1+1, p2+1);
}
int main(void)
{
const char *strings[] =
{
"ADAMADAM",
"MADAMADAM",
"nonsense",
};
enum { NUM_STRINGS = sizeof(strings) / sizeof(strings[0]) };
for (int i = 0; i < NUM_STRINGS; i++)
{
const char *p1 = strings[i];
const char *p2 = strings[i] + strlen(strings[i]) / 2;
printf("[%s] ([%s]) = %s\n", p1, p2,
identical_halfstrings(p1, p2) ? "TRUE" : "FALSE");
}
return 0;
}
The second assertion ensures that p1 and p2 are pointing to the same string — that there isn't a null byte between the locations pointed at by p1 and p2.
Test case output:
[ADAMADAM] ([ADAM]) = TRUE
[MADAMADAM] ([MADAM]) = TRUE
[nonsense] ([ense]) = FALSE
Just for the record, an iterative version of the same function is:
static bool identical_halfstrings(const char *p1, const char *p2)
{
assert(p1 <= p2);
assert(strlen(p1) >= strlen(p2) + (p2 - p1));
while (*p2 != '\0')
{
if (*p1++ != *p2++)
return false;
}
return true;
}
It produces the same output for the sample data.
Related
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.
I have trouble with my code and I need your help! What I need to do is to write a function that will extract the web address that starts from www. and ends with .edu from an inputted string. The inputted string will have no spaces in it so scanf() should work well here.
For example:
http://www.school.edu/admission. The extracted address should be www.school.edu.
This is what I came up with so far, it obviously didn't work, and I can't think of anything else unfortunately.
void extract(char *s1, char *s2) {
int size = 0;
char *p, *j;
p = s1;
j = s2;
size = strlen(s1);
for(p = s1; p < (s1 + size); p++) {
if(*p == 'w' && *(p+1) == 'w' && *(p+2) == 'w' && *(p+3) == '.'){
for(p; p < (p+4); p++)
strcat(*j, *p);
}
else if(*p=='.' && *(p+1)=='e' && *(p+2)=='d' && *(p+3)=='u'){
for(p; (p+1) < (p+4); p++)
strcat(*j, *p);
}
}
size = strlen(j);
*(j+size+1) = '\0';
}
The function has to use pointer arithmetic. The errors I get have something to do with incompatible types and casting. Thanks ahead!
So the most trivial approach might be:
#include <stdio.h>
int main(void)
{
char str[1000];
sscanf("http://www.school.edu/admission", "%*[^/]%*c%*c%[^/]", str);
puts(str);
}
Now, here goes the fixed code:
#include <stdio.h>
#include <string.h>
void extract(char *s1, char *s2) {
size_t size = strlen(s1), i = 0;
while(memcmp(s1 + i, "www.", 4)){
i++;
}
while(memcmp(s1 + i, ".edu", 4)){
*s2++ = *(s1 + i);
i++;
}
*s2 = '\0';
strcat(s2, ".edu");
}
int main(void)
{
char str1[1000] = "http://www.school.edu/admission", str2[1000];
extract(str1, str2);
puts(str2);
}
Note that s2 must be large enough to contain the extracted web address, or you may get a segfault.
This is an easy solution for your problem:
char* extract(char *s1) {
char* ptr_www;
char* ptr_edu;
int len ;
char* s2;
ptr_www = strstr(s1,"www");
ptr_edu = strstr(s1,".edu");
len = ptr_edu -ptr_www + 4;
s2 = malloc (sizeof(char)*len+1);
strncpy(s2,ptr_www,len);
s2[len] = '\0';
printf ("%s",s2);
return s2;
}
There is a lot wrong unfortunately. Your compilation is failing because you pass a char to strcat when it expects a char*. Even if it did compile though it would crash.
for(p = s1; p < (s1 + size); p++) {
// This if statement will reference beyond s1+size when p=s1+size-2. Consequently it may segfault
if(*p=='w' && *(p+1)=='w' && *(p+2)=='w' && *(p+3)=='.') {
for(p; p < (p+4); p++) // This is an infinite loop
// strcat concatenates one string onto another.
// Dereferencing the pointer makes no sense.
// This is the likely causing your compilation error.
// If this compiled it would almost certainly segfault.
strcat(*j, *p);
}
// This will also reference beyond s1+size. Consequently it may segfault
else if(*p=='.' && *(p+1)=='e' && *(p+2)=='d' && *(p+3)=='u') {
for(p; (p+1) < (p+4); p++) // This is also an infinite loop
// Again strcat expects 2x char* (aka. strings) not 2x char
// This will also almost certainly segfault.
strcat(*j, *p);
}
}
// strlen() counts the number of chars until the first '\0' occurrence
// It is never correct to call strlen() to determine where to add a '\0' string termination character.
// If the character were actually absent this would almost certainly result in a segfault.
// As it is strcat() (when called correctly) will add the terminator anyway.
size = strlen(j);
*(j+size+1) = '\0';
EDIT: This seems like a homework question, so I thought it would be more constructive to mention where your current code is going wrong, so you can recheck your knowledge in those areas.
The answer to your exact question is it doesn't compile because you dereference the string and hence pass 2x char instead of char* to strcat().
I have created a function for strend, which basically returns 1 if string t is present at the end of string s, however it never returns 1:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int strend(char *s, char *t) {
int p;
for (p = 0; p < strlen(s) - strlen(t); p++) {
*s++;
}
printf("%s\n%s\n", s, t);
if (s == t)
return 1;
return 0;
}
int main(void) {
int bool = strend("Hello", "ello");
printf("%i\n", bool);
return 0;
}
This gives me an output of:
ello
ello
0
So technically I should get 1. I assume the comparison using pointers is not used in this way?
You need to review your basic knowledge of C strings. There are lots of standard string functions in string.h that can help you with this test.
The basic problem is that the test s == t is valid, but you are comparing memory addresses here. You can see that is valid if you change the strings to test to
char test[] = "Hello";
int bool = strend_(test, test+1);
where test obviously is the same as your "Hello", and similarly, test+1 is the same as "ello" (try it by printing them). This correctly returns 1 with your routine.
In addition, I get two warnings:
on *s++; "warning: expression result unused [-Wunused-value]": you increment s but also ask what character is at that position through *s; and you don't use that information.
Fix by removing the * there.
on p < strlen(s) ..; "warning: comparison of integers of different signs: 'int' and 'unsigned long'", because strlen does not return a signed integer but an unsigned one (apparently, my header uses unsigned long).
Fix by declaring p as unsigned long, or even better, size_t.
Your entire routine can be condensed to a simple
int strend (char *s, char *t)
{
if (strlen(s) >= strlen(t) && !strcmp (s+strlen(s)-strlen(t),t))
return 1;
return 0;
}
It's not worth the trouble to cache the result of those four strlen calls into 2 temporary variables; a good compiler will work it out and do that for you. (A quick glance to the assembly output of the compiler I'm using – clang – shows it does, even with the default optimization settings.)
A slightly modified test, based on #M.M.'s comment:
int strend (char *s, char *t)
{
if (strlen(s) < strlen(t)) return 0;
return !strcmp (s+strlen(s)-strlen(t),t);
}
but attempting to optimize it this way is not as easy parsed as the routine above, and its assembly is ever so slightly "wordy" as well. Personally, I'd go for the more humanly readable version.
Use strcmp(3)
if (strcmp(s, t) == 0) return 1;
This actually compares the contents of the memory pointed to by s and t rather than their addresses.
Your code is broken in multiple ways:
The initial loop is a very cumbersome way to advance p by the difference of lengths if positive.
Once you have pointers at the same distance from the end of both strings, You should compare the characters with strcmp() (or memcmp() if you can first exclude the case of strlen(s) < strlen(t).
Comparing the pointers obtained after the loop will only work if t points inside the string pointed to by s, a special case that may or may not be produced by the compiler for the specific call in main: strend("Hello", "ello");.
Here is a modified version:
#include <string.h>
int strend(const char *str1, const char *str2) {
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
return len1 >= len2 && !memcmp(str1 + len1 - len2, str2, len2);
}
I corrected/modified your code, here is the code,
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#pragma warning(disable:4996)
int strend(char *s, char *t)
{
int p,flag=0,count=0;//count will be the starting index for *t
p = strlen(s) - strlen(t);//this will be the starting index for *s
while(count<strlen(t))
{
if (*(s+p) == *(t+count))
{
flag = 1;
count++;
p++;
continue;
}
else
{
flag = 0;
break;
}
}
return flag;
}
int main(void)
{
int flag = strend("Hello", "ello");
printf("%i\n", flag);
return 0;
}
This code works too.
#include <stdio.h>
#include <string.h>
int strend (char *s1, char *s2);
void main ()
{
char str1[20] = "somethings";
char str2[20] = "things";
int f;
f = strend (str1,str2);
if (f==1)
printf ("1");
else
printf ("0");
}
int strend (char *str1, char *str2)
{
int l = strlen(str1) - strlen(str2);
str1 = str1 + l;
int d = strcmp(str1,str2);
if (d == 0)
return 1;
else
return 0;
}
this code works well.
int strend(char *s, char *t){
while(*t & *s){
if(*t == *s){
t++;
}
s++;
}
return *t==*s;
}
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';
}
Here is my function's code:
char * increment_b_string(char * b_string)
{
char * ret_string = (char *) malloc(7);
ret_string = "001aaa";
if (*(b_string + 2) == '9')
{
*(ret_string +2) == '0';
if (*(b_string + 1) == '9')
{
*(ret_string +1) = '0';
*(ret_string) = *(b_string) + 1;
} else {
*(ret_string + 1) = *(b_string + 1) + 1;
}
} else {
*(ret_string + 2) = *(b_string + 2) + 1;
}
return ret_string;
}
Any thoughts as to why it might be failing?
The general idea is that b_string will contain a value like "123aaa". The "aaa" portion does not matter, and will never change. Only the first 3 numbers will. They need to increment as though they were integers. Leading zeroes need to be preserved in the beginning. So, if input is "004aaa", output should be "005aaa". This only needs to go up to "300aaa", hence why I'm not considering anything more than that. It's a school programming assignment, hence the very contrived nature of the problem.
Thanks.
Edit: I've changed my function. Here is what it is now.
void increment_b_string(char * b_string)
{
if (b_string[2] == '9')
{
b_string[2] == '0';
if (b_string[1] == '9')
{
b_string[1] = '0';
b_string[0] += 1;
} else {
b_string[1] += 1;
}
} else {
b_string[2] += 1;
}
}
Assuming that b_string is initially populated with...
strcpy(b_string, "001aaa");
...would this be correct? My program is still exhibiting the same behavior, but this may be an error somewhere else in the code.
You cannot increment or change a string literal - they are read only in C, which is what you're doing.
char * ret_string = (char *) malloc(7);
ret_string = "001aaa";
Does not do what you think it does, it does not copy the string into the malloced spaceret_string points to, it sets ret_string to point to the string literal "001aaa". Use this instead:
char * ret_string = (char *) malloc(7);
strcpy(ret_string,"001aaa");
For one, I would initialize ret_string through:
char * ret_string = strdup("001aaa");
You currently assign ret_string to a string literal, which you don't want to do when you are going to modify the string.
Note that you should call free(ret_string) when you are done with it. The implementation of strdup contains a malloc.
Second, you may have an easier time going through the logic of the function if you use the syntax b_string[i] instead of *(b_string + i). They are equivalent.
Edit:
Since strdup isn't ANSI C, you could always define your own function with the same functionality, something like:
char *strdup (const char *s) {
char *d = (char *)(malloc (strlen (s) + 1));
if (d == NULL) return NULL;
strcpy (d,s);
return d;
}
Reference:
strdup() - what does it do in C?
None of this has been tested, but the following should work well.
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
/* Many libc implementations have strdup, but some don't */
char * strdup(const char * str) {
char * copy = malloc(strlen(str)+1);
if (copy) {
strcpy(copy, str);
}
return copy;
}
/* This increments the first ASCII integer it finds within the string by one.
* The integer is treated as though it is base 10 for the purposes of incrementing
* and in the event that the upper digit is '9' before the increment it is '0' after
* the increment and the carry digit is lost.
*/
char * increment_b_string_inplace(char * str) {
char * s = str;
while (*s) { /* Look for where the number starts. */
char c = *s;
if (isdigit(c)) {
/* This is the start of a number! */
char * e = s+1;
unsigned carry;
while (isdigit(*++e) ) {;} /* find where the number ends */
e--; /* we went 1 too far, so back up */
do { /* Do the actual increment. ][
unsigned i = *e - '0';
i++;
carry = i % 10; /* this should always be 0 or 1 */
*e = (char)('0' + (i / 10) );
e--;
} while (e<=s && carry);
}
}
return str;
}
/* This is the function you originally wanted. */
char * increment_b_string(const char * b_string)
{
char * str = strdup(b_string);
if (!str) {
return str;
}
return increment_b_string_inplace(str);
}
You could also have read the entire integer into an int variable, incremented that, and then turned that back into a string.
Below is code to achieve what I think you're wanting. Included is a re-write of your increment_b_string. It uses strtol (string to long) and sprintf to convert from string to int and back, respectively. An assumption is that the character string on which increment_b_string will operate is always of dddccc format (d = digit, c = character).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * increment_b_string(char * b_string)
{
char * ret_string;
int int2inc;
ret_string = malloc(sizeof(b_string));
int2inc = (int) strtol(b_string, ret_string, 10);
int2inc ++;
sprintf(ret_string, "%03d%s", int2inc, &b_string[3]);
return ret_string;
}
int main(void) {
char the_string[7] = "000aaa";
printf("before, the_string = \"%s\"\n", the_string);
strcpy(the_string, increment_b_string(the_string));
printf("after, the_string = \"%s\"\n", the_string);
}
The output is:
before, the_string = "000aaa"
after, the_string = "001aaa"
Also, your ret_string will have one or two zeroes at the front that should be something else, if the else clauses are exercised. To fix this, you need to initialize ret_string from b_string, not from a constant:
char * ret_string = strdup(b_string);
a solution with strictly C89, without malloc/strdup and more flexible for variable input-strings:
char *inc_bstr(char *str)
{
char *e,f[] = "%03ld%s";
long i = strtol(str,&e,10)+1;
f[2] = '0'+(e-str);
sprintf(str,f,i>300?300:i,e);
return str;
}
...
char x1[]="0aaa";
puts(inc_bstr(x1));
...
char x2[]="00aaa";
puts(inc_bstr(x2));
...
char x3[]="000aaa";
puts(inc_bstr(x3));
...
char x4[]="0000aaa";
puts(inc_bstr(x4));
...
char x5[]="00000aaa";
puts(inc_bstr(x5));
...
char x6[]="0000bb";
puts(inc_bstr(x6));
...
char x7[]="000";
puts(inc_bstr(x7));
...
char x8[]="000xxx",z=99;
while( z-- )
puts(ins_bstr(x8));