How to avoid segmentation fault in recursive function with strings in C? - c

Note: I am not trying to get the algorithmic implementation! I already have it figured out in Java. I just can't seem to get my logic to work in C. Below is the Java code (which works) followed by the C99 code that breaks.
The high-level coding challenge that is presenting the segfault in my implementation is:
How to find all combinations of k length and smaller using alphabet of length n with repeating elements in C?
Problem
Code compiles, but I get a segmentation fault at runtime.
Notes / Observations
This is from a self-paced edX course I'm working my way through. I've already done the "less comfortable" challenges, and frankly they were a bit too easy. I'm now trying to go above the requirements and do this "more comfortable" (read more challenging) challenge. It is one of the more advanced beginner challenges.
I'm not a beginner programmer, but pretty much a novice with C.
As far as I understand it, the <cs50.h>is a custom header file that implements some things that simplify (read abstract away) command-line input and handling of strings. Documentation in it can be found at the cs50.net site and on the cs50lib GitHub page
I can't figure out the correct way to pass the values to the recursive function and need to utilize address referencing/dereferencing. Unfortunately my C is a bit fuzzy compared to other langs.
Test Calls with Desired Output Result
~/myTerminal $ ./printall ab 3
aaa
aab
aba
abb
baa
bab
bba
bbb
aa
ab
ba
bb
a
b
~/myTerminal $ ./printall abc 2
aa
ab
ac
ba
bb
bc
ca
cb
cc
a
b
c
myTerminal $ ./printall abcd 1
a
b
c
d
Java Code that Works
public class Main {
public static void main(String[] args) {
System.out.println("First Test");
char[] set1 = {'a', 'b'};
int k = 3;
printCombinations(set1, k);
System.out.println("\nSecond Test");
char[] set2 = {'a', 'b', 'c'};
k = 2;
printCombinations(set2, k);
System.out.println("\nThird Test");
char[] set3 = {'a', 'b', 'c', 'd'};
k = 1;
printCombinations(set3, k);
}
// Print all possible strings of length k or smaller.
static void printCombinations(char[] set, int k) {
int n = set.length;
for(int i = k; i > 0; i--)
{
printCombinationsRec(set, "", n, i);
}
}
// Print all combinations of length k
static void printCombinationsRec(char[] set, String prefix, int n, int k)
{
if (k == 0)
{ // Base case
System.out.println(prefix);
return;
}
// One by one add all characters
// from set and recursively
// call for k equals to k-1
for (int i = 0; i < n; ++i)
{
String newPrefix = prefix + set[i];
printCombinationsRec(set, newPrefix, n, k - 1);
}
}
}
C Code Causing Segmentation Fault
// CS50 custom header file
#include <cs50.h>
// "Regular" headers
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void printCombinations();
void printCombinationsRecur();
int main(int argc, string argv[])
{
if (argc == 3) // Correct number of arguments
{
string strSet = argv[1];
int maxLength = atoi(argv[2]);
printCombinations(strSet, maxLength);
return 0;
}
// Incorrect usage
printf("Usage: %s <charset>:string\n <maxLength>:int\n", argv[0]);
return 1;
}
// Functions below were adapted and modified from code at :
// https://www.geeksforgeeks.org/print-all-combinations-of-given-length/
// Accessed : 2018-07-13
void printCombinations(string sSet, int strLength)
{
int aLength = strlen(sSet);
for (int i = strLength; i > 0; i--)
{
printCombinationsRecur(sSet, "", aLength, strLength);
}
}
void printCombinationsRecur(string *sSet, string prefix, int aLength, int strLength )
{
// printf("sSet: %s\nprefix: %s\naLength: %i\nstrLength: %i\n", *sSet, prefix, aLength, strLength);
// In terms of the traditional equation k=> strLength, n=>aLength, S=>sSet
if (strLength == 0)
{
printf("%s\n", prefix);
}
for (int i = 0; i < aLength; i++)
{
string temp1 = "";
strcat(temp1, prefix); // <== SEGFAULT HAPPENING HERE!
string newPrefix = strcat(temp1, sSet[i]);
printCombinationsRecur(sSet, newPrefix, aLength, strLength - 1);
}
}
I made the following change (suggested by #Stargateur) to the recursive function, but still get a segfault!
void printCombinationsRecur(string *sSet, string prefix, int aLength, int strLength )
{
// printf("sSet: %s\nprefix: %s\naLength: %i\nstrLength: %i\n", *sSet, prefix, aLength, strLength);
// In terms of the traditional equation k=> strLength, n=>aLength, S=>sSet
if (strLength == 0)
{
printf("%s\n", prefix);
}
for (int i = 0; i < aLength; i++)
{
printf("This prints");
char *temp1 = malloc((strLength +2) * sizeof(char));
for (int j = 0; j < strLength + 2; j++){
if(j < strLength)
{
temp1[j] = prefix[j];
}
if(j == strLength)
{
temp1[j] = *sSet[i];
}
if(j == strLength + 1){
temp1[j] = '\0';
}
}
printCombinationsRecur(sSet, temp1, aLength, strLength - 1);
free(temp1);
}
}

One of the key differences between your Java code that works and the C code that doesn't is in the printCombinations() function.
Working Java:
for(int i = k; i > 0; i--)
{
printCombinationsRec(set, "", n, i);
}
Broken C:
int aLength = strlen(sSet);
for (int i = strLength; i > 0; i--)
{
printCombinationsRecur(sSet, "", aLength, strLength);
}
You're calling the recursive function with the same length, over and over and over again. To match the Java, the strLength argument should be i instead.
You also do not handle the base case properly. The Java code returns after printing if k == 0; the C code doesn't.
Working Java:
if (k == 0)
{ // Base case
System.out.println(prefix);
return;
}
Broken C:
if (strLength == 0)
{
printf("%s\n", prefix);
}
And then you handle the string concatenation incorrectly. C is not very forgiving. There are at least two ways to handle it. The method that will work with any version of C uses malloc(). The method that will work with C99, or with C11 as long as the compiler does not define __STDC_NO_VLA__, uses a VLA. The version using malloc() also calls free() and so it does a bit more work than the other.
Since the length allocated is always the same, you could offset the cost by calling malloc() once before the loop and free() once after the loop, and you'd only need to copy the prefix once and then simply set the extra characters (even the null could be set once). You could also enhance the VLA code to define the new prefix array once outside the loop, copy the prefix once, set the null byte once, and just set the extra character inside the loop.
You should also use formal prototype declarations for the functions, not mere function declarations that care not one whit about the arguments presented.
The code shown below is lazy and does not check that the malloc() calls work. It also does not validate that the alphabet is a sensible length, nor that the maximum length is reasonable, nor that the elements in the alphabet are unique.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void printCombinations(const char *set, int strLength);
static void printCombinationsRec(const char *set, const char *prefix, int aLength, int strLength);
int main(int argc, char *argv[])
{
if (argc != 3)
{
fprintf(stderr, "Usage: %s alphabet maxlen\n", argv[0]);
return 1;
}
/* GIGO: garbage in, garbage out */
char *strSet = argv[1];
int maxLength = atoi(argv[2]);
printCombinations(strSet, maxLength);
return 0;
}
static void printCombinations(const char *set, int k)
{
int n = strlen(set);
for (int i = k; i > 0; i--)
{
printCombinationsRec(set, "", n, i);
}
}
#if defined(USE_VLA) && __STDC_NO_VLA__ != 1
static void printCombinationsRec(const char *set, const char *prefix, int n, int k)
{
if (k == 0)
{
printf("%s\n", prefix);
return;
}
for (int i = 0; i < n; ++i)
{
size_t len = strlen(prefix);
char newPrefix[len + 2];
strcpy(newPrefix, prefix);
newPrefix[len + 0] = set[i];
newPrefix[len + 1] = '\0';
printCombinationsRec(set, newPrefix, n, k - 1);
}
}
#else
static void printCombinationsRec(const char *set, const char *prefix, int n, int k)
{
if (k == 0)
{
printf("%s\n", prefix);
return;
}
for (int i = 0; i < n; ++i)
{
size_t len = strlen(prefix);
char *newPrefix = malloc(len + 2);
strcpy(newPrefix, prefix);
newPrefix[len + 0] = set[i];
newPrefix[len + 1] = '\0';
printCombinationsRec(set, newPrefix, n, k - 1);
free(newPrefix);
}
}
#endif /* USE_VLA */
Compiled with -DUSE_VLA with a compiler that supports VLAs, it will not use malloc(). Compiled without the option, or with a compiler that supports C11 but does not support VLAs, then it uses malloc() and free().
At one point, I also added argument validation code in main(), but the 20 lines or so seemed to be more getting in the way than useful, so I left the GIGO comment there instead.
If this was 'production code', I'd be using error reporting functions and would not skip the checks (in part because the error reporting functions make it easier, using a single line per reported error instead of 5 or so without. I'd be using the error reporting code available in my SOQ (Stack Overflow Questions) repository on GitHub as files stderr.c and stderr.h in the src/libsoq sub-directory.
Note that you can't use strcat() easily because you want to append a single character, not a string. Hence the use of the two assignments. The + 0 emphasizes the similarity between the two assignments; the compiler does not generate any code for + 0.
When run (I called it comb47.c, compiled to comb47), it produces the output desired:
$ comb47 ab 3
aaa
aab
aba
abb
baa
bab
bba
bbb
aa
ab
ba
bb
a
b
$ comb47 abc 2
aa
ab
ac
ba
bb
bc
ca
cb
cc
a
b
c
$ comb47 abcd 1
a
b
c
d
$

Related

Is it possible to simplify this algorithm so that it only uses 1 loop and 2 variables?

Is it possible to get the same results as this code using only a and b?
I'm trying to calculate c from a and b to avoid using a third variable, but I can't find a solution.
#include <stdio.h>
#include <stdlib.h>
const int LENGTH = 20;
int main()
{
char arr[LENGTH];
for (int a = 0, b = 1, c = 0; c < LENGTH; c++) {
if (a < b) {
arr[c] = '*';
a++;
} else {
arr[c] = ' ';
a = 0;
b++;
}
}
printf(arr);
return EXIT_SUCCESS;
}
Code result : * ** *** **** *****
In the event that checking if a number is triangular by linking or writing some sort of sqrt() function is not a solution that you find acceptable:
Each group of **... in the final string has a ' ' at the end, so the shortest segment in the string is "* ", which is 2 chars long.
The c in your loop is the index of the char array that this iteration should write to, the a is the index inside the current group of '*'s, and b is length of the current group of '*'s less one (since we want to count the spaces). Directly before the if clause in your for loop, it can be said that c is the sum from 2 to b plus a.
In other words, if a=0, and b=1, then c=0, because the sum from 2 to 0 is 0, plus 0 is 0.
If a=3, and b=4, then c= (2+3+4) + 3 = 12.
This means that you could write your code like this:
#include <stdio.h>
const int LENGTH = 20;
int sumFromTwo(int in){ //Recursive function to calculate sigma(2:in)
if(in < 2)
return 0;
else
return in + sumFromTwo(in - 1);
}
int main()
{
char arr[LENGTH + 1]; //Extra byte for null-terminator
for (int a = 0, b = 1; sumFromTwo(b) + a < LENGTH ; ) {
if (a < b) {
arr[sumFromTwo(b) + a] = '*';
a++;
} else {
arr[sumFromTwo(b) + a] = ' ';
a = 0;
b++;
}
}
arr[LENGTH] = '\0'; //Always null-terminate your strings
printf(arr);
return EXIT_SUCCESS;
}
But using recursion to avoid using a variable that is almost certainly going to be optimized into a register anyway is not going to save your computer any resources, least of all RAM, so it is definitely cleaner to do it the way you did in your question (but please null-terminate your string before passing it to your choice of printf or puts).

Random Bytes In C Output

I just wrote my first program in C and it is a cesarean shift implementation. It works as expected with short inputs, but sometimes produces seemingly random bytes at the and of the output and I cannot figure out why.
I have tried looking at the program in GDB, but just don't have enough experience yet to figure out exactly what is going wrong. I would love to know how one would go about figuring this out with a debugger like GDB.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void rot(char*, int);
char alphabet[27] = "abcdefghijklmnopqrstuvwxyz";
int main (int argc, char* argv[]) {
if (argc != 3) {
printf("Usage: %s [lowercase-text] [rotation-number]\n", argv[0]);
return 1;
} else {
rot(argv[1], atoi(argv[2]));
}
}
void rot (char* t, int r) {
char result[100];
for (int i = 0; i < strlen(t); i++) {
char* location = strchr(alphabet, t[i]);
result[i] = location ? alphabet[(location - alphabet + r) % strlen(alphabet)] : t[i];
}
printf("%s\n", result);
}
Here is the unexpected output. The actual rotation works fine but there are some unexpected bytes at the end.
michael#linux:~/Desktop$ ./rotation
Usage: ./rotation [lowercase-text] [rotation-number]
michael#linux:~/Desktop$ ./rotation rotations_are_cool 13
ebgngvbaf_ner_pbby��� (<- Why are these here ???)
Here was my attempt with GDB. I have not been able to identify the extra data tagging at the end. (full output # https://pastebin.com/uhWnj17e)
(gdb) break *rot+260
Breakpoint 1 at 0x936: file ../rot.c, line 25.
(gdb) r rotations_are_cool 13
Starting program: /home/michael/Desktop/rotation rotations_are_cool 13
Breakpoint 1, 0x0000555555554936 in rot (
t=0x7fffffffe2d2 "rotations_are_cool", r=13) at ../rot.c:25
25 printf("%s\n", result);
(gdb) x/s $rbp-0x80
0x7fffffffdde0: "ebgngvbaf_ner_pbby\377\367\377\177"
This strange occurrence only happens around 50% of the time and happens more often with longer strings. Please help explain and eliminate this. Any other tips that would improve my code are also appreciated. Thanks a dozen!
The end of a string is recognized by the character '\0'.
So you could do it like this
char result[100];
int i;
for (i = 0; i < strlen(t); i++) {
char* location = strchr(alphabet, t[i]);
result[i] = location ? alphabet[(location - alphabet + r) % strlen(alphabet)] : t[i];
}
result[i] = '\0';
You also don't check, that result is large enough for the string, so you could allocate the needed memory dynamically
size_t len = strlen(t)
char *result = malloc(len + 1); /* +1 for terminating '\0' character */
if(result == NULL) {
/* Error allocating memory */
}
int i;
for (i = 0; i < len; i++) {
char* location = strchr(alphabet, t[i]);
result[i] = location ? alphabet[(location - alphabet + r) % strlen(alphabet)] : t[i];
}
result[i] = '\0';
printf("%s\n", result);
free(result);

N permutations of string (with repetitions)

I have to print first n permutations with repetitions of a string.
String is formed with characters 'a','b','c','d','e','f'.
For example, first 10 permutations would be: aaaaaa,aaaaab,aaaaac,aaaaad,aaaaae,aaaaaf,aaaaba,aaaabb,aaaabc,aaaabd.
This is my failed attempt:
int main()
{
FILE *c;
c = fopen("C:\\Users\\Korisnik\\Desktop\\tekst\\permutacija.txt", "w");
char s[6] = "abcdef";
char t[6] = "aaaaaa";
s[6] = '\0';
t[6] = '\0';
int k = strlen(t);
int m = k;
int n;
scanf("%d", &n);
int br = 0;
int i = 0;
while (br < n) {
i = 0;
while (i < 6) {
t[k-1] = s[i];
fprintf(c, "%s ", t);
fprintf(c, "\n");
i++;
br++;
if (br == n) {
exit(1);
}
}
t[k-1] = 'a';
k--;
if (k < 0) {
k = m;
}
}
return 0;
}
And my output for first 10 permutations is:
aaaaa
aaaaab
aaaaac
aaaaad
aaaaae
aaaaaf
aaaa
aaaaba
aaaaca
aaaada
Any suggestions?
(Showing a different idea)If you look carefully you will see that all the permutations are the numbers in base-7. Consider a as 0, b as 1 and so on. So for every number 1..n you will convert it into base 7 and write it (By write it I mean, in place of 0 you put a,1 - b etc). That will give you the required result. (Ofcourse in conversion you will have to append 0 to the left of the number as per number of digits you want to show). There are problems in your code:
char s[6]="abcdef";
is legal in C.
s[6]=0;
This is not as you are accessing array index out of bound which is Undefined behavior. strlen(t) is undefined behavior as t is not NUL terminated.
Also you have fprintf(c,"%s ",t); in your code - this also leads to undefined behavior, it also expects a char* which points to a nul terminated char array. This will make your realize that how irrelevant it is to have something like this
char s[6]="abcdef";
Long story short, use char s[7]="abcdef"; (same applies to t also).

Attempting to split and store arrays similar to strtok

For an assignment in class, we have been instructed to write a program which takes a string and a delimiter and then takes "words" and stores them in a new array of strings. i.e., the input ("my name is", " ") would return an array with elements "my" "name" "is".
Roughly, what I've attempted is to:
Use a separate helper called number_of_delimeters() to determine the size of the array of strings
Iterate through the initial array to find the number of elements in a given string which would be placed in the array
Allocate storage within my array for each string
Store the elements within the allocated memory
Include directives:
#include <stdlib.h>
#include <stdio.h>
This is the separate helper:
int number_of_delimiters (char* s, int d)
{
int numdelim = 0;
for (int i = 0; s[i] != '\0'; i++)
{
if (s[i] == d)
{
numdelim++;
}
}
return numdelim;
}
`This is the function itself:
char** split_at (char* s, char d)
{
int numdelim = number_of_delimiters(s, d);
int a = 0;
int b = 0;
char** final = (char**)malloc((numdelim+1) * sizeof(char*));
for (int i = 0; i <= numdelim; i++)
{
int sizeofj = 0;
while (s[a] != d)
{
sizeofj++;
a++;
}
final[i] = (char*)malloc(sizeofj);
a++;
int j = 0;
while (j < sizeofj)
{
final[i][j] = s[b];
j++;
b++;
}
b++;
final[i][j+1] = '\0';
}
return final;
}
To print:
void print_string_array(char* a[], unsigned int alen)
{
printf("{");
for (int i = 0; i < alen; i++)
{
if (i == alen - 1)
{
printf("%s", a[i]);
}
else
{
printf("%s ", a[i]);
}
}
printf("}");
}
int main(int argc, char *argv[])
{
print_string_array(split_at("Hi, my name is none.", ' '), 5);
return 0;
}
This currently returns {Hi, my name is none.}
After doing some research, I realized that the purpose of this function is either similar or identical to strtok. However, looking at the source code for this proved to be little help because it included concepts we have not yet used in class.
I know the question is vague, and the code rough to read, but what can you point to as immediately problematic with this approach to the problem?
The program has several problems.
while (s[a] != d) is wrong, there is no delimiter after the last word in the string.
final[i][j+1] = '\0'; is wrong, j+1 is one position too much.
The returned array is unusable, unless you know beforehand how many elements are there.
Just for explanation:
strtok will modify the array you pass in! After
char test[] = "a b c ";
for(char* t = test; strtok(t, " "); t = NULL);
test content will be:
{ 'a', 0, 'b', 0, 'c', 0, 0 }
You get subsequently these pointers to your test array: test + 0, test + 2, test + 4, NULL.
strtok remembers the pointer you pass to it internally (most likely, you saw a static variable in your source code...) so you can (and must) pass NULL the next time you call it (as long as you want to operate on the same source string).
You, in contrast, apparently want to copy the data. Fine, one can do so. But here we get a problem:
char** final = //...
return final;
void print_string_array(char* a[], unsigned int alen)
You just return the array, but you are losing length information!
How do you want to pass the length to your print function then?
char** tokens = split_at(...);
print_string_array(tokens, sizeof(tokens));
will fail, because sizeof(tokens) will always return the size of a pointer on your local system (most likely 8, possibly 4 on older hardware)!
My personal recommendation: create a null terminated array of c strings:
char** final = (char**)malloc((numdelim + 2) * sizeof(char*));
// ^ (!)
// ...
final[numdelim + 1] = NULL;
Then your print function could look like this:
void print_string_array(char* a[]) // no len parameter any more!
{
printf("{");
if(*a)
{
printf("%s", *a); // printing first element without space
for (++a; *a; ++a) // *a: checking, if current pointer is not NULL
{
printf(" %s", *a); // next elements with spaces
}
}
printf("}");
}
No problems with length any more. Actually, this is exactly the same principle C strings use themselves (the terminating null character, remember?).
Additionally, here is a problem in your own code:
while (j < sizeofj)
{
final[i][j] = s[b];
j++; // j will always point behind your string!
b++;
}
b++;
// thus, you need:
final[i][j] = '\0'; // no +1 !
For completeness (this was discovered by n.m. already, see the other answer): If there is no trailing delimiter in your source string,
while (s[a] != d)
will read beyond your input string (which is undefined behaviour and could result in your program crashing). You need to check for the terminating null character, too:
while(s[a] && s[a] != d)
Finally: how do you want to handle subsequent delimiters? Currently, you will insert empty strings into your array? Print out your strings as follows (with two delimiting symbols - I used * and + like birth and death...):
printf("*%s+", *a);
and you will see. Is this intended?
Edit 2: The variant with pointer arithmetic (only):
char** split_at (char* s, char d)
{
int numdelim = 0;
char* t = s; // need a copy
while(*t)
{
numdelim += *t == d;
++t;
}
char** final = (char**)malloc((numdelim + 2) * sizeof(char*));
char** f = final; // pointer to current position within final
t = s; // re-assign t, using s as start pointer for new strings
while(*t) // see above
{
if(*t == d) // delimiter found!
{
// can subtract pointers --
// as long as they point to the same array!!!
char* n = (char*)malloc(t - s + 1); // +1: terminating null
*f++ = n; // store in position pointer and increment it
while(s != t) // copy the string from start to current t
*n++ = *s++;
*n = 0; // terminate the new string
}
++t; // next character...
}
*f = NULL; // and finally terminate the string array
return final;
}
While I've now been shown a more elegant solution, I've found and rectified the issues in my code:
char** split_at (char* s, char d)
{
int numdelim = 0;
int x;
for (x = 0; s[x] != '\0'; x++)
{
if (s[x] == d)
{
numdelim++;
}
}
int a = 0;
int b = 0;
char** final = (char**)malloc((numdelim+1) * sizeof(char*));
for (int i = 0; i <= numdelim; i++)
{
int sizeofj = 0;
while ((s[a] != d) && (a < x))
{
sizeofj++;
a++;
}
final[i] = (char*)malloc(sizeofj);
a++;
int j = 0;
while (j < sizeofj)
{
final[i][j] = s[b];
j++;
b++;
}
final[i][j] = '\0';
b++;
}
return final;
}
I consolidated what I previously had as a helper function, and modified some points where I incorrectly incremented .

remove a specified number of characters from a string in C

I can't write a workable code for a function that deletes N characters from the string S, starting from position P. How you guys would you write such a function?
void remove_substring(char *s, int p, int n) {
int i;
if(n == 0) {
printf("%s", s);
}
for (i = 0; i < p - 1; i++) {
printf("%c", s[i]);
}
for (i = strlen(s) - n; i < strlen(s); i++) {
printf("%c", s[i]);
}
}
Example:
s: "abcdefghi"
p: 4
n: 3
output:
abcghi
But for a case like n = 0 and p = 1 it's not working!
Thanks a lot!
A few people have shown you how to do this, but most of their solutions are highly condensed, use standard library functions or simply don't explain what's going on. Here's a version that includes not only some very basic error checking but some explanation of what's happening:
void remove_substr(char *s, size_t p, size_t n)
{
// p is 1-indexed for some reason... adjust it.
p--;
// ensure that we're not being asked to access
// memory past the current end of the string.
// Note that if p is already past the end of
// string then p + n will, necessarily, also be
// past the end of the string so this one check
// is sufficient.
if(p + n >= strlen(s))
return;
// Offset n to account for the data we will be
// skipping.
n += p;
// We copy one character at a time until we
// find the end-of-string character
while(s[n] != 0)
s[p++] = s[n++];
// And make sure our string is properly terminated.
s[p] = 0;
}
One caveat to watch out for: please don't call this function like this:
remove_substr("abcdefghi", 4, 3);
Or like this:
char *s = "abcdefghi";
remove_substr(s, 4, 3);
Doing so will result in undefined behavior, as string literals are read-only and modifying them is not allowed by the standard.
Strictly speaking, you didn't implement a removal of a substring: your code prints the original string with a range of characters removed.
Another thing to note is that according to your example, the index p is one-based, not zero-based like it is in C. Otherwise the output for "abcdefghi", 4, 3 would have been "abcdhi", not "abcghi".
With this in mind, let's make some changes. First, your math is a little off: the last loop should look like this:
for (i = p+n-1; i < strlen(s); i++) {
printf("%c", s[i]);
}
Demo on ideone.
If you would like to use C's zero-based indexing scheme, change your loops as follows:
for (i = 0; i < p; i++) {
printf("%c", s[i]);
}
for (i = p+n; i < strlen(s); i++) {
printf("%c", s[i]);
}
In addition, you should return from the if at the top, or add an else:
if(n == 0) {
printf("%s", s);
return;
}
or
if(n == 0) {
printf("%s", s);
} else {
// The rest of your code here
...
}
or remove the if altogether: it's only an optimization, your code is going to work fine without it, too.
Currently, you code would print the original string twice when n is 0.
If you would like to make your code remove the substring and return a result, you need to allocate the result, and replace printing with copying, like this:
char *remove_substring(char *s, int p, int n) {
// You need to do some checking before calling malloc
if (n == 0) return s;
size_t len = strlen(s);
if (n < 0 || p < 0 || p+n > len) return NULL;
size_t rlen = len-n+1;
char *res = malloc(rlen);
if (res == NULL) return NULL;
char *pt = res;
// Now let's use the two familiar loops,
// except printf("%c"...) will be replaced with *p++ = ...
for (int i = 0; i < p; i++) {
*pt++ = s[i];
}
for (int i = p+n; i < strlen(s); i++) {
*pt++ = s[i];
}
*pt='\0';
return res;
}
Note that this new version of your code returns dynamically allocated memory, which needs to be freed after use.
Here is a demo of this modified version on ideone.
Try copying the first part of the string, then the second
char result[10];
const char input[] = "abcdefg";
int n = 3;
int p = 4;
strncpy(result, input, p);
strncpy(result+p, input+p+n, length(input)-p-n);
printf("%s", result);
If you are looking to do this without the use of functions like strcpy or strncpy (which I see you said in a comment) then use a similar approach to how strcpy (or at least one possible variant) works under the hood:
void strnewcpy(char *dest, char *origin, int n, int p) {
while(p-- && *dest++ = *origin++)
;
origin += n;
while(*dest++ = *origin++)
;
}
metacode:
allocate a buffer for the destination
decalre a pointer s to your source string
advance the pointer "p-1" positions in your source string and copy them on the fly to destination
advance "n" positions
copy rest to destination
What did you try? Doesn't strcpy(s+p, s+p+n) work?
Edit: Fixed to not rely on undefined behaviour in strcpy:
void remove_substring(char *s, int p, int n)
{
p--; // 1 indexed - why?
memmove(s+p, s+p+n, strlen(s) - n);
}
If your heart's really set on it, you can also replace the memmove call with a loop:
char *dst = s + p;
char *src = s + p + n;
for (int i = 0; i < strlen(s) - n; i++)
*dst++ = *src++;
And if you do that, you can strip out the strlen call, too:
while ((*dst++ = *src++) != '\0);
But I'm not sure I recommend compressing it that much.

Resources