Any issues with the following in-place reverse function? - c

I wrote this following in-place reverse function and it works fine.
However, when I googled for a solution, I found plethora of more complex solutions but nothing this simple. Will the following not work for certain inputs or has some performance issues?
void reverse(char* str) {
int n = strlen(str);
char temp;
for (int i=0; i<n/2; i++) {
temp = str[i];
str[i] = str[n-i-1];
str[n-i-1] = temp;
}
}
int main() {
char input[] = "Reverse Me!";
reverse(input);
return 0;
}

Yes. This may work correctly on your system; however, strlen returns size_t, which might have a greater precision than int, resulting in only a part (or none?) of the actual string being reversed. That's an easy fix: Declare n as a size_t instead of an int.
Your solution could be simpler, if you were to decrease n each iteration while you're increasing x. Then you wouldn't need as much of the subtraction logic, or any of the division logic.
void reverse(char *str) {
for (size_t x = 0, y = strlen(str); y --> x; x++) {
char temp = str[x];
str[x] = str[y];
str[y] = temp;
}
}

No, there are no issues with your code. It will work perfectly in any situation with ASCII characters.

#Rahat Mahbub is right about size_t. On many 64 bit machines, your algorithm will fail for strings 2^31 in length or longer. Using size_t will prevent that.
But the main problem is the complexity. A clearer code is something like:
void reverse(char *str) {
if (*p == '\0') return;
for (size_t i = 0, j = strlen(str) - 1; i < j; ++i, --j) {
char tmp = str[i];
str[i] = str[j];
str[j] = tmp;
}
}
In words, place i at the start of the string and j at the end. Swap characters they index and move both toward the middle of until they touch.
With pointers, it's even a bit more succinct:
void reverse(char *p) {
if (*p == '\0') return;
for (char *q = p + (strlen(p) - 1); p < q; ++p, --q) {
char tmp = *p;
*p = *q;
*q = tmp;
}
}
Unfortunately as #chux pointed out, you need the if or some other footwork to avoid computing q = p - 1 on empty input, which is not a valid pointer. In the indexed version, you need it because size_t is an unsigned type; it has no -1 value.

Related

Custom Reverse and strlen functions in C

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;
}
}

Extracting a portion of an array by using pointer in C

For example if I have an array and I want to extract elements of this array with a specified value as a new array. I did as the following:
int a[10] = { 1, 2, 1, 3, 2, 3, 4, 1, 2, 6 };
int i, k;
int count = 0;
for (i = 0; i < 10; i++) {
if (a[i] == 1) {
count = count + 1;
}
}
int b[count];
k = 0;
for (i = 0; i < 10; i++) {
if (a[i] == 1) {
b[k] = a[i];
k = k + 1;
}
}
So, for the array "a" I extracted all the elements of value 1, and make them as a new array "b". How can I achieve the same thing by using pointers? Will it be conciser than this way? If it is possible, is there any other advantages?
I think you already noticed that you just had to write 1 several times; yet I suppose you want that it works for arbitrary conditions.
"Using a pointer" can mean dynamic memory allocation instead of a variable length array. Just for the sake of having use a pointer, you could then write:
int *b = malloc(count * sizeof(int));
k = 0;
for (i = 0; i < 10; i++) {
if (a[i] == 1) {
b[k] = a[i];
k = k + 1;
}
}
If, just for sake of using a pointer for the writing process, too, you could adapt the program as follows:
int *b = malloc(count * sizeof(int));
int *bPtr = b;
for (i = 0; i < 10; i++) {
if (a[i] == 1) {
*bPtr++ = a[i];
}
}
Hope it helps a bit.
If you don't know which portion of the array your target values will be in, as in your case, where you're searching the entire unsorted array for a specific value, then there is no advantage to using pointers rather than a linear search to find the elements in the array you are after.
If, however, you are trying to access or copy a contiguous set of elements starting at a known index in the array, then you could use a pointer to simplify things. For example, if I'm after the last few elements in an array of chars, this works:
#include <stdio.h>
int main()
{
char str[100] = "I don\'t want anyone to think I hate the Calgary Flames";
char *c = (str + 29);
printf("%s\n", c);
return 0;
}
Output:
I hate the Calgary Flames
In this case, no, there is no benefit. a[i] is already basically a + (sizeof(int) * i). Even if you used pointers, you'd still have to do all the counting anyway to make sure you don't walk off the end of the array.
Where its often handy is with a null terminated array of pointers, such as a string, where you don't know the length. But it's not really about performance. As you can see below, they have to do roughly the same things.
char string[] = "foo bar";
// Allocate and initialize i.
// `string + i` twice, compare, increment.
for( int i = 0; string[i] != '\0'; i++ ) {
printf("%c", string[i]);
}
puts("");
// Allocate and initialize s.
// Dereference s twice, compare, increment.
for( char *s = string; *s != '\0'; s++ ) {
printf("%c", *s);
}
puts("");
Where iterating by pointer is handy is when you need to iterate through an array in several steps. Instead of passing around the original array pointer plus your last index, and changing all your function signatures to accommodate, just pass around the incremented pointer and return the incremented pointer. This allows you to use standard string functions on the middle of a string.
#include <stdio.h>
char *findVal( char *string, char delim ) {
char *val = string;
for( ; *val != '\0' && *val != delim; val++ ) {
}
if( val == '\0' ) {
return NULL;
}
else {
// val is sitting on the ':'
return val+1;
}
}
int main() {
char string[] = "this:that";
char *val = findVal(string, ':');
if( val != NULL ) {
// Just use val, not string[valIdx].
printf("%s\n", val);
}
}
This is also safer. With an offset there's two things which must remain in sync, the pointer and the offset; that opens the possibility that the wrong offset will be used with the wrong pointer. An incremented pointer carries its offset with it.
As has been pointed out in the comments, you can tighten up the second loop like so:
int b[count];
for (i = 0; i < count; i++) {
b[i] = 1;
}

Fastest way to reverse a string in C

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;
}

C++: error: lvalue required as left operand of assignment

I receive the error in the title when I try to run this code:
//Copies the information from one array to another until a set length using pointers
void strncpy2(char* t, const char* s, const unsigned int n)
{
unsigned int i;
for (i = 0; i < n /* This i < n causes the error */ && *t++ = *s++; i++);
*(t + i) = '\0';
}
When I take out the i < n it works fine, and when I move the i < n to the right of the *t++ = *s++ I get no compiling error, however the code doesn't work as intended.
What's going on here? I appreciate any help. Thank you!
You need parentheses around the assignment in the loop, since the && operator has higher precedence than the assignment. Try this:
for (i = 0; i < n && (*t++ = *s++); i++);
The semantic of your call is not clear, especially it differs significantly from strncpy().
You seem to manipulate raw string but you don't check for the end of string, which is unsafe.
Moreover the assignment after the loop uses "t" as a base pointer, which is wrong since you modified it.
I don't know what "n" implies on the size of t, if t has to be at least size n, you have to stop iterating at n-2 or your final assignment is out-of-bound.
Finally I stay away from codes that have side effects in the for loop header.
void strncpy2(char* t, const char* s, const unsigned int n)
{
unsigned int i;
for (i = 0; i < n-1; i++) {
t[i] = s[i];
if(s[i] == '\0') return;
}
t[i] = '\0';
}
if you want to use pointers chasing (c):
void strncpy2(char* t, const char* s, const unsigned int n)
{
unsigned int i;
for (i = 0; i < n-1; i++) {
*t = *s;
if(*s == '\0') return;
t++; s++;
}
*t = '\0';
}
Now I don't know what is the point of doing that, already having strncpy() and memcpy() in the standard library. And of course, C++ code should avoid raw string ;)

Help with reversing a string in C

I am trying to reverse a character string in C
Here is what I have
void reverse(char str[]) {
int i = 0;
int length;
// Get string length
for (i = 0; str[i] != '\0' ; ++i) {
length = i;
}
char reversed[1000];
int j;
j = 0;
// Reverse it
for (j = 0; j < length ; ++j) {
reversed[j] = str[length - j];
}
}
I know that reversed contains the string reversed, but I'm not sure how to modify the original str without throwing away data I need.
I also don't know how to set strto reversed without looping again.
Would it be acceptable to do another...
int m;
m = 0;
for (m = 0; m < length ; ++m) {
str[j] = reversed[j];
}
Usually I'd say this many loops smells, but I'm also still quite unfamiliar with the language so I'm not sure...
Update
Thanks for all the answers guys, and I appreciate the edits too!
I ended up going with this...
int main() {
char str[] = "Reverse me!";
int length;
for (length = 0; str[length] != '\0'; length++) {
}
printf("length => %d chars\n", length);
int j, k;
char c;
for (j = 0, k = length - 1; j < k; j++, k--) {
c = str[k];
str[k] = str[j];
str[j] = c;
}
printf("reversed => %s\n", str);
return 0;
}
Some things I now know...
There is a strlen() like in PHP. However, it has not been discussed in the book yet, plus I need to get familiar with null terminated strings.
A for loop can assign and do multiple things that are comma separated. I never knew this!
So asking was worthwhile :)
You want to do an in-place reversal. Here's a standard algorithm:
// taken from The C Programming Language
// by Brian Kernighan and Dennis Ritchie (K&R)
void reverse(char s[])
{
int c, i, j;
for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
Note that strlen pretty much replaces your original first loop. It's one of the many standard string manipulation routines available from string.h.
See also
Wikipedia/In-place algorithm
Wikipedia/string.h
Wikipedia/C Standard Library
You can use either of the two methods below, depending on whether you're comfortable with pointers or not. It will also be worthwhile looking at them side-by-side when you satrt learning about pointers so you can better understand how they map to each other.
This is a full program for testing purposes:
#include <stdio.h>
#include <string.h>
// The pointer version.
void reverse1 (char *str) {
char t; // Temporary char for swapping.
char *s = str; // First character of string.
char *e = &(s[strlen(s)-1]); // Last character of string.
// Swap first and last character the move both pointers
// towards each other. Stop when they meet or cross.
while (s < e) {
t = *s;
*s++ = *e;
*e-- = t;
}
}
// The array version.
void reverse2 (char *str) {
char t; // Temporary char for swapping.
int s = 0; // First character of string.
int e = strlen(str)-1; // Last character of string.
// Swap first and last character the move both pointers
// towards each other. Stop when they meet or cross.
while (s < e) {
t = str[s];
str[s++] = str[e];
str[e--] = t;
}
}
int main (void) {
char x[] = "This is a string for reversing.";
printf ("Original: [%s]\n", x);
reverse1 (x);
printf ("Reversed: [%s]\n", x);
reverse2 (x);
printf (" Again: [%s]\n", x);
return 0;
}
and the output is:
Original: [This is a string for reversing.]
Reversed: [.gnisrever rof gnirts a si sihT]
Again: [This is a string for reversing.]
Comments on your code:
void reverse(char str[]) {
int i = 0;
int length;
// Get string length
for (i = 0; str[i] != '\0' ; ++i) {
length = i;
}
Rather than copying the i to length every time you could just wait until the end.
size_t len = 0; // size_t is an unsigned integer that is large enough to hold the sizes
// of the biggest things you can have (32 bits on 32 bit computer,
// 64 bits on a 64 bit computer)
char * s = str;
while (*s) {
len++;
s++;
}
Though the compiler would probably be able to make this optimization for you.
You should know, though, that there is a standard string function strlen ( #include <string.h> )which will measure the length of a char string using the same general algorithm (look for the end) but is usually optimized for the target processor.
len = strlen(str);
Your code again:
char reversed[1000];
Using big arrays are good for learning and simple examples, but you can also allocate memory dynamically based on the size you now know you need. The standard function for doing this is malloc which is in stdlib.h (also in malloc.h). Memory allocated with this function should also be freed, though.
int * p = malloc( 8 * sizeof(int) ); // allocate an array of 8 ints
/* ... work on p ... */
free(p);
/* ... don't access the memory pointed to by p anymore ... */
p = 0;
There are also other functions in the malloc family. There's calloc, which allocates memory and clears sets it to 0. There is also a function called strdup (which isn't in standard C, but is very widely available in string.h) which takes a string and allocates a duplicate of it. It is really just:
char * strdup(const char * str) {
size_t len = strlen(str);
char * s = malloc(len+1);
if (!s) {
return s;
}
return strcpy(s,str); // This could have been memcpy since you know the size
// and memcpy might have been faster on many processors
}
Another useful memory allocation function is alloca (not in the C standard, but widely available and similar functionality is available with variable length arrays in C99). It is great, but works differently from malloc. It allocates memory that is only useable until the current function returns because this memory is allocated in the same way as memory for local variables (from the stack).
More of your code:
int j;
j = 0;
// Reverse it
for (j = 0; j < length ; ++j) {
reversed[j] = str[length - j];
}
The code:
void reverse_in_place(char * str, size_t len) {
size_t i, j;
for (i = 0, j = len - 1; i < j ; i++, j--) {
char a = str[i];
char z = str[j];
str[i] = z;
str[j] = a;
}
}
should swap the order of the string without making a copy of it. For strings with odd length it won't try to swap the middle char with itself.
Reversing a string in C using pointers
#include<stdio.h>
char *srcptr = "Hello!";
char *destptr;
unsigned int length = 0;
void main(void)
{
while(*(ptr++) != '\0')
{
length++;
}
//at the end of while loop, pointer points to end of string
while(length--)
{
*destptr++ = *ptr--;
}
//append null at the end
*destptr = '\0';
printf("%s",ptr);
}

Resources