My task is
Realization of function reverse.When implementing a function, it is forbidden to use the functions of the C language libraries.
Can someone help me with the realization of function strlen?
My function:
void revstr( char * str){
int i, len = 0;
len = strlen(str);
for (i = 0; i <= len / 2; i++){
*(str + len - i) = *(str + i);
*(str + i) = *(str + len - i - 1);
}
for (i = len / 2; i <= len; i++)
*(str + i) = *(str + i + 1);
}
strlen returns the length of the string. So you can iterate through whole string, increment a counter and return the counter as the length
code:
size_t my_strlen(char *str) {
size_t i;
for (i = 0; str[i] != '\0'; i++)
;
return i;
}
Edit:
Your revstr code seems really complex. Here is a simple way of doing that.
char * revstr( char * str)
{
int i,end,len;
char temp;
len= my_strlen(str);
end = len-1;
for(i=0;i<len/2;i++)
{
temp=str[i];
str[i]=str[end];
str[end--]=temp;
}
return str;
}
Note: It is good practice to write str[i] instead of *(str + i).
and the code works perfect for your question :)
I don't think it's ever "forbidden" to use standard library functions, using strlen() here is fine. Their is no need to write your own. As for your reverse function, I suggest you break down the problem.
A simple approach is to have two counters, one which starts at the beginning of the string, and one that starts at the end of the string. You'll need to swap these characters, which can be done like this:
void swap(char *a, char *b) {
char temp = *a;
*a = *b;
*b = temp;
}
Once these start and end characters are swapped, increment the start counter and decrement the end counter to move inward the string, and swap again. This can be written in a simple loop:
for (start = 0, end = len-1; start <= end; start++, end--) {
swap(&string[start], &string[end]);
}
I'll let you fill in the rest of the code, but this gives the general idea.
I do it like this:
void revstr(char *str)
{
char *s = str;
char *e;
for (e=s; *e; ++e); //find the end
if (e==s)
return; //empty string
for (--e; e>s; --e,++s)
{
char t = *s;
*s = *e;
*e = t;
}
}
Related
I'm quite new to C and am trying to write a function, which will split a string into an array of strings at a specific delimiter. But strangely I can only write at the first index of my char** array of strings, which will be my result. For example if I want to split the following string "Hello;;world;;!" at ;; I get [ "Hello" ] instead of [ "Hello", "world", "!" ]. I can't find my mistake.
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "strings.h"
int split(char **dest, const char *src, const char *splitStr) {
char buffer[16384];
int counter = 0;
int len = strlen(splitStr);
int flag = 0;
int start = 0;
for (int i = 0; i < strlen(src); i++) {
flag = 0;
if (src[i] == splitStr[0]) {
for (int j = 1; j < len; j++) {
//check if all elements in delimiter are in string
if (src[i + j] == splitStr[j] && j != (len - 1)) {
continue;
}
else if(src[i + j] == splitStr[j] && j == (len - 1)) {
buffer[i] = '\0';
dest[counter] = malloc(sizeof(char) * (i - start + 1));
strncpy(dest[counter], buffer + start, (i - start));
start = i + (len-1)
flag = 1;
i += (len - 1);
counter++;
}
//if not break
else {
break;
}
}
}
if (i == (strlen(src) - 1)) {
buffer[i] = src[i];
buffer[i + 1] = '\0';
counter++;
break;
}
if (flag == 0) {
buffer[i] = src[i];
}
}
return counter;
}
A proper function call would look like this:
auto src = "Hello;;world;;!";
auto buffer = (char **)malloc(32);
int count = split(buffer, src, ";;");
The buffer should contain, all the splitted strings, more or less like this: [ "Hello", "world", "!" ].
Currently my result buffer looks like this in the debugger. It appears as only the first element is written into it.
There are multiple problems in your code:
you compute string lengths repeatedly, which may be very inefficient. Instead of testing i < strlen(src) you should write src[i] != '\0'.
your test for check a matching delimiter is too complicated. You should use strstr to locate the delimiter string in the remaining portion of the string.
strncpy does not do what you think: strncpy(dest[counter], buffer + start, (i - start)); should be replaced with memcpy(dest[counter], buffer + start, i - start); and you must set the null terminator explicitly: dest[counter][i - start] = '\0'; You should read why you should never use strncpy().
it is unclear why you use buffer at all.
Here is a modified version:
#include <stdlib.h>
#include <string.h>
/* if POSIX function strndup() is not defined on your system, use this */
char *strndup(const char *str, size_t n) {
size_t len;
for (len = 0; len < n && str[len] != '\0'; len++)
continue;
char *s = malloc(len + 1);
if (s != NULL) {
memcpy(s, str, len);
s[len] = '\0';
}
return s;
}
int split(char **dest, const char *src, const char *splitStr) {
const char *p = str;
const char *end;
int counter = 0;
size_t len = strlen(splitStr);
if (len == 0) {
/* special case */
while (*p != '\0') {
dest[counter++] = strndup(p++, 1);
}
} else {
while ((end = strstr(p, splitStr)) != NULL) {
dest[counter++] = strndup(p, end - p);
p = end + len;
}
dest[counter++] = strdup(p);
}
return counter;
}
First of all you are not updating the start variable after you have copied the first string.
For simple debugging I would recommend adding some printf statements to see what is going on.
Proper formatting is not to be underestimated to make the code easy to read and easier to debug.
Also it is not clear what the buffer is for, and I think you can do without it.
The tips in the comments are also good. Split the function into smaller pieces and structure your code so it is simple to read.
A suggestion is to write a function to find the index of the next split string and the end of the string. Then you can use that to get the index and length you need to copy.
/*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 =)
Is this function the fastest, most optimized way of reversing a string in C? This runs in O(n/2) time. The optimization is that it only iterates through half of the string.
char* str_reverse(char *str, int len)
{
char word[len];
int i;
for (i = 0; i <= len / 2; ++i) {
word[i] = str[len - i - 1];
word[len - i - 1] = str[i];
}
word[len] = '\0';
return word;
}
Maybe something like this?
char *str_reverse_in_place(char *str, int len)
{
char *p1 = str;
char *p2 = str + len - 1;
while (p1 < p2) {
char tmp = *p1;
*p1++ = *p2;
*p2-- = tmp;
}
return str;
}
You'll find algorithms taking less instructions, like this in place reverse
char* str_reverse_in_place(char *str, int len)
{
int i;
for (i = len/2-1 ; i >= 0 ; --i) {
char c = str[i];
str[i] = str[len-i-1];
str[len-i-1] = c;
}
return str;
}
Optimizing for speed at that level, look at the inline keyword, also compile with (for gcc) with -O3 (does usually a better job that adding register ... by yourself).
If you need to have the reversed string elsewhere, either provide it in the function (being allocated for strlen(str)+1 - actually len+1 here - characters)
char* str_reverse(char *str, char *reverse, int len)
{
int i;
for (i = len-1 ; i >= 0 ; --i) {
reverse[i] = str[len-i-1];
}
reverse[len] = 0;
return reverse;
}
or malloc it (it will have to be freed by the caller).
char* str_reverse_malloc(char *str, int len)
{
char *reverse = malloc(len+1);
if ( ! reverse) return NULL;
int i;
for (i = len-1 ; i >= 0 ; --i) {
reverse[i] = str[len-i-1];
}
reverse[len] = 0;
return reverse;
}
The "most" optimized way must address the question of CPU and memory architecture as well as what is being reversed (long strings or short strings and what is the distribution).
There's no way to get relax the O(N) requirement, but one can use techniques such as loop unrolling, loop blocking and parallelism to optimize cache misses for very large strings. Also one can increase the word size and swap words, dwords or larger entities in-place (while dealing with the probable alignment issue).
// This will most likely be faster than byte-wise copying, but it's not O(N/8)...
if (len & 7 == 0)
{
uint32_t *dst = src+len-4;
uint32_t *src = (uint32_t *)ptr;
while (src<dst)
{
a = *src; b = *dst;
*src++ = byte_swap(b);
*dst-- = byte_swap(a);
}
}
int main() {
char str[100], temp;
int i, j = 0;
printf("\nEnter the string :");
fgets(str, sizeof(str), stdin);
i = 0;
j = strlen(str) - 1;
while (i < j) {
temp = str[i];
str[i] = str[j];
str[j] = temp;
i++;
j--;
}
printf("\nReverse string is :%s", str);
return (0);
}
Firstly, when considering complexity, it's impossible to do better than O(n) where n is the length of the string. Every single element - except for one element in the middle when n is odd - needs to be moved, and there is no way around it.
Also, this problem is so simple, so it's barely impossible to do something dramatic if you only consider the pure algorithm and not take real life factors into account. For very large strings, the single most important thing will be if the algorithm is cache friendly or not.
Here is a version which is cache friendly. It's not an in-place variant. If modified to in place, it can become even more cache friendly. First, we need a simple reverse function which does NOT terminate the destination string:
void str_reverse_aux(char *dest, const char *src, int len) {
for (int i = len-1 ; i >= 0 ; --i)
dest[i] = src[len-i-1];
}
After that, we use an algorithm that takes the N first and N last characters of a string, reverse both and swap their positions. Then move N step inwards from both directions and repeat until there is less than 2*N characters to process. Then we call the above function to finish things of.
void str_reverse(char *dest, const char *src, int block_size) {
int len = strlen(src);
char *d = dest;
const char *s = src;
int chunks = len / (2 * block_size);
char *dtail = &dest[len];
char *stail = &src [len];
for(int i=0; i<chunks; i++) { // Reverse the string blockwise
dtail -= block_size;
stail -= block_size;
char *buf = alloca(block_size); // Almost equivalent to char buf[block_size];
str_reverse_aux(buf, s, block_size);
memcpy(d, buf, block_size);
str_reverse_aux(buf, stail, block_size);
memcpy(dtail, buf, block_size);
d+=block_size;
s+=block_size;
}
str_reverse_aux(d, s, len - 2* chunks * block_size); // Take care of remainder
dest[len] = 0;
}
For very large strings, this will give a huge performance boost.
Here is a variation that does not require the length to be passed and will swap both the beginning and ending characters at a given offset within the string each pass through the loop:
/** strrevstr - reverse string, swaps src & dest each iteration.
* Takes valid string and reverses, original is not preserved.
* If str is valid, returns pointer to str, NULL otherwise.
*/
char *strrevstr (char *str)
{
if (!str) {
printf ("strrevstr() error: invalid string\n");
return NULL;
}
char *begin = str;
char *end = str + strlen (str) - 1;
char tmp;
while (end > begin)
{
tmp = *end;
*end-- = *begin;
*begin++ = tmp;
}
return str;
}
Here's one just for fun. But I reckon it's as fast as some of the others posted here.
The fun part is in the bitwise xor! Do that on paper, it works!
void inplace_swap(char *x, char *y) {
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}
void reverse_string(char *str, int len) {
int first, last;
for (first = 0, last = len - 1; first < last; first++, last--) {
inplace_swap(&str[first], &str[last]);
}
}
This is faster than fastest way to reverse string in C ... ;)
#include <stdio.h>
#include <string.h>
int main(void){
char string[] = "hello";
int len = strlen(string);
char reverse[len];
for (int i = 0; i<len; i++){
reverse[i] = string[len - i - 1];
}
printf("%s",reverse);
return 0;
}
I wrote the following function to inverse a string s
char *strinverse( const char *s ){
char *t;
int i = 0;
while (*s) {
s++;
i++;
}
while (i >= 0){
s--;
*t = *s;
t++;
i--;
}
*t = '\0';
return t;
}
int main(void){
char v[4]="abc";
char r[4];
char *pr = r;
pr = strinverse(v);
printf("%s", pr);
return 0;
}
The idea is to find out the length of the string s in the first while-loop, then to decrease the pointer of s while copying the respective values into t. For some reason the program crashes and the compiler gives me no information. Maybe there's something wrong in the main function? Thanks for your advices!
Answer edited
#include<stdio.h>
#include<stdlib.h>
char *strinverse(const char *s ){
char *t, *p;
int i = 0;
while (*s) {
s++;
i++;
}
t = (char*)malloc((i + 1) * sizeof(char)); //added this!
p = t;
while (i >= 0){
s--;
*t = *s;
t++;
i--;
}
*t = '\0';
return p;
}
int main(void){
char v[4]="abc";
char *pr;
pr = strinverse(v);
printf("%s\n", pr);
return 0;
}
The reason that program crashes is that you have not allocated space for pointer t. In this case your program invokes undefined behavior. Allocate space for t
t = malloc(i + 1);
Do not forget to free memory at the end using free(t).
I would use a function that changes the string in-place instead.
void reversestr(char *s)
{
char tmp;
size_t i, len = strlen(s);
for (i = 0; i < len / 2; i++) {
tmp = s[i];
s[i] = s[len - 1 - i];
s[len - 1 - i] = tmp;
}
s[len] = '\0';
}
If you need the reversed string separately, you can just use strdup before you call reversestr. BTW: function names that start with "str" are reserved for functions of the C standard library.
you have not make malloc in the pointer "t" and you have problem in this line "*t = *s;"
I'm trying to reverse the order of words in a sentence in place, eg:
This sentences words are reversed.
becomes
reversed. are words sentences This
This is what I have so far, which almost works:
I use the strrev function to reverse the string, and then the inprev function to send each word to the strrev function individually, to reverse them back to the original orientation, but in reversed order.
Sending a pointer for the start and end of the strrev function might seem a bit silly, but it allows the same function to be used in inprev(), sending off a pointer to the start and end of individual words.
#include <stdio.h>
#include <string.h>
void strrev(char * start, char * end);
void inprev(char * start);
int main(void)
{
char str[] = "Foobar my friends, foobar";
char * end = (str + strlen(str) -1);
puts(str);
strrev(str, end);
puts(str);
inprev(str);
puts(str);
return 0;
}
void strrev(char * start, char * end)
{
char temp;
while (end > start)
{
temp = *start;
*start = *end;
*end = temp;
start++;
end--;
}
}
void inprev(char * start)
{
char * first = start;
char * spcpnt = start;
while (*spcpnt)
{
while (*spcpnt != ' ' && *spcpnt)
spcpnt++;
strrev(start, spcpnt-1); // removing the -1 sends the space on the
start = spcpnt++; // other side to be reversed, doesn't stop
// the problem.
}
}
Here is the output:
Foobar my friends, foobar
raboof ,sdneirf ym rabooF
foobarfriends, my Foobar
The problem is that the lack of a final space at the end of the final word means that a space is missing between that word and the preceeding one in the final string, and instead gets thrown onto the end of the last word, which was the first word in the original string. Sending off the space on the other side of the word only moves the problem elsewhere. Can anyone see a solution?
You just need to move the start pointer in the inprev function to skip the space between words. As this appears to be homework (correct me if I'm wrong) I'll just say that all you need to do is move the location of one operator.
But, this produces a problem, namely, the inprev performs a buffer overrun because the search isn't terminated properly. A better way to do it is:
while not end of string
search for start of word
start = start of word
search for end of word
strrev (start, end)
and that will take care of multiple spaces too. Also, U+0020 (ASCII 32, a space) is not the only white space character. There are standard library functions that test characters. They are in <ctype.h> and start with is..., e.g. isspace.
Sometimes things get easier if you don't use pointers but offsets.
The strspn() and strcspn() library functions more or less force you to use offsets,
and deal with the end-of-string condition quite nicely.
#include <stdio.h>
#include <string.h>
size_t revword(char *str);
void revmem(void *ptr, size_t len);
size_t revword(char *str) {
size_t pos,len;
for (pos=len=0; str[pos]; pos += len) {
len = strspn( str+pos, " \t\n\r");
if (len) continue;
len = strcspn( str+pos, " \t\n\r");
if (!len) continue;
revmem( str+pos, len );
}
revmem( str, pos );
return len;
}
void revmem(void *ptr, size_t len)
{
size_t idx;
char *str = (char*) ptr;
if (len-- < 2) return;
for (idx = 0; idx < len; idx++,len--) {
char tmp = str[idx];
str[idx] = str[len];
str[len] = tmp;
}
}
int main (int argc, char **argv)
{
if (!argv[1]) return 0;
revword(argv[1] );
printf("'%s'\n", argv[1] );
return 0;
}
Figured out a solution; here is my revised function that works properly.
void inprev(char * str)
{
_Bool inword = 0;
char * wordend;
char * wordstart;
while(*str)
{
if(!isspace(*str) && (inword == 0))
{
wordstart = str;
inword = 1;
}
else if (isspace(*str) && (inword == 1))
{
wordend = str-1;
inword = 0;
strrev(wordstart, wordend);
}
str++;
}
if (*str == '\0')
strrev(wordstart, str-1);
}
char * wordend is uneccessary as you can just pass str-1 to the strrev function, but it makes it a bit more clear what's happening.
The following algorithm is in-place and runs in 2 steps. First it reverses the entire string. Then it reverses each word.
#include <stdio.h>
void reverse(char *str, int len)
{
char *p = str;
char *e = str + len - 1;
while (p != e) {
*p ^= *e ^= *p ^= *e;
p++;
e--;
}
}
void reverse_words(char *str)
{
char *p;
// First, reverse the entire string
reverse(str, strlen(str));
// Then, reverse each word
p = str;
while (*p) {
char *e = p;
while (*e != ' ' && *e != '\0') {
e++;
}
reverse(p, e - p);
printf("%.*s%c", e - p, p, *e);
if (*e == '\0')
break;
else
p = e + 1;
}
}
int main(void) {
char buf[] = "Bob likes Alice";
reverse_words(buf);
return 0;
}
void reverse_str(char* const p, int i, int j) // helper to reverse string p from index i to j
{
char t;
for(; i < j ; i++, j--)
t=p[i], p[i]=p[j], p[j]=t;
}
void reverse_word_order(char* const p) // reverse order of words in string p
{
int i, j, len = strlen(p); // use i, j for start, end indices of each word
reverse_str(p, 0, len-1); // first reverse the whole string p
for(i = j = 0; i < len; i = j) // now reverse chars in each word of string p
{
for(; p[i] && isspace(p[i]);) // advance i to word begin
i++;
for(j = i; p[j] && !isspace(p[j]);) // advance j to past word end
j++;
reverse_str(p, i, j-1); // reverse chars in word between i, j-1
}
}