Reversing String in C for loop error - c

I have an array of strings and am trying to reverse each string in the array to see if that string is a palindrome. I am using a for loop to increment an int i (the index). However after the I call the reverse function, the value of i becomes some really large number and I cant figure out why this is happening.
#include <stdio.h>
#include <string.h>
void revString(char *dest, const char *source);
int main() {
const char *strs[] = {
"racecar",
"radar",
"hello",
"world"
};
int i;
char res[] = "";
for (i = 0; i < strlen(*strs); i++) {
printf("i is %d\n", i);
revString(&res[0], strs[i]); //reversing string
printf("i is now %d\n", i);
//comparing string and reversed string
if (strcmp(res, strs[i]) == 0) {
printf("Is a palindrome");
} else {
printf("Not a palindrome");
}
}
return 0;
}
void revString(char *dest, const char *source) {
printf("%s\n", source);
int len = strlen(source);
printf("%d\n", len);
const char *p;
char s;
for (p = (source + (len - 1)); p >= source; p--) {
s = *p;
*(dest) = s;
dest += 1;
}
*dest = '\0';
}
This is the output showing the value of i before and after the revString function is called.
i is 0
i is now 1667588961
Illegal instruction: 4

There are multiple problems in your code:
You pass a destination array char res[] = ""; that is much too small for the strings you want to reverse. It's size is 1. This causes buffer overflow, resulting in undefined behavior.
Use char res[20]; instead.
You enumerate the array of string with an incorrect upper bound. Use this instead:
for (i = 0; i < sizeof(strs) / sizeof(*strs); i++)
The termination test for the loop in revString() is incorrect too: decrementing p when is equal to source has undefined behavior, although it is unlikely to have an consequences. You can simplify this function this way:
void revString(char *dest, const char *source) {
size_t len = strlen(source);
for (size_t i = 0; i < len; i++) {
dest[i] = source[len - i - 1];
}
dest[len] = '\0';
}
Here is the resulting code:
#include <stdio.h>
#include <string.h>
void revString(char *dest, const char *source) {
size_t len = strlen(source);
for (size_t i = 0; i < len; i++) {
dest[i] = source[len - i - 1];
}
dest[len] = '\0';
}
int main(void) {
const char *strs[] = { "racecar", "radar", "hello", "world" };
char res[20];
for (size_t i = 0; i < sizeof(strs) / sizeof(*strs); i++) {
revString(res, strs[i]);
//comparing string and reversed string
if (strcmp(res, strs[i]) == 0) {
printf("Is a palindrome\n");
} else {
printf("Not a palindrome\n");
}
}
return 0;
}

Here is Final Code with some change
#include <stdio.h>
#include <string.h>
void revString(char* dest, const char* source);
int main(){
const char* strs[] = {
"racecar",
"radar",
"hello",
"world"
};
static int i;
char res[] = "";
int length = (int) sizeof(strs)/sizeof(char*);
for(i = 0; i < length; i++)
{
printf("i is %d\n", i);
revString(&res[0], strs[i]); //reversing string
printf("i is now %d\n", i);
//comparing string and reversed string
if(strcmp(res, strs[i]) == 0){
printf("Is a palindrome");
}else{
printf("Not a palindrome");
}
}
return 0;
}
void revString(char* dest, const char* source){
printf("%s\n", source);
int len = (int) strlen(source);
printf("%d\n", len);
const char* p;
char s;
for(p = (source + (len - 1)); p >= source; p--){
s = *p;
*(dest) = s;
dest += 1;
}
*dest = '\0';
}
Change 1 :-
int i; to static int i; (Reason:- i is local variable you are calling
function so when function call the value of i will remove and after
that it will assign garbage value.)
change 2 :-
strlen(*strs) to length of array (because strlen(*strs) will give the
length of first string)

Related

How can I write the concatenated string to the given string pointer in C?

I am having trouble with the very last line in my function, where I am stilly learning the basics of C. I have the signature of this function given and am tasked to write a function to concatenate two strings. The commented line outputs the correct result.
#include <stdio.h>
#include <stdlib.h>
// 1) len = dst-len + max_dst_len
int strlcat(char *dst, const char *src, int max_dst_len) {
int len = 0;
while (dst[len] != '\0') {
len++;
}
int total_len = len + max_dst_len;
char *new_str = malloc(sizeof(char) * total_len);
for (int i = 0; i < len; i++) {
new_str[i] = dst[i];
}
for (int i = len; i < total_len; i++) {
new_str[i] = src[i - len];
}
new_str[total_len] = '\0';
//printf("%s <--\n", new_str);
dst = *new_str;
return total_len;
}
int main() {
char test1[] = "dst";
char test1src[] = "src";
printf("%s\n", test1);
printf("%d\n", strlcat(test1, test1src, 10));
printf("%s\n", test1);
}
You should not be adding max_dst_len to the length of dst. max_dst_len is the amount of memory that's already allocated in dst, you need to ensure that the concatenated string doesn't exceed this length.
So you need to subtract len from max_dst_len, and also subtract 1 to allow room for the null byte. This will tell you the maximum number of bytes you can copy from src to the end of dst.
In your main() code, you need to declare test1 to be at least 10 bytes if you pass 10 as the max_dst_len argument. When you omit the size in the array declaration, it sizes the array just big enough to hold the string you use to initialize it. It's best to use sizeof test1 as this argument, to ensure that it's correct for the string you're concatenating to.
#include <stdio.h>
int strlcat(char *dst, const char *src, int max_dst_len) {
int len = 0;
while (dst[len] != '\0') {
len++;
}
int len_to_copy = max_dst_len - len - 1;
int i;
for (i = 0; i < len_to_copy && src[i] != '\0'; i++) {
dst[len+i] = src[i];
}
dst[i] = '\0';
//printf("%s <--\n", new_str);
return i + len;
}
int main() {
char test1[6] = "dst";
char test1src[] = "src";
printf("%s\n", test1);
printf("%d\n", strlcat(test1, test1src, sizeof test1));
printf("%s\n", test1);
}

Trying to "copy" one string in reverse

I've been trying to "copy" one string to another, in reverse.
It kindof works, but it prints some weird symbols.
I've tried setting char copy[length2] but that makes the program not run at all.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define ARR_SIZE 50
int main()
{
char string[ARR_SIZE];
printf("Enter char array!\n");
fgets(string, ARR_SIZE, stdin);
string[strlen(string) - 1] = '\0';
int length = (strlen(string) - 1);
int length2 = (strlen(string) - 1);
printf("%s\t%d\n", string, length);
for (int i = 0; i <= length; i++)
{
printf("INDEX = %d CHAR = %c\n", i, string[i]);
}
printf("%d", length2);
char copy[ARR_SIZE];
for (int i = 0; i <= length2; i++)
{
copy[i] = string[length];
length--;
}
printf("\n%s", copy);
}
These are the minimal modifications I'd make to your code:
#include <stdio.h>
#include <string.h>
// remove unneeded headers
#define ARR_SIZE 50
int main(void)
{
char string[ARR_SIZE];
printf("Enter char array!\n");
fgets(string, ARR_SIZE, stdin);
string[strlen(string) - 1] = '\0';
// remove the -1 on the string length calculation, the NUL terminator is not
// included in strlen's return value
int length = strlen(string);
// no sense in calling strlen twice
int length2 = length;
// fixed `length` now prints the correct length
printf("%s\t%d\n", string, length);
// change from <= to <. The array indices where the characters live are
// [0, length-1].
for (int i = 0; i < length; i++)
{
printf("INDEX = %d CHAR = %c\n", i, string[i]);
}
// fixed `length2` now prints the correct length
printf("%d", length2);
char copy[ARR_SIZE];
for (int i = 0; i < length2; i++)
{
// last character in `string` lives at the `length`-1 index
copy[i] = string[length-1];
length--;
}
// `length2` is the index after the last char in `copy`, this needs
// to be NUL terminated.
copy[length2] = '\0';
// prints the reversed string
printf("\n%s", copy);
}
Demo
Use functions.
Terminate strings with null character \0 or simply 0.
char *copyreverse(char *dest, const char *src)
{
size_t len = strlen(src);
const char *end = src + len - !!len;
char *wrk = dest;
while(len--)
*wrk++ = *end--;
*wrk = 0;
return dest;
}
int main()
{
char dest[10];
char *src = "hello";
printf("`%s` reversed `%s`\n", src, copyreverse(dest, src));
}

Substring exists in string in C

#include <stdio.h>
#include <string.h>
int main()
{
char str1[80] = "downtown", str2[20] = "town";
int len1 = 0, len2 = 0, i, j, count;
len1 = strlen(str1);
len2 = strlen(str2);
for (i = 0; i <= len1 - len2; i++) {
for (j = i; j < i + len2; j++) {
count = 1;
if (str1[j] != str2[j - i]) {
count = 0;
break;
}
}
if (count == 1) {
break;
}
}
if (count == 1) {
printf("True");
} else {
printf("False");
}
}
In the above code, I'm trying to solve this one without using string functions apart from strlen() which can be replaced with a simple while loop. Is there any other way of checking for consecutive characters like firstly checking if the character is in the string, and if the i index is in the next position and not randomly in the string.
Here is a very clean way to do it using a function. It assumes that both str and sub are proper C-strings and returns a pointer to first match and NULL if no match.
char *substr(const char *str, const char *sub) {
if (!*sub)
return str; // Empty string is substring of all strings
while (*str) {
const char *sub1 = sub;
const char *str1 = str;
while (*str1++ == *sub1++) {
if (!*sub1)
return (char *)str;
}
str++;
}
return NULL;
}
This function is identical to the standard function strstr(), present in the C Standard library and declared in <string.h>.
There are some problems in the posted code:
count is uninitialized and only set if the inner loop is reached, which will not be the case if len1 < len2. count should be intialized to 0 to handle this case properly. found would be a more informative name for this variable.
Furthermore, the statement count = 1; should be moved before the inner loop to handle the case of an empty substring.
Here is a modified version:
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "downtown", str2[] = "town";
int found = 0;
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
if (len1 >= len2) {
for (size_t i = 0; i <= len1 - len2; i++) {
found = 1;
for (size_t j = 0; j < len2; j++) {
if (str1[i + j] != str2[j]) {
found = 0;
break;
}
}
if (found) {
break;
}
}
}
if (found) {
printf("True\n");
} else {
printf("False\n");
}
return 0;
}
I did not readily see OP's error. See #chqrlie.
Minor: Use size_t i, not int to cope with long strings.
strlen(str1) runs down the entire string, even when not needed, so that is avoided.
Alternative:
Fixed a faulty case in #klutt otherwise good answer.
Added const for greater application.
Return beginning of match on success.
Simplified and test harness added.
I like the needle in a haystack identifiers
char* substr3(const char *haystack, const char *needle) {
while (*haystack) {
const char *htmp = haystack;
const char *ntmp = needle;
while (*htmp == *ntmp && *htmp) {
htmp++;
ntmp++;
}
if (!*ntmp) {
return (char*) haystack; // Beginning of match
}
haystack++;
}
return *needle ? NULL : (char *) haystack;
}
int main(void) {
printf("%s\n", substr3("ababc", "abc"));
printf("%s\n", substr3("abc", "abc"));
printf("%s\n", substr3("abd", "ab"));
printf("%s\n", substr3("abc", ""));
printf("%s\n", substr3("", ""));
printf("%p\n", substr3("a", "abc"));
printf("%p\n", substr3("aba", "abc"));
printf("%p\n", substr3("x", "ab"));
printf("%p\n", substr3("aaa", "ab"));
return 0;
}
Some tighter code
char* substr4(const char *haystack, const char *needle) {
do {
const char *htmp = haystack;
const char *ntmp = needle;
while (*htmp == *ntmp && *ntmp) {
htmp++;
ntmp++;
}
if (!*ntmp) {
return (char*) haystack; // Beginning of match
}
} while (*haystack++);
return NULL;
}
Simple && naive:
char *substr2(char *str, char *sub) {
if (!*sub) return str; // not needed: see the generated code.
for(; *str; str++) {
size_t pos;
for(pos=0; str[pos] ; pos++) {
if (str[pos] != sub[pos]) break;
}
if (!sub[pos]) return str;
}
return NULL;
}
Generally:
if you don't try to outsmart the compiler: you win.
fewer variables: you win
fewer conditions inside the loop: win
try to be smart: you'll lose
when all else fails: use KMP or BM search

Why does variable loss its value after end of block?

I have written a code to test a substring operation.
test.c
#include "compiler_expression.h"
#include <stdio.h>
int main()
{
char *s1 = "(Hello) World";
int l = 0;
printf("%s\n", substr_limits(s1, '(', ')', &l));
return 0;
}
compiler_expression.h
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
// strcpy(s1, s2) Copies s2 into s1
// strcat(s1, s2) Concatenates s2 onto the end of s1
// strlen(s1) Returns the length of s1
// strcmp(s1, s2) Returns 0 if s1 and s2 are the same; less than 0 if s1<s2; greater
// than 0 if s1>s2
// strchr(s1, ch) Returns a pointer to the first occurrence of ch in s1
// strstr(s1, s2) Returns a pointer to the first occurrence of s2 in s1
char * substr(char *str, int begin, int end)
{
if (begin<0 || end>strlen(str))
{
printf("\nError: arguments (begin or end) is out of index\n");
exit(1);
}
char *res = malloc(end-begin);
for (int i=begin; i<end; i++)
{
res[i-begin] = str[i];
}
return res;
}
char *substr_limits(char *str, char begin_char, char end_char, int *no_limit)
{
*no_limit = 1;
if (begin_char==end_char)
{
printf("%s\n", "::begin char is equal to end char");
int begin_limit = 0;
int end_limit = 0;
int limit_num = 0;
for (int i=0;i<strlen(str);i++)
{
printf("%s\n", "::begin first loop");
if (str[i]!=begin_char) continue;
printf("%s\n", "::limit char found");
if (limit_num==1)
{
printf("%s\n", "::limit_num==1");
end_limit = i;
char *res = malloc(end_limit-begin_limit-1);
for (int j=begin_limit+1;j<end_limit;j++)
{
printf("%s%c\n", "::char==", str[j]);
res[j-begin_limit] = str[j];
printf("%s%c\n", "::res_char==", res[j-begin_limit]);
}
printf("%s%s\n", "::result==", res);
return res;
}
*no_limit = 0;
limit_num = 1;
begin_limit = i;
}
if (limit_num!=0) {
printf("\nError: The limits are not justified\n");
exit(1);
}
if (*no_limit==1) {
return str;
}
}
else
{
int begin_limit = 0;
int end_limit = 0;
int limit_num = 0;
for (int i=0;i<strlen(str);i++)
{
if (str[i]!=begin_char&&str[i]!=end_char) continue;
*no_limit = 0;
if (str[i]==begin_char)
{
limit_num += 1;
if (limit_num==1) begin_limit = i;
}
else if (str[i]==end_char)
{
limit_num -= 1;
if (limit_num==0)
{
end_limit = i;
char *res = malloc(end_limit-begin_limit);
for (i=begin_limit+1;i<end_limit;i++)
{
res[i-begin_limit] = str[i];
}
return res;
}
}
}
if (limit_num!=0) {
printf("\nError: The limits are not justified\n");
exit(1);
}
if (*no_limit==1) {
return str;
}
}
}
at the function (substr_limits), there is a problem: when the condition (begin_char==end_char) is true and after executing all code, I noticed at last operations before executing (return res;) that the code is run in the correct way and get all specified characters from (str) into (res) variable but after ending block of (for loop), I have noticed that the value of (res) is blank string which makes also the returned value is blank string, I was trying to solve problem by writing this code:
char *res = malloc(end_limit-begin_limit-1);
for (int j=begin_limit+1;j<end_limit;j++)
{
printf("%s%c\n", "::char==", str[j]);
res[j-begin_limit] = str[j];
printf("%s%c\n", "::res_char==", res[j-begin_limit]);
if (j==end_limit-1) {
printf("%s%s\n", "::result==", res);
return res;
}
}
But the problem still exists!
Bug #1. Instead of this:
char *res = malloc(end-begin);
for (int i=begin; i<end; i++)
{
res[i-begin] = str[i];
}
This:
char *res = malloc(end-begin+1); //+1 for null char
for (int i=begin; i<end; i++)
{
res[i-begin] = str[i];
}
res[end-begin] = '\0'; // null terminate the string that gets returned
I don't see substr getting invoked, but I see other variations of this string copy pattern missing the null char in your limits function.
More to come as I keep looking at the code....

Efficiently replace a substring in a string

I have made two functions that find a substring index and substitute that substring in the string. I'm glad I jury rigged this at all, given that similar questions previously asked were never answered/marked as closed without any help. Is there a cleaner method?
void destroy_substr(int index, int len)
{
int i;
for (i = index; i < len; i++)
{
string[i] = '~';
}
}
void find_substr_index(char* substr)
{
int i;
int j;
int k;
int count;
int len = strlen(substr);
for (i = 0; i < strlen(string); i++)
{
if (string[i] == substr[0])
{
for(j = i, k = 0; k < len; j++, k++)
{
if (string[j] == substr[k])
{
count++;
}
if (count == len)
destroy_substr((j - len + 1), len);
}
j = 0;
k = 0;
count = 0;
}
}
}
Your code seems like you're trying to re-inventing your own wheel.
By using standard C functions, which is strstr() and memset(), you can achieve the same result as you expected.
#include <stdio.h>
#include <string.h>
char string[] = "foobar foobar foobar";
char substr[] = "foo";
char replace = '~';
int main() {
int substr_size = strlen(substr);
// Make a copy of your `string` pointer.
// This is to ensure we can safely modify this pointer value, without 'touching' the original one.
char *ptr = string;
// while true (infinite loop)
while(1) {
// Find pointer to next substring
ptr = strstr(ptr, substr);
// If no substring found, then break from the loop
if(ptr == NULL) { break; }
// If found, then replace it with your character
memset(ptr, replace, substr_size);
// iIncrement our string pointer, pass replaced substring
ptr += substr_size;
}
printf("%s\n", string);
return 0;
}
How about this:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char string[] = "HELLO hello WORLD world HELLO hello ell";
char substring[] = "ell";
int stringLength = strlen(string);
int substringLength = strlen(substring);
printf("Before: %s\n", string);
if(substringLength <= stringLength)
{
int i;
int j;
for(i = 0, j = stringLength - substringLength + 1; i < j; )
{
if(memcmp(&string[i], substring, substringLength) == 0)
{
memset(&string[i], '~', substringLength);
i += substringLength;
}
else
{
i++;
}
}
}
printf("After: %s\n", string);
return 0;
}
Key ideas are:
You only need to scan the string (stringLength - substringLength) times
You can use functions from string.h to do the comparison and to replace the substring
You can copy the new string in place. If you want to support insertion of longer strings you will need to manage memory with malloc()/realloc(). If you want to support insertion of smaller strings you'll need to advance the pointer to the beginning by the length of the replacement string, copy the rest of the string to that new location, then zero the new end of the string.
#include <stdio.h>
#include <string.h>
#include <err.h>
int main(int argc, char **argv)
{
char *str = strdup("The fox jumps the dog\n");
char *search = "fox";
char *replace = "cat";
size_t replace_len = strlen(replace);
char *begin = strstr(str, search);
if (begin == NULL)
errx(1, "substring not found");
if (strlen(begin) < replace_len)
errx(1, "replacement too long");
printf("%s", str);
memcpy(begin, replace, replace_len);
printf("%s", str);
return 0;
}

Resources