Help with reversing a string in C - 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);
}

Related

Why is my output different depending on how the variable len is defined?

Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int len = 3; // length of word
char * word = "cat"; // word to be sorted
char sortedWord[len];
int i, j, temp;
// store chars from 'word' to an array 'sortedWord[]'
for (i = 0; i < len; i++) {
sortedWord[i] = *word;
word++;
}
// sort the array using bubble sort
for (i = 0; i < len - 1; i++) {
for (j = 0; j < len - i - 1; j++) {
if (sortedWord[j] > sortedWord[j + 1]) {
temp = sortedWord[j];
sortedWord[j] = sortedWord[j + 1];
sortedWord[j + 1] = temp;
}
}
}
printf("%s\n", sortedWord);
}
The focus of this question is the variable 'len'. If I were to define len to be equal to 3, then the output is as expected (i.e. "act"). However, I want to be able to find the length without explicitly defining it.
I have tried to define len as:
int len = strlen (word);
However, the output is not as expected. It would give me results such as actW?, actX?, and so on.
This same behavior occurs when I try to define len as:
int len;
for (len = 0; *word != '\0'; len++) {
word++;
}
Surprisingly, if I were to print the the variable len right after explicitly defining it, it would also behave the same way.
int len = 3;
printf("Length: %d\n", len); // will cause the output to be different
I am sure that I am missing a fundamental concept, but I am not sure on an approach to resolve this problem. Thanks in advance!
Your storeWord is not null terminated causing undefined behavior, add the null terminator and it will no longer behave erratically.
And also, if you increment the word pointer, it will end up pointing to the null terminator of the original string, so don't do that. Instead, use the index to access elements.
char sortedWord[len + 1]; // One more for the '\0'
int i, j, temp;
// store chars from 'word' to an array 'sortedWord[]'
for (i = 0; i < len; i++) {
sortedWord[i] = word[i];
}
storeWord[len] = '\0';
One more thing, when writing pointers to string literals, use const to prevent accidentally modifiying them, since that is undefined behavior too, so
const char *word = "cat";

Kochan InsertString segmentation fault

I am working through Kochan's programming in C book and I am working on an exercise which requires a function to insert one character string inside another string, with the function call including where the string is to be inserted.
I have written the below code but I receive a segmentation fault whenever I enter the inputs. I think it's because the 'input' string is defined to the length of the user's input and then the insertString function tries to add additional characters to this string. I just can't see a way of defining the string as large enough to be able to take in additional characters. Do you think that this is the reason I am receiving a segmentation fault? Are there any other ways to go about this problem?
#include<stdio.h>
#include <string.h>
insertString(char input[], const char insert[], int position)
{
int i, j;
char temp[81];
j = strlen(input);
for(i = 0; i < position - 1; i++)
{
temp[i] = input[i];
}
for(j = 0; insert != '\0'; i++, j++)
{
temp[i] = insert[j];
}
for(j = i - j; input != '\0'; i++, j++)
{
temp[i] = input[j];
}
for(i = 0; temp[i] != '\0'; i++)
{
input[i] = temp[i];
}
input[i] = '\0';
}
void readLine(char buffer[])
{
char character;
int i = 0;
do
{
character = getchar();
buffer[i] = character;
i++;
}
while(character != '\n');
buffer[i - 1] = '\0';
}
int main(void)
{
char input[81];
char insert[81];
int position;
printf("Enter the first string: ");
readLine(input);
printf("Enter the insert string: ");
readLine(insert);
printf("Enter placement position int: ");
scanf("%i", &position);
insertString(input, insert, position);
printf("The adjusted string is %s\n", input);
return 0;
}
There might be other reasons as well, but the following fragment will crash for sure:
for(j = 0; insert != '\0'; i++, j++)
{
temp[i] = insert[j];
}
The reason is that - since insert will not be increased or manipulated - this is an endless loop writing "indefinitely" long into temp. Once exceeding its length 80 (or a bit later) it will crash. I suppose you meant for(j = 0; insert[j] != '\0'; i++, j++), right?
Check all for loop conditions in insertString function. For example:
for(j = 0; insert != '\0'; i++, j++)
{
temp[i] = insert[j];
}
is infinite loop. Because of it you access memory out of temp array bounds. It causes UB and segmentation fault. Looks like you need insert[j] != '\0' condition here.
I'm familiar with this book. The author, Stephen Kochan, has a website with answers to the odd-numbered end of chapter exercises.
The website is at classroomm.com but you'll need to look around some to find the information.
Here is the info from that site related to this exercise:
Programming in C, exercise 10-7 (3rd edition) and 9-7 (4th edition)
/* insert string s into string source starting at i
This function uses the stringLength function defined
in the chapter.
Note: this function assumes source is big enough
to store the inserted string (dangerous!) */
void insertString (char source[], char s[], int i)
{
int j, lenS, lenSource;
/* first, find out how big the two strings are */
lenSource = stringLength (source);
lenS = stringLength (s);
/* sanity check here -- note that i == lenSource
effectively concatenates s onto the end of source */
if (i > lenSource)
return;
/* now we have to move the characters in source
down from the insertion point to make room for s.
Note that we copy the string starting from the end
to avoid overwriting characters in source.
We also copy the terminating null (j starts at lenS)
as well since the final result must be null-terminated */
for ( j = lenSource; j >= i; --j )
source [lenS + j] = source [j];
/* we've made room, now copy s into source at the
insertion point */
for ( j = 0; j < lenS; ++j )
source [j + i] = s[j];
}
There's an error somewhere in your insertString function where it goes out of bounds. By the way your insertString function doesn't start with the word void.
If I substitute the insertString function which I wrote for the exercise then the program works.
#include<stdio.h>
#include <string.h>
void insertString (char source[], const char s[], int start)
{
int stringLength (const char s[]);
int lenSource = strlen (source);
int lenString = strlen (s);
int i;
if ( start > lenSource ) {
printf ("insertion point exceeds string length\n");
return;
}
// move the characters in the source string which are above the
// starting point (including the terminating null character) to make
// room for the new characters; to avoid overwriting characters the
// process begins at the end of the string
for ( i = lenSource; i >= start; --i )
source[i + lenString] = source[i];
// insert new characters
for ( i = 0; i < lenString; ++i )
source[start + i] = s[i];
}
void readLine(char buffer[])
{
char character;
int i = 0;
do
{
character = getchar();
buffer[i] = character;
i++;
}
while(character != '\n');
buffer[i - 1] = '\0';
}
int main(void)
{
char input[81];
char insert[81];
int position;
printf("Enter the first string: ");
readLine(input);
printf("Enter the insert string: ");
readLine(insert);
printf("Enter placement position int: ");
scanf("%i", &position);
insertString(input, insert, position);
printf("The adjusted string is %s\n", input);
return 0;
}

c language How I free a pointer after I move it

what I wanna to do is get space separated words into a 2d array.what I am thinking is replace space by a '\0',so I can copy the string to array.And then add the string pointer to the place after '\0'.keeping do it till the last.But when I done it ,I consider how can I free the pointer.I consider maybe I can save it first so I use char *t = s.But when I free the t I get a segment error.so how should i free the s pointer after it move to another place.
beside this,I also have some questions:
1.after I malloc a sizeof(char)*15 memory ,I expect printf strlen(s) I can print 15,but I get 0,why?If I wanna know the size of s now,how should I do?
2.after strcpy str to s,the strlen s become 11.But I malloc size of 15,where will the left memory go? does that effect program?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(){
char buf[10][5];
char *str = "10 8 6 9 76";
char *s;
char *t = s;
s = (char *)malloc(sizeof(char)*15);
printf("strlen 1 s:%d\n",strlen(s));
memset(s,0x00,sizeof(char)*15);
printf("strlen 2 s:%d\n",strlen(s));
strcpy(s,str);
int n = strlen(s);
int i = 0;
int j = 0;
int k = 0;
for( i = 0; i < n; i++ ){
if( s[i] == ' '){
s[i] = '\0';
strcpy( buf[k], s);
s += (i+1);
k++;
n = n - i - 1;
i = 0;
}
}
printf("s:%s\n",s);
strcpy( buf[k], s );
for( j = 0; j<= k; j++ ){
printf("buf[%d]:[%s]\n",j,buf[j]);
}
printf("j:%d\n",j);
free(t);
return 0;
}
The main problem is that you are assigning s to t before allocating memory for s:
char *t = s;
and then you try to free t which point to an un-initialised memory.
If you compile your code with -Wall you will get a warning.
You should set t = s after the memory allocation of s.
Also, always check the return value of malloc (there is no need to cast it):
s = malloc(sizeof(char)*15);
if (s == NULL) {
// Error!
return 1
}

Any issues with the following in-place reverse function?

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.

How do I complete K&R Exercise 2-4?

I'm learning how to write programs in C using the k&r book (The C Programming Language) and I have a problem with one of the exercises. It's asking me to detect and remove a character in string s1, which matches any characters in the string s2.
So, say s1 = "A";
And s2 = "AABAACAADAAE"
I want it to return "BCDE"
I know I'm on the right path towards it, i just don't know how to design programs very well, could you give me any additional tips. I tried to read about the binary search tree algorithm, but felt it was a little too advanced for this mundane task.
Thanks everyone!
/* An alternate version of squeeze(s1, s2) that deletes each character in
* s1 that matches any character in the string s2
*
* Angie#odfx.org
*/
#include <stdio.h>
#include <string.h>
void squeeze(char s[], char t[]);
char string[] = "BAD";
char sstring[] = "ABC";
int
main(void)
{
squeeze(string, sstring);
return 0;
}
void
squeeze(char s[], char t[])
{
int i, j, d;
d = 0;
if(strstr(s, t) == NULL)
printf("%c", s[i]);
s[j] = '\0';
}
Great book. If I were you, I would proceed exactly as for the squeeze() in section 2.8, but instead of a direct comparison (s[i] != c) I would write and exploit a function
int contains(char s[], int c)
which returns 1 if the string s contains c, 0 otherwise. Start with the simple approach; when it works you may improve performance with more complex solutions (binary search, but note that the problem doesn't require the characters in s2 to be in a particular order).
A binary search is way overkill for this. You need three indices. One index (i) to walk through s, one index (k) to walk through t, and one index (j) to keep track of where you are in s for the characters that you need to keep because they are not in t. So, for each character in s, check and see if it is in t. If it is not, keep it in s.
void squeeze(char *s, char *t) {
int i, j, k;
int found = 0;
for(i = j = 0; s[i] != '\0'; i++) {
found = 0;
for(k = 0; t[k] != '\0' && (found == 0); k++) {
if(t[k] == s[i]) {
found = 1;
}
}
if(found == 0) {
s[j++] = s[i];
}
}
s[j] = '\0';
}
Here is my very clear and simple answer with some logical explanation.
#include<stdio.h>
void squeeze();
Inside main we type our tested string and wantbedelete string that has the chars that we want to delete from yourstring.
int main()
{
char yourstring[] = "AABAACAADAAE";
char wantbedelete[] = "A";
squeeze(yourstring, wantbedelete);
printf("%s", yourstring);
return 0;
}
The logic inside the squeeze function is as follows,
loop inside the wantbedelete char by char
then, for any char found in wantbedelete we loop through the whole yourstring
then, we compare if the two founded char does not match, we update (save) that char from yourstring to yourstring so basically here just we define a new index(k) to keep track and update the only wanted characters
now the trick here is that we want to reset our index each time we go back again to the outer loop that is looping through each char we intend to delete so that we end up with the only wanted string.
I highly recommend using the debugger and following each line inside the squeeze function so that you would be able to understand the logic more clearly.
void squeeze(char s1[], char s2[])
{
int i, j, k;
k = 0;
for (i = 0 ; s2[i] != '\0' ; i++) /* loop for each char we want to delete with index i */
{
for (j = 0 ; s1[j] != '\0'; j++) /* loop for each char we want to keep with index j */
{
if (s2[i] != s1[j]) /* if the two chars do not match */
{
s1[k++] = s1[j]; /* update s1 with the char that we want to keep using index k */
}
}
s1[k] = '\0'; /* since we update all char that we want to keep, the last char of index k must be empty */
k = 0; /* reset index k so we will be ready for the next char that we want to delete from s1 */
}
}
You don't need a fancy binary search to do the job. What you need is a double for loop that check for occurrence of each char in one string in another, and copy the non-occurring chars into a third char array (which is your result).
Code can be something like the following (not tested!):
char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (s1[i] == s2[j]) {
break;
}
}
if (j == len2) { /* s1[i] is not found in s2 */
*result = s1[i];
result++; /* assuming your result array is long enough */
}
}
void squeeze(char s1[], char s2[])
{
int i,j,k;
char c;
for(i=0;s2[i]!='\0';i++)
{
c=s2[i];
for(j=k=0;s1[j]!='\0';j++)
if(s1[j]!=c)
s1[k++]=s1[j];
s1[k]='\0';
}
}
this is my function:
void squeeze(char s1[],char s2[])
{
int i,j,p;
int found;
p=0;
for(i=0;s1[i]!='\0';i++)
{
for(j=0;s2[j]!='\0';j++)
if(s1[i]==s2[j])
found=YES;
else
found=NO;
if(found==NO)
s1[p++]=s1[i];
}
s1[p]='\0';
}

Resources