Ive been trying to reverse a string as simply as possible , trying to prove a point to myself but for some reason the code is not working. I know i can easily find a different approach online but then i wont learn anything. So would anyone explain please?
#include <stdio.h>
#include <stdlib.h>
int main()
{ int i,n,c=0;
char s[50];
char a[50];
for(i = 0; i < 50;i++)
scanf("%[^\n]c",&s[i]);
for(n = strlen(s), i = 0; n > i; n--,c++)
s[c] = s[n-1];
printf("%s", s);
return 0;
}
For starters you need to include the header <string.h>.
This loop
for(i = 0; i < 50;i++)
scanf("%[^\n]c",&s[i]);
does not make a great sense. Moreover you need to append the entered string with the terminating zero character '\0'.
What you need is to enter a string one time as for example
scanf("%49s", s );
Or even better to write
scanf( "%49[^\n]", s );
to enter a string with several words in the array.
This for loop
for(n = strlen(s), i = 0; n > i; n--,c++)
s[c] = s[n-1];
also does not make a sense. It does not reverse the string. The variable i is not increased. That is you need to swap two characters.
Also you need to declare variables in minimum scopes where they are used.
The loop can look for example the following way
for ( size_t i = 0, n = strlen(s); i < n / 2; i++ )
{
char c = s[i];
s[i] = s[n-1-i];
s[n-1-i] = c;
}
Apart from all these the declared array a is not used in the program.
So the program can look the following way
#include <stdio.h>
#include <string.h>
int main( void )
{
char s[50] = "";
scanf( "%49[^\n]", s );
for ( size_t i = 0, n = strlen(s); i < n / 2; i++ )
{
char c = s[i];
s[i] = s[n-1-i];
s[n-1-i] = c;
}
puts( s );
}
If to enter string
Hello, unikatura!
then the program output will be
!arutakinu ,olleH
Use functions.
Two variants:
char *reverse(char *str)
{
char *end = str, *start = str;
if(str && *str)
{
for(;*(end + 1);end++);
for(;end > start;)
{
char tmp = *end;
*end-- = *start;
*start++ = tmp;
}
}
return str;
}
char *reverse1(char *dest, const char *str)
{
char *wrk = dest;
size_t len = 0;
if(str)
{
for(;*str;len++, str++);str -= !!*str;
for(;len--; *wrk++ = *str--);
}
*wrk = 0;
return dest;
}
Related
I am trying to reverse this C-string and I thought I did it correct but the string remains the same when it passes through the function.
#include <stdio.h>
char* reverse(char* string);
int main(int arc, char* argv[]) {
char word[] = "Hello World!";
printf("%s\n", word);
printf("%s\n", reverse(word));
return 0;
}
char* reverse(char* string) {
int i, j, n = 0;int len = 0;char temp;
//Gets string length
for (i = 0; *(string + i) != '0'; i++) {
len++;
}
//Reverses string
for (j = len - 1; j >= 0; j--) {
temp = string[n];
string[n] = string[j];
string[j] = temp;
n++;
}
return &string[0];
}
Expected output:
Hello World!
!dlroW olleH
For starters there is a typo
for (i = 0; *(string + i) != '0'; i++) {
You have to write
for (i = 0; *(string + i) != '\0'; i++) {
That is instead of the character '0' you have to use '\0' or 0.
In this for loop
for (j = len - 1; j >= 0; j--) {
temp = string[n];
string[n] = string[j];
string[j] = temp;
n++;
}
the string is reversed twice.:) As a result you get the same string.
The function can look for example the following way
char * reverse(char *string)
{
//Gets string length
size_t n = 0;
while ( string[n] != '\0' ) ++n;
//Reverses string
for ( size_t i = 0, m = n / 2; i < m; i++ )
{
char temp = string[i];
string[i] = string[n - i - 1];
string[n - i - 1] = temp;
}
return string;
}
Or the function can be defined the following way using pointers
char * reverse(char *string)
{
//Gets string length
char *right = string;
while ( *right ) ++right;
//Reverses string
if ( right != string )
{
for ( char *left = string; left < --right; ++left )
{
char temp = *left;
*left = *right;
*right = temp;
}
}
return string;
}
The same approach of the function implementation without using pointers can look the following way
char * reverse(char *string)
{
//Gets string length
size_t n = 0;
while ( string[n] != '\0' ) ++n;
//Reverses string
if ( n != 0 )
{
for ( size_t i = 0; i < --n; ++i )
{
char temp = string[i];
string[i] = string[n];
string[n] = temp;
}
}
return string;
}
Here is one more solution. I like it most of all. Tough it is inefficient but it is not trivial as the early presented solutions. It is based on an attempt of one beginner to write a function that reverses a string.:)
#include <stdio.h>
#include <string.h>
char *reverse( char *string )
{
size_t n = 0;
while (string[n]) ++n;
while (!( n < 2 ))
{
char c = string[0];
memmove( string, string + 1, --n );
string[n] = c;
}
return string;
}
int main( void )
{
char string[] = "Hello World!";
puts( string );
puts( reverse( string ) );
}
The program output is
Hello World!
!dlroW olleH
Of course instead of manually calculating the length of a string in all the presented solutions there could be used standard string function strlen declared in the header <string.h>.
The problem is that the input word[] is an array, which decays to a pointer when passed to the reverse function.
In the for loop, instead of using n to keep track of the position, I suggest you to use i and j to keep track of the start and end of the string, and increment and decrement them respectively and use strlen to get the length of string.
Also, as it is mentionned above by #Vlad from Moscow, in your for loop you are checking for 0 but it should be \0 which is the null character.
Please find down below an update of your posted code that is generating the expected result :
#include <stdio.h>
char* reverse(char* string);
int main(int arc, char* argv[]) {
char word[] = "Hello World!";
printf("%s ", word);
printf("%s\n", reverse(word));
return 0;
}
char* reverse(char* string) {
int i, j;
char temp;
int len = strlen(string);
//Reverses string
for (i = 0, j = len - 1; i < j; i++, j--) {
temp = string[i];
string[i] = string[j];
string[j] = temp;
}
return &string[0];
}
The output is as expected: Hello World! !dlroW olleH
Aditionnally, you can include the header <string.h> or explicitly
provide a declaration for 'strlen' to avoid the warning that indicate to implicitly declaring library function 'strlen' with type 'unsigned long (const char *)' [-Wimplicit-function-declaration]
This question is mainly based on my past question: to solve this exercise, I needed to ask a standalone question; here's the link: " little question about a thing when it comes to dynamically allocate a string, how can I solve? ". (I said it, because problems are in the heap).
this is the exercise:
write a function that find the longest word in a string, and return another string (dynamically allocated in the heap). (word is defined as: sequence of alphanumeric characters without whitespaces).
this is my code:
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
char* longest_word(const char* sz) {
size_t length = 0;
for (size_t i = 0; sz[i] != 0; i++) {
if (isspace(sz[i])) {
length = 0;
}
else {
length++;
}
}
size_t sum = length + 1;
char* str = malloc(sum);
if (str == NULL) {
return NULL;
}
size_t stringlength = strlen(sz);
size_t sl = stringlength - (sum - 1);
for (size_t i = sl; sz[i] != 0; i++) {
str[i] = sz[i];
}
str[sum - 1] = 0;
return str;
}
int main(void) {
char sz[] = "widdewdw ededudeide sjfsdhiuodsfhuiodfihuodsfihuodsihuodsihuosdihuquesto";
char* str;
str = longest_word(sz);
free(str);
return 0;
}
the final string is the following: "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍsjfsdhiuodsfhuiodfihuodsfihuodsi".
this is a good sign, because that means that my thinking process was right (although not entirely).
here's a detailed explanation:
find the length of the longest string, if the current character is a whitespace start counting from zero again. this works.
allocate enough space to store each character, plus the zero-terminator. (I've used size_t, because of the accepted answer of the linked question).
here's the critical part: "sz[i]" is the i-th position in the original string (i.e "sz"). I start counting from sz[i].
I have copied each character into str[i] until zero-terminator is reached.
at the end, placed 0 in str[sum-1], (not str[sum], because I've done it and it turned out to be a buffer overflow).
The funtion is incorrect.
This for loop
size_t length = 0;
for (size_t i = 0; sz[i] != 0; i++) {
if (isspace(sz[i])) {
length = 0;
}
else {
length++;
}
}
does not find the maximum length of words in the string. It returns just the last calculated value of the variable length. For example if the string is ended with a space then the value of length after the loop will be equal to 0.
And this for loop
for (size_t i = sl; sz[i] != 0; i++) {
str[i] = sz[i];
}
is trying to copy the tail of the string but not the word with the maximum length.
The function can be defined the following way as it is shown in the demonstration program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * longest_word( const char *s )
{
const char *delim = " \t";
size_t max_n = 0;
const char *max_word = s;
for ( const char *p = s; *p; )
{
p += strspn( p, delim );
if ( *p )
{
const char *q = p;
p += strcspn( p, delim );
size_t n = p - q;
if ( max_n < n )
{
max_n = n;
max_word = q;
}
}
}
char *result = malloc( max_n + 1 );
if ( result != NULL )
{
result[max_n] = '\0';
memcpy( result, max_word, max_n );
}
return result;
}
int main( void )
{
const char *s = "Find the longest word";
char *p = longest_word( s );
if ( p ) puts( p );
free( p );
}
The program output is
longest
Trying to write a C program to reverse the given string (using Pointer) and here is the code.
[sample.c]
#include <stdio.h>
#include <stdlib.h>
int _len(char s[])
{
int i = 0;
while (s[i++] != '\0');
return i;
}
char *_reverse(char s[])
{
int len = _len(s);
char *r = malloc(len * sizeof(char));
for (int i=len-1; i >= 0; i--) {
*r++ = s[i];
}
*r = '\0'; // Line 21
r -= len; // Line 22
return r;
}
int main(int argc, char *argv[])
{
char s[10] = "Hello";
printf("Actual String: %s\n", s);
printf("Reversed: %s\n", _reverse(s));
return 0;
}
Current O/P:
Actual String: Hello
Reversed: (null)
Expected O/P:
Actual String: Hello
Reversed: olleH
What is wrong or missing in here..? Please correct me. Thanks in advance.
You are modifying the pointer "r" of your newly allocated memory. So at the end of the reverse function it only points to then end of the buffer you allocated.
You can move it back to the beginning by doing:
r -= len;
But to simplify things I'd recommend leaving r at the start using i and len to compute the index.
Also, you don't terminate the reversed string with a '\0'.
You increase r in the loop, then return it. Obviously, it points to an address after the actual reversed string. Copy r to another variable after malloc and return that.
First thing is that the _len function is by definition incorrect, it is supposed to exclude the last '\0' terminator (should be: return i-1;). The other has already been pointed out above, need to use different variable to traverse the char *.
#include <stdio.h>
#include <stdlib.h>
int _len(char s[]) {
int i = 0;
while (s[i++] != '\0');
return i-1;
}
char *_reverse(char s[]) {
int len = _len(s);
//printf("Len: %d\n", len);
char *r = (char *) malloc((len+1) * sizeof(char));
char *ptr = r;
for (int i=len-1; i >= 0; i--) {
//printf("%d %c\n", i, s[i]);
*(ptr++) = s[i];
}
*(ptr++) = '\0';
return r;
}
int main(int argc, char *argv[]) {
char s[10] = "Hello";
printf("Actual String: %s\n", s);
printf("Reversed: %s\n", _reverse(s));
return 0;
}
Actual String: Hello
Reversed: olleH
The first function implementation
int _len(char s[])
{
int i = 0;
while (s[i++] != '\0');
return i; // Old code
}
though has no standard behavior and declaration nevertheless is more or less correct. Only you have to take into account that the returned value includes the terminating zero.
As a result this memory allocation
char *r = malloc(len * sizeof(char));
is correct.
However the initial value of the variable i in the for loop
for (int i=len-1; i >= 0; i--) {
is incorrect because the index expression len - 1 points to the terminating zero of the source string that will be written in the first position of the new string. As a result the new array will contain an empty string.
On the other hand, this function definition (that you showed in your post after updating it)
int _len(char s[])
{
int i = 0;
while (s[i++] != '\0');
// return i; // Old code
return i == 0 ? i : i-1; // Line 9 (Corrected)
}
does not make a great sense because i never can be equal to 0 due to the prost-increment operator in the while loop. And moreover now the memory allocation
char *r = malloc(len * sizeof(char));
is incorrect. There is no space for the terminating zero character '\0'.
Also it is a bad idea to prefix identifiers with an underscore. Such names can be reserved by the system.
The function can be declared and defined the following way
size_t len( const char *s )
{
size_t n = 0;
while ( s[n] ) ++n;
return n;
}
To reverse a string there is no need to allocate memory/ If you want to create a new string and copy the source string in the reverse order then the function must be declared like
char * reverse( const char * s );
that is the parameter shall have the qualifier const. Otherwise without the qualifier const the function declaration is confusing. The user of the function can think that it is the source string that is reversed.
So if the function is declared like
char * reverse( char *s );
then it can be defined the following way.
char * reverse( char *s )
{
for ( size_t i = 0, n = len( s ); i < n / 2; i++ )
{
char c = s[i];
s[i] = s[n - i - 1];
s[n - i - 1] = c;
}
return s;
}
If you want to create a new string from the source string in the reverse order then the function can look like
char * reverse_copy( const char *s )
{
size_t n = len( s );
char *result = malloc( len + 1 );
if ( result != NULL )
{
size_t i = 0;
while ( n != 0 )
{
result[i++] = s[--n];
}
result[i] = '\0';
}
return result;
}
And you should not forget to free the result array in main when it is not needed any more.
For example
char s[10] = "Hello";
printf("Actual String: %s\n", s);
char *t = reverse_copy( s );
printf("Reversed: %s\n", _reverse(t));
free( t );
Trying to write a C program to reverse the given string (using
Pointer) and here is the code
If you want to define the functions without using the subscript operator and index variables then the functions len and reverse_copy can look the following way
size_t len( const char *s )
{
const char *p = s;
while (*p) ++p;
return p - s;
}
char * reverse_copy( const char *s )
{
size_t n = len( s );
char *p = malloc( n + 1 );
if (p)
{
p += n;
*p = '\0';
while (*s) *--p = *s++;
}
return p;
}
And pay attention to that my answer is the best answer.:)
I made code which will for string "aabbcc" return "abc" but in cases when there is more letters like "aaa" it will return "aa" instead of just one.
Here is the code I made.
void Ponavljanje(char *s, char *p) {
int i, j = 0, k = 0, br = 0, m = 0;
for (i = 0; i < strlen(s) - 1; i++) {
for (j = i + 1; j < strlen(s); j++) {
if (s[i] == s[j]) {
br++;
if (br == 1) {
p[k++] = s[i];
}
}
}
br = 0;
}
p[k] = '\0';
puts(p);
}
For "112233" output should be "123" or for "11122333" it should be also "123".
Avoid repeated calls to strlen(s). A weak compiler may not see that s is unchanged and call strlen(s) many times, each call insuring a cost of n operations - quite inefficient. #arkku.1 Instead simply stop iterating when the null character detected.
Initialize a boolean list of flags for all char to false. When a character occurs, set the flag to prevent subsequent usage. Be careful when indexing that list as char can be negative.
Using a const char *s allows for wider allocation and helps a compiler optimization.
Example:
#include <stdbool.h>
#include <limits.h>
void Ponavljanje(const char *s, char *p) {
const char *p_original = p;
bool occurred[CHAR_MAX - CHAR_MIN + 1] = { 0 }; // all values set to 0 (false)
while (*s) {
if (!occurred[*s - CHAR_MIN]) {
occurred[*s - CHAR_MIN] = true;
*p++ = *s;
}
s++;
}
*p = '\0';
puts(p_original);
}
1 #wrongway4you comments that many compilers may assume the string did not change and optimize out the repeated strlen() call. A compliant compiler cannot do that though without restrict unless it is known that in all calls, s and p do not overlap. A compiler otherwise needs to assume p may affect s and warrant a repeated strlen() call.
does the work with a complexity O(n)
I suppose programming can give rmg
void Ponavljanje(char *s,char *p)
{
char n[256] = {0};
int i = 0;
while (*s) {
switch (n[(unsigned char) *s]) {
case 0:
n[(unsigned char) *s] = 1;
break;
case 1:
p[i++] = *s;
n[(unsigned char) *s] = 2;
}
s += 1;
}
p[i] = 0;
puts(p);
}
While the inner loop checks br to only copy the output on the first repetition, the outer loop still passes over each repetition in s on future iterations. Hence each further occurrence of the same character will run a separate inner loop after br has already been reset.
With aaa as the input, both the first and the second a cause the inner loop to find a repetition, giving you aa. In fact, you always get one occurrence fewer of each character in the output than there is in the input, which means it only works for 1 or 2 occurrences in the input (resulting in 0 and 1 occurrences, respectively, in the output).
If you only want to remove the successive double letters, then this function would be sufficient, and the examples given in the question would fit:
#include <stdio.h>
void Ponavljanje(char *s,char *p)
{
char dd = '\0';
char *r;
if(s == NULL || p == NULL)
return;
r = p;
while(*s){
if(*s != dd){
*r = *s;
dd = *s;
r++;
}
s++;
}
*r = '\0';
puts(p);
}
int main(void)
{
char s[20] = "1111332222";
char p[20];
Ponavljanje(s,p);
}
Here is something that works regardless of order:
#include <stdio.h>
#include <string.h>
void
repeat(char *s, char *p)
{
int slen;
int sidx;
int pidx;
int plen;
int schr;
slen = strlen(s);
plen = 0;
for (sidx = 0; sidx < slen; ++sidx) {
schr = s[sidx];
// look for duplicate char
int dupflg = 0;
for (pidx = 0; pidx < plen; ++pidx) {
if (p[pidx] == schr) {
dupflg = 1;
break;
}
}
// skip duplicate chars
if (dupflg)
continue;
p[plen++] = schr;
}
p[plen] = 0;
puts(p);
}
int
main(void)
{
char p[100];
repeat("112233",p);
repeat("123123",p);
return 0;
}
Note: As others have mentioned, strlen should not be placed in the loop condition clause of the for [because the length of s is invariant]. Save strlen(s) to a separate variable and loop to that limit
Here is a different/faster version that uses a histogram so that only a single loop is required:
#include <stdio.h>
#include <string.h>
void
repeat(char *s, char *p)
{
char dups[256] = { 0 };
int slen;
int sidx;
int pidx;
int plen;
int schr;
slen = strlen(s);
sidx = 0;
plen = 0;
for (sidx = 0; sidx < slen; ++sidx) {
schr = s[sidx] & 0xFF;
// look for duplicate char
if (dups[schr])
continue;
dups[schr] = 1;
p[plen++] = schr;
}
p[plen] = 0;
puts(p);
}
int
main(void)
{
char p[100];
repeat("112233",p);
repeat("123123",p);
return 0;
}
UPDATE #2:
I would suggest iterating until the terminating NUL byte
Okay, here's a full pointer version that is as fast as I know how to make it:
#include <stdio.h>
#include <string.h>
void
repeat(char *s, char *p)
{
char dups[256] = { 0 };
char *pp;
int schr;
pp = p;
for (schr = *s++; schr != 0; schr = *s++) {
schr &= 0xFF;
// look for duplicate char
if (dups[schr])
continue;
dups[schr] = 1;
*pp++ = schr;
}
*pp = 0;
puts(p);
}
int
main(void)
{
char p[100];
repeat("112233",p);
repeat("123123",p);
return 0;
}
This is a very small question, and probably something really silly! But why am I getting garbage returned in my output for this function which should remove double letters?
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char *makehello( char *s ) {
char new[16] ;
int i ;
int c = strlen(s);
for ( i = 0; i < (c + 1); i++)
if (toupper(s[i]) != toupper(s[i+1]))
new[i] = toupper(s[i]);
return strdup( new ) ;
}
int main(void) {
char *new;
char data[100];
scanf("%s", data);
new = makehello(data);
printf("%s", new);
return 0;
}
You need a separate count for your 'new' array. You're storing them at index 'i' (where you found the character), but what you really want is to store them from position 0 and increment this count instead.
EDIT: Of course this isn't a fullproof method.
i.e something like this:
for ( i = 0; i < c; i++)
{
if (toupper(s[i]) != toupper(s[i+1]))
{
new[count++]= toupper(s[i]);
}
}
new[count] = '\0';
The line
for ( i = 0; i < (c + 1); i++)
should be
for ( i = 0; i < (c - 1); i++)
And you then need before the strdup new[i]=0;
Braces would not go amise either.
EDIT
Forgot need to change the following
int i, j=0;
and in the for loop
new[j++] = toupper(s[i]);
and after the for loop
new[j] = 0;
Here's a reasonably compact C99 version of the algorithm (headers omitted, example):
const char * makehello (const char * s)
{
char new[16] = { *s, 0 };
const char * p = s;
char c = *s, * q = new;
while (*p) { if (*++p != c) { c = *++q = *p; } }
return strdup(new) ;
}
int main(void)
{
char data[100];
scanf("%s", data);
printf("%s", makehello(data));
return 0;
}
(This one discriminates case.)