C: the pointer to pointer issue - c

Following is my code, I want to use pointer to pointer to store strings.
char **BlankWords(char word[]){
// take word 'lad' as an example
// length of it is 3
// fill in blank: _ad, l_d, la_, _lad, l_ad, la_d, lad_
// 3 + 4 = 7
// which means, length of 'lad' + (length of 'lad') + 1
int strLength = strlen(word);
char **blank_words = malloc(sizeof(char*) * (2 * strLength + 1));
assert(blank_words != NULL);
int i, j, k;
for (i = 0; i < strLength; i++){
// allocate memory for each length of the word
blank_words[i] = calloc(MAX_WORD_LENGTH, sizeof(char));
assert(blank_words[i] != NULL);
char temp[MAX_WORD_LENGTH];
strcpy(temp, word);
temp[strLength] = '\0';
temp[i] = '_';
blank_words[i] = temp;
// printf("%s\n", blank_words[0]);
}
for (j = strLength; j < (2 * strLength + 1); j++){
// allocate memory for each length of the word
blank_words[j] = calloc(MAX_WORD_LENGTH, sizeof(char));
assert(blank_words[j] != NULL);
char temp[MAX_WORD_LENGTH];
strcpy(temp, word);
temp[(strlen(temp) + 1)] = '\0';
for (k = (strLength - 1); k >= (j - strLength); k--){
if (k >= 0){
temp[k + 1] = temp[k]; // in order to insert '_' to the word, then the other letter move back one
}
}
temp[j - strLength] = '_'; // insert '_' to the word
blank_words[j] = temp;
}
return blank_words;
}
Following is the output, each row was overwritten after each loop, but in my opinion, each row cannot be overwritten, and may store a unique string.
blank_words[0]: lab_
blank_words[1]: lab_
blank_words[2]: lab_
blank_words[3]: lab_
blank_words[4]: lab_
blank_words[5]: lab_
blank_words[6]: lab_
I don't know why the previous data gets overwritten after each loop. In my opinion, the output should be:
blank_words[0]: _ab
blank_words[1]: l_b
blank_words[2]: la_
blank_words[3]: _lab
blank_words[4]: l_ab
blank_words[5]: la_b
blank_words[6]: lab_

As others have said, a local buffer disappears when its scope closes. Since the char** array points to buffers of that sort, the result is undefined behavior. You'll need to allocate the result strings with malloc.
Another tip: You can build the second set of strings by just moving the underscore rather than creating each from scratch. This is simpler:
#include <stdlib.h>
#include <string.h>
#include <assert.h>
void *safe_malloc(size_t n) {
void *r = malloc(n);
assert(r);
return r;
}
char *stralloc(char *s) {
return strcpy(safe_malloc((strlen(s) + 1) * sizeof(char)), s);
}
char **variations(char *s) {
int len = strlen(s), rp = 0;
char **r = safe_malloc((2 * len + 1) * sizeof *r);;
char buf[len + 2];
strcpy(buf, s); // Copy in case s is a read-only literal.
for (int i = 0; i < len; ++i) {
char t = buf[i]; // Remember the i'th char.
buf[i] = '_'; // Overwrite with _.
r[rp++] = stralloc(buf); // Capture a copy.
buf[i] = t; // Replace original char.
}
buf[0] = '_'; // Make the 1st char _.
strcpy(buf + 1, s); // Copy the rest after.
r[rp++] = stralloc(buf); // Capture a copy.
for (int i = 0; i < len; ++i) {
buf[i] = buf[i + 1]; // Overwrite _ with following char.
buf[i + 1] = '_'; // Move the _ up one position.
r[rp++] = stralloc(buf); // Capture a copy.
}
return r;
}

Related

String allocated with malloc is only returning the first character

I'm writing a function to rearrange a string's characters. I allocated the new string with malloc and initialize it, and then return it. But when I return it, it always only returns the first character.
I've tried printing the characters of the new string one by one and they print out correctly, but when I call the function, only the first character is returned. I can't figure out why. Any help would be appreciated!
Here's my code:
char * solution(char * s) {
int len = strlen(s);
int i;
int index = 0;
char *ans = (char *) malloc(sizeof(char) * (len + 1));
if (ans == NULL) {
fprintf(stderr, "Ran out of space in some function \n");
exit(1);
}
//char* ans = (char *) malloc(len + 1);
ans[len] = '\0';
for(i = 0; i < len/2; i++){
ans[index++] = s[i];
ans[index++] = s[len - i];
}
if(len % 2 == 1){
ans[index] = s[len/2];
}
return ans;
}
In the first iteration of this for loop
for(i = 0; i < len/2; i++){
ans[index++] = s[i];
ans[index++] = s[len - i];
}
the character ans[1] is set to s[len] (i is equal to 0 in the first iteration of the loop) that is to '\0'.
As a result you will get a string that contains only one character.
What you do is what you get.:)
It seems you mean
ans[index++] = s[len - i - 1];
Pay attention to that as the source string is not changed within the function then the function should be declared like
char * solution( const char * s );
The original declaration
char * solution(char * s);
means that the string will be changed in place.
If you want to change a string in place then the function can look the following way as shown in the demonstration program below.
#include <string.h>
#include <stdio.h>
char * solution( char *s )
{
size_t n = strlen( s );
for (size_t i = 1; i < n; i += 2)
{
char c = s[n - 1];
memmove( s + i + 1, s + i, n - i - 1);
s[i] = c;
}
return s;
}
int main( void )
{
char s[] = "0123456789";
puts( s );
puts( solution( s ) );
}
The program output is
0123456789
0918273645

Segfault 11 on long string, PRIOR to accessing string, only when string > 14

ANSI c on OSX 10.13.6
Apple LLVM version 9.1.0 (clang-902.0.39.2)
Target: x86_64-apple-darwin17.7.0
Thread model: posix
I'm learning c
This is a function that manually (character-by-character) adds two character strings representing large numbers (that exceed the unsigned long long or double size).
It functions fine with any two strings 14 or less characters long, but segmentation fault 11 with any strings greater than 14 chars.
Changing the string's memory allocation method seems to have no effect (I.e. from char[15] addend1; // not a ptr to char *addend1 = (char *) malloc(sizeof(char) * (16) ); // pointer
One things that's curious, is that it seems to segfault on the ...
for (int j = maxlength - 1 ; j >= 0; j--)
... prior to accessing either of addend1 or addend2, but I'm not able to find an error there or change it to prevent the segfault.
Am I misreading where the error arises, or could it be related to the for loop?
Successful run (less than 15 chars)
maxlength = 14
char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) ) ... DONE
for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } ... DONE
Start adding individual ints from end (right side) ...
13 ...12 ...11 ...10 ...9 ...8 ...7 ...6 ...5 ...4 ...3 ...2 ...1 ...0 ...main.sum = 28147497671064
UNSuccessful run (15 chars)
maxlength = 15
char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) ) ... DONE
for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } ... DONE
Start adding individual ints from end (right side) ...
Segmentation fault: 11
MAIN.c
#include <stdio.h>
#include <stdlib.h>
#include "../../c-library/include/addViaStrings.h"
int main(void) {
// s[0] = 72; s[1] = 101; s[2] = 108; s[3] = 108; s[4] = 111; s[5] = 32; s[6] = 87; s[7] = 111; s[8] = 114; s[9] = 108; s[10] = 100; s[11] = 0;
// WORKS
// char s1[] = "14073748835532";
// char s2[] = "14073748835532";
// FAILS
char s1[] = "140737488355328";
char s2[] = "140737488355328";
char *sum = addNumericStrings(&s1, &s2);
printf("main.sum = %s\n", sum);
}
addViaStrings.h
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
char* addNumericStrings(char *s1, char *s2);
char leftPad(char *result, char *s, int maxlength);
int findMaxLength(char *s1, char *s2);
char* addNumericStrings(char *s1, char *s2){
// Find the length of the greater of the two
int maxlength = findMaxLength(s1, s2);
printf("maxlength = %d\n", maxlength); //333
///////////////////////////////////////////////
// Using malloc instead of char[maxlength] seems to have NO EFFECT on the issue
// char addend1[maxlength]; // not a pointer
char *addend1 = (char *) malloc(sizeof(char) * (maxlength + 1) );
addend1[maxlength + 1] = 0; // end flag
// char addend2[maxlength]; // not a pointer
char *addend2 = (char *) malloc(sizeof(char) * (maxlength + 1) );
addend2[maxlength + 1] = 0; // end flag
// Allocate sum pointer
printf("char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) ) ... "); //333
char *sum = (char *) malloc(sizeof(char) * (maxlength + 1) );
printf("DONE\n"); //333
// General use vars
int a1, a2, total;
int carry = 0;
// Prepare the strings for manual addition. Pad the left with char 0s
leftPad(addend1, s1, maxlength);
leftPad(addend2, s2, maxlength);
// Buffer sum with zeros
sum[maxlength + 1] = 0; // end flag
printf("for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } ... "); //333
for (int i = 0; i < (maxlength); i++) { sum[i] = '0'; } // Fill w/ 0s
printf("DONE\n"); //333
// Run the manual addition
// Start adding individual ints from end (right side)
printf("Start adding individual ints from end (right side) ...\n"); //333
// maxlength -1 because(I think) the termination char takes 2 bytes
// If I use (maxlength) instead of (maxlength -1) I get a weird
// question mark char at the end of returnsum
for (int j = maxlength - 1 ; j >= 0; j--) {
///////////////////////////////////////////
// The segfault seems to happen BEFORE accessing addend1 or addend2
printf("%d ...", j); // 333 This DOES NOT print
///////////////////////////////////////////
a1 = addend1[j] - '0'; // Convert to int
a2 = addend2[j] - '0'; // Convert to int
total = (a1 + a2 + carry);
carry = 0;
if ( total >= 10){
carry += 1;
total -= 10;
}
sum[j + 1] = '0'+total; // convert to ascii value for numbers (adding 48)
}
sum[0] = '0' + carry; // add last carry to start of num always, even if 0
// Before returning, truncate leading zeros
char *returnsum = (char *) malloc(sizeof(char) * (strlen(sum) + 1) );
int sum_i = 0;
int returnsm_i = 0;
// bool truncate = true; // Find out why this wont compile
int truncate = 1; // true
while (1){
// if order is important here
if (sum[sum_i] == '\0') { break; } // we're done
if (sum[sum_i] == '0' && truncate == 1) { sum_i += 1; continue; } // 1 is true
// if a num, Stop truncating 0s but DO continue adding numbers
if (sum[sum_i] != '0') { truncate = 0; } // 0 is false
returnsum[returnsm_i] = sum[sum_i];
returnsm_i += 1;
sum_i += 1;
}
return returnsum;
}
char leftPad(char *result, char *s, int maxlength){
int slength = strlen(s);
// buffer with zeros, not '\0's
for (int i = (maxlength); i >= 0; i--){ result[i] = '0'; }
// right fill result with s
for (int j = 0; j <= slength; j++){
int index = ((maxlength - slength) + j);
result[index] = s[j];
}
result[maxlength + 1] = 0;
}
int findMaxLength(char *s1, char *s2){
// int length1 = findEndLength(s1);
// int length2 = findEndLength(s2);
int length1 = strlen(s1);
int length2 = strlen(s2);
int maxlength;
(length1 > length2) ? (maxlength = length1) : (maxlength = length2);
return maxlength;
}
The issue was I was trying to access the sum string as if it was one position longer than the addends strings, but I had declared it as the same length (I.e. maxlength + 1). So I was attempting to access one position past the actual sum array.
This was a somewhat hidden problem, because it was not until the length of sum needed to be greater than 15, that this access error stepped into disallowed memory space, resulting in a segfault.
Details
Because the sum of two addends could conceivably require at least one additional position in the array if the sums carried over a one (I.e. 999 + 999 = 1998), the sum string needs to be one array position longer than the addends.
If the addends were 3 digits long (length of array = 4) then the sum needed to be 4 digits long (array length = 5).
// Correct code if "maxlength" (number of actual digits in string) = 14
char *addend1 = (char *) malloc(sizeof(char) * (maxlength + 1) ); // +1 To include termination byte
char *addend2 = (char *) malloc(sizeof(char) * (maxlength + 1) ); // +1 To include termination byte
char *sum = (char *) malloc(sizeof(char) * (maxlength + 2) ); // +2 To include termination byte, AND an extra char at the front
...so that the final actual digit character of sum is accessed via maxlength + 1
CORRECTED CODE
NOTE: Because calculating against maxlength as the number of digits (versus the length of the entire array including terminator) was confusing - as well as considered bad form, I have since learned - the following final code has been simplified to use more intuitive variables.
#include <stdio.h>
#include <stdlib.h>
char* addIntsAsStrings(char *s1, char *s2);
char* addIntsAsStrings(char *s1, char *s2){
// Find the length of the greater of the two
int length1 = strlen(s1);
int length2 = strlen(s2);
int addendDigits;
(length1 > length2) ? (addendDigits = length1) : (addendDigits = length2);
// We need separate strings of so they can be buffered with zeros
// Create the string for the addends and buffer with zeros.
char addend1[addendDigits + 1];
char addend2[addendDigits + 1];
for (int i = 0; i < (addendDigits) ; i++){ // "<" not "<="
addend1[i] = '0'; // buffer w/ 0s
addend2[i] = '0'; // buffer w/ 0s
} // buffer w/ 0s
addend1[addendDigits] = '\0'; // terminate
// put s1 and s2 into buffered addends strings
int s1_index = (strlen(s1) - 1);
int s2_index = (strlen(s2) - 1);
for (int i = (addendDigits - 1); i >= 0; i--){ //Start at back of addend
if ( s1_index >= 0) { addend1[i] = s1[s1_index]; }
if ( s2_index >= 0) { addend2[i] = s2[s2_index]; }
s1_index -= 1;
s2_index -= 1;
}
// Allocate sum pointer. The sum pointer needs to be ONE char
// longer than the addends, in the event that the addends need
// to carry a final one to the front. I.e. 999 + 999 = 1998
int sumDigits = addendDigits + 1;
char *sum = (char *) malloc(sizeof(char) * (sumDigits + 1) ); // +1 To include termination byte, AND an extra char at the front
for (int i = 0; i < (sumDigits) ; i++){ // "<" not "<="
sum[i] = '0'; // buffer w/ 0s
}
sum[sumDigits] = '\0';
// Manual addition vars
int a1, a2, total;
int carry = 0;
// Run the manual addition
// Start adding individual ints from end (right side)
for (int j = addendDigits - 1; j >= 0; j--) {
a1 = addend1[j] - '0'; // Convert to int
a2 = addend2[j] - '0'; // Convert to int
total = (a1 + a2 + carry);
carry = 0;
if ( total >= 10){
carry += 1;
total -= 10;
}
// convert to ascii value for numbers (adding 48)
sum[j + 1] = '0'+total; // sum[j + 1] because `sum`is always one index larger than the addends
}
sum[0] = '0' + carry; // add last carry to start of num always, even if 0
// Before returning, truncate leading zeros
char *returnsum = (char *) malloc(sizeof(char) * (strlen(sum) + 1) );
int sum_i = 0;
int returnsm_i = 0;
int truncate = 1; // true
while (1){
// if order is important here
if (sum[sum_i] == '\0') { break; } // we're done
if (sum[sum_i] == '0' && truncate == 1) { sum_i += 1; continue; } // 1 is true
// if a num, Stop truncating 0s but DO continue adding numbers
if (sum[sum_i] != '0') { truncate = 0; } // 0 is false
returnsum[returnsm_i] = sum[sum_i];
returnsm_i += 1;
sum_i += 1;
}
return returnsum;
}

What’s wrong with this code to reverse a string, if swap fuction is created without using the "temp" variable?

/*implementation of strrev i.e. string reverse function*/
#include<stdio.h>
#include<string.h>
/*length of the string i.e. cells in the string*/
static const unsigned int MAX_LENGTH = 100;
//static const int MAX_LENGTH = -100;
/*reverses the string*/
void reverseString(char[]);
/*swaps the elements in the cells of a string*/
void swap(char[], int, int);
/*runs the program*/
int main()
{
char string[MAX_LENGTH];
//char string[0]; //no error!
//char string[-1]; //error!
gets(string);
reverseString(string);
printf("\n%s", string);
return 0;
}
void reverseString(char string[])
{
int i;
for(i = 0; i < (strlen(string) / 2); i++)
//for(i = 0; i <= ((strlen(string) - 1) / 2); i++)
{
swap(string, i, (strlen(string) - 1 - i));
}
}
void swap(char string[], int i, int j)
{
int temp = string[i];
string[i] = string[j];
string[j] = temp;
/*
string[i] = string[i] + string[j]; //i = i + j
string[j] = string[i] - string[j]; //j = i + j - j = i
string[i] = string[i] - string[j]; //i = i + j - i = j
*/
}
Look at the "reverseString" and "swap" functions. The current code works perfectly. If the swap function is rewritten by using no "temp" variable, code still runs smoothly.
However if the "for" line in "reverseString" function is replaced with the code just below it (commented using single-line comment), the code doesn't work for single character strings if modified swap function (without temp) is used, but works if original swap function (with temp) is used.
Why is this behavior seen?
The version of the swap function without a temp depends on the two indexes being different from each other.
Suppose i and j are the same. You then effectively have the following:
string[i] = string[i] + string[i]; // string[i] is now 2 * string[i]
string[i] = string[i] - string[i]; // string[i] is now always 0
string[i] = string[i] - string[i]; // still 0
So swapping an element with itself will zero it out.
Now looking at the loop in reverseString when you use i < (strlen(string) / 2) as your condition. If the length of the string is odd, the loop stops before reaching the middle element, so swapping an element with itself doesn't happen.
But when i <= ((strlen(string) - 1) / 2) is your condition, the loop does operate on the middle element which subsequently gets swapped with itself and gets zeroed out.
I would suggest less strlen calls. Your code it IMO too complicated
char *reverse(char *str)
{
char *saved = str;
size_t len = strlen(str);
char *end = str + len - 1;
for(size_t index = 0; index < len / 2; index++)
{
char tmp = *str;
*str++ = *end;
*end -- = tmp;
}
return saved;
}
or
char *reverse1(char *str)
{
char *saved = str;
size_t len = strlen(str);
char *end = str + len - 1;
while(str < end)
{
char tmp = *str;
*str++ = *end;
*end -- = tmp;
}
return saved;
}
why to return char * instead of void. it allows you yo use the functions directly in another operations. For example:
char str[] = "Hello World";
printf("%s\n", reverse(str));
It does not work because you cannot use this code:
string[i] = string[i] + string[j]; //i = i + j
string[j] = string[i] - string[j]; //j = i + j - j = i
string[i] = string[i] - string[j]; //i = i + j - i = j
to swap the value of the variable with itself, since it's the same variable, not two different variables, and the value gets overwritten.
I would strongly recommend you start using C++, and use std::swap() every time you need something to be swapped =)

How to concatenate char and string / How to make char to string

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAX_SIZE 20
void main()
{
int i, j;
char *str1, *str2, *str3, *str_mid;
bool **lcs1, **lcs2;
int len1, len2, len3, len_mid;
char *ch = (char*)malloc(sizeof(char) * 3);
str1 = (char*)malloc(sizeof(char)*MAX_SIZE); //applicatian
str2 = (char*)malloc(sizeof(char)*MAX_SIZE); //apiasn
str3 = (char*)malloc(sizeof(char)*MAX_SIZE); //apun
str_mid = (char*)malloc(sizeof(char)*MAX_SIZE); //apn
str_mid = "";
scanf("%s", str1);
scanf("%s", str2);
scanf("%s", str3);
len1 = strlen(str1);
len2 = strlen(str2);
len3 = strlen(str3);
//str2, str3 ->str_mid (lcs1)
lcs1 = (bool**)malloc(sizeof(bool*)*(len3 + 1));
for (i = 0; i < len3 + 1; i++)
lcs1[i] = (bool*)malloc(sizeof(bool)*(len2 + 1));
for (i = 0; i < len3 + 1; i++)
for (j = 0; j < len2 + 1; j++)
lcs1[i][j] = false;
for (i = 1; i < len3 + 1; i++)
for (j = 1; j < len2 + 1; j++)
if (str3[i-1] == str2[j-1])
lcs1[i][j] = true;
for (i = 1; i < len3 + 1; i++)
{
for (j = 1; j < len2 + 1; j++)
if (lcs1[i][j])
{
//<--- error
ch = str3[i - 1];
strcat(str_mid, ch);
//--->
break;
}
}
//printf("%s", str_mid);
//str_mid, str1 (lcs2)
}
In <--- error ---> part,
I want to concatenation str3[i-1] and str_mid but, str3[i-1] is character type.
So make temporary string is ch and do concatenate.
But, the access error is occurred.
How to make char to string or, how to concatenate char and string?
So long as MAX_SIZE is large enough that str_mid will be able to hold the extra character, you can use strcat() and a compound literal (for C99 or later):
strcat(str_mid, (char[2]){ str3[i - 1], '\0' });
Note that in your code, str3[i - 1] is a char, while you have declared ch as a pointer to char.
How to concatenate char and string
There is no need to call any library function at all, nor to dynamically allocate any memory.
Assuming str_mid is large enough just do:
{
size_t tmp_len = strlen(str_mid);
str_mid[tmp_len] = str3[i - 1];
str_mid[tmp_len + 1] = '\0';
}
To still have the feeling to use a function you might want to wrap the above code into a macro like this:
#define STRCATCHAR(s, c) \
do { \
assert(s != NULL); \
size_t tmp_len = strlen(s); \
(s)[tmp_len] = (c); \
(s)[tmp_len + 1] = '\0'; \
} while (0)
and use it like this:
STRCATCHAR(str_mid, str3[i - 1]);
To concatenate char and string (first char, then string) do:
const char* conc_char_string(const char ch, const char* str, int len) {
const char* out = malloc(len + 2); /* 1 char from ch, len chars from str, and zero character, totally len+2 */
out[0] = ch; /* copy the character */
strcpy(out+1, str); /* copy the string */
return out;
}
Here len should be strlen(str)
The statement str_mid = ""; assigns the address of costant string "" to pointer str_mid, which will cause segmentfault later. Use str_mid[0] = '\0'; instead.
In statement ch = str3[i - 1];, you are assigning char to char pointer, which will cause segmentfault.
If all you want is simply appending a character to the string, you can keep track of length of string, like this:
len_mid = strlen(str_mid);
for (i = 1; i < len3 + 1; i++)
{
for (j = 1; j < len2 + 1; j++)
if (lcs1[i][j])
{
str_mid[len_mid++] = str3[i - 1];
str_mid[len_mid] = '\0';
break;
}
}
First rule of the C-club, create your own string buffer, second rule of the C-club create your own string buffer.
Each programmer has it's own string buffer. Minimal requirement is :
#define STRINGSTRTSZ 256
typedef struct string
{
char *str;
size_t n;
size_t buf_sz;
}String,*pString;
pString new_string()
{
pString res;
res=malloc(sizeof(String)); ///i leave to you the error handling
res->bus_sz=STRINGSTRSZ;
res->n=0;
res->str=malloc(res->bus_sz);
}
char*concatbase(pString *string,char*s,size_t len)
{
if(len+1>=string->buf_sz-string->n)
{
res->buf_sz+=len+res->buf_sz;
char *tmp=realloc(string->str,res->buf_sz);
if(tmp)
string->str=tmp;
else
...
}
memcpy(string->str+string->n,s,len);
string->n+=len;
*(string->str+string->n)='\0';
return string->str;
}
#define concatNllPtr(string,nllptr) concatbase(string,nllptr,strlen(nllptr))
#define concatString(string,str2) concatbase(string,(str2)->str,(str2)->n)
End so on...

Replace every word in text with the word shifted to right 'k' times

I tried to make a function which replace every word in a text with the word shifted to right by 'k' times.
the code look like this:
void operation_3(char *string, int k){
int len = 0, i;
int string_len = strlen(string);
char *word;
char s[12] = " .,?!\"'";
char *dup;
dup = strdup(string);
word = strtok(dup, s);
while (word != NULL) {
len = strlen(word);
char *new_word = (char *)malloc(len * sizeof(char));
for (i = 0; i < k; i++) {
new_word = shift_to_right(word);
}
string = replace_word(string, word, new_word);
word = strtok(NULL, s);
}
}
shift_to_right is:
char *shift_to_right(char *string){
char temp;
int len = strlen(string) - 1;
int i;
for (i = len - 1; i >= 0; i--) {
temp = string[i+1];
string[i+1] = string[i];
string[i] = temp;
}
return string;
}
replace_word is:
char *replace_word(char *string, char *word, char *new_word) {
int len = strlen(string) + 1;
char *temp = malloc(len * sizeof(char));
int temp_len = 0;
char *found;
while (found = strstr(string, word)) {
if (strlen(found) != strlen(word) || isDelimitator(*(found - 1)) == 1) {
break;
}
memcpy(temp + temp_len, string, found - string);
temp_len = temp_len + found - string;
string = found + strlen(word)
len = len - strlen(word) + strlen(new_word);
temp = realloc(temp, len * sizeof(char));
memcpy(temp + temp_len, new_word, strlen(new_word));
temp_len = temp_len + strlen(new_word);
}
strcpy(temp + temp_len, string);
return temp;
}
and isDelimitator is:
int isDelimitator(char c) {
if(c == ' ' || c == '.' || c == ',' || c == '?' || c == '!' ||
c == '"' || c == '\0' || c == '\'') {
return 0;
}
else return 1;
}
I tested shift_to_right, replace_word and isDelimitator and work fine. But the final function, operation_3 doesn't work as expected. For example, for input: "Hi I am John" and for k = 1 the output is : "Hi I am John". Basically operation_3 doesn't modify the string. Any advice, corrections please?
There are a few things which I see are possibly the reason for error.
1) In operation_3 you do this : new_word = shift_to_right(word); And, in the definition of char *shift_to_right(char *string) you modify the string itself and return a pointer to it. So, if you called shift_to_right(word) and word = "Hi" then after the execution of shift_to_right both word and new_word are now pointing to the same string "iH", so in replace_word when you pass both the words and check for the substring word you will always get NULL, because, there is no substring "iH".
A possible solution, in shift_to_right add a statement,
char *new_string = strdup(string);
and instead of swapping the characters in string, swap the characters now in new_string and return the new_string from the function.
Your code shall look like this ::
char *shift_to_right(char *string){
char temp;
int len = strlen(string) - 1;
char *new_string = strdup(string);
int i;
for (i = len - 1; i >= 0; i--) {
temp = new_string[i+1];
new_string[i+1] = new_string[i];
new_string[i] = temp;
}
return new_string;
}
2) In the function replace_word, for a moment let us consider that the above mentioned error does not occur and replace_word get called with the parameters :: replace_word(string, "Hi", "iH");.
So, when you perform found = strstr(string, word), it gives you a pointer to the first letter where Hi started. So, in this case, if your string was "Hi I am John", then you get a pointer to the first H, and when you perform strlen(found) you will get 12(length of string left starting from the pointer) as the output, and strlen(word) will always be less (unless found points to the last word in the string), so in most cases your if condition becomes true and you break from the loop, without any swapping.
Moreover, as you yourself pointed out in the comments that strstr will return Johns as well if you want a substring John the only solution for this would be to run a loop and check that in string after John if there is delimiter character or not, if there is no delimiter character, then this is not the substring that you needed.
replace_word shall look something like this ::
void replace_word(char *string, char *word, char *new_word) {
char *found = strstr(string, word);
int len = strlen(word);
while(found) {
char temp = *(found + len);
if(isDelimeter(temp) == 0) {
break;
} else {
found = strstr(found + len + 1);
}
}
if(found != NULL) {
for(int i = 0; i < len; i++) {
*(found + i) = new_word[i]; // *(found + i) is accessing the i^th, character in string from the pointer found
}
}
}
I think this replace_word shall work, you can directly modify the string, and there is no need to actually make a temp string and return it. This reduces the need of allocating new memory and saving that pointer.
I hope this could help!
EDIT :: Since we have been using strdup in the code, which dynamically allocates memory of the size of the string with an extra block for the \0 character, we shall take care of freeing it explicitly, so it will be a good idea according to me free the allocated memory in replace_word just before we exit the function since the new_word is useless after it.
Moreover, I saw a statement in your code::
1) char *new_word = (char *)malloc(len * sizeof(char));
Just before you start the shifting the words, I hope you understand that you do not need to do it. new_word is just a pointer, and since we now allocated memory to it in strdup we do not need to do it. Even before, considering the code that you had written there was no reason to allocate memory to new_word since you were returning the address of the array, which was already in the stack, and would stay in the stack till the end of the execution of the program.
This code is simpler than what you have, and it prints all the word delimiters that were in the input string. And rather than looking for specific punctuation characters, it checks alphanumeric instead.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
char instr[] = "Hi! I am 'John' ;)";
int lennin = strlen(instr);
int shifts, i, len, index, start, next;
printf("Working with %s\n", instr);
for(shifts=0; shifts<5; shifts++) { // various examples
printf("Shifts = %d ", shifts);
start = 0;
while(start < lennin) {
while (start < lennin && !isalnum(instr[start])) { // find next alphanum
printf("%c", instr[start]); // output non-alphanum
start++;
}
next = start + 1;
while (isalnum(instr[next])) // find next non-alphanum
next++;
len = next - start;
for(i=0; i<len; i++) { // shift the substring
index = i - shifts;
while(index < 0) index += len; // get index in range
printf("%c", instr[start + (index % len)]); // ditto
}
start = next; // next substring
}
printf("\n");
}
return 0;
}
Program output:
Working with Hi! I am 'John' ;)
Shifts = 0 Hi! I am 'John' ;)
Shifts = 1 iH! I ma 'nJoh' ;)
Shifts = 2 Hi! I am 'hnJo' ;)
Shifts = 3 iH! I ma 'ohnJ' ;)
Shifts = 4 Hi! I am 'John' ;)

Resources