I'm trying to solve a piece of code a friend sent me. He's practicing pointers and is trying to reverse a string using them.
It should be a pretty simple task, but there's just a gap in my knowledge here. I've been able to successfully create a for loops which iterates through the string properly using i and j as control variables. I've been able to print out the characters as well, which need to be swapped.
The only problem is that commented line of code in the for loop. It's supposed to be swapping the two values, but is throwing an error, and I don't get why.
#include <stdio.h>
int length(char *p);
void reverse(char *);
int main() {
char *p = "Computer";
reverse(p);
}
int length(char *p) {
int i;
for (i = 0; *(p + i) != '\0'; i++);
return i;
}
void reverse(char *p) {
int l, i;
char t;
for (l = 0; *(p + l) != '\0'; l++);
int len = l;
int temp;
for (i = 0; i < l; i++, l--) {
t = *(p + i);
printf("i-th element - %c\n", t);
printf("j-th element - %c\n", *(p + len - 1 - i));
*(p + i) = *(p + len - 1 - i); // crash
}
puts(p);
}
I'm not even sure it's possible to swap them this way. I would have gone with the t = p[i]; p[i] = p[i + 1]... approach. If anyone can correct and explain this and the pointer related drama, that would be great.
Firstly, you must not try to modify string literals.
You should use an (modifiable) array:
char p[] = "Computer";
instead of
char *p = "Computer";
Secondly, you will need
*(p + len - 1 - i) = t;
after
*(p + i) = *(p + len - 1 - i);
to complete the swapping.
For starters you may not change a string literal
char *p = "Computer";
any attempt to change a string literal result in undefined behavior.
So instead of the pointer to a string literal declare a character array like
char s[] = "Computer";
Moreover your function reverse does not use the function length and in fact does not reverse a string.
This loop
for (i = 0; i < l; i++, l--) {
uses two auxiliary variables as indices.
The function reverse shall output nothing. What it shall do is one thing to reverse the passed string. It is the caller of the function that decides whether to output the reversed string or to use it for other purposes.
The functions length and reverse can be declared and defined using only pointers the following way
size_t length( const char *s )
{
const char *p = s;
while ( *p ) ++p;
return p - s;
}
and
char * reverse( char *s )
{
if ( *s )
{
for ( char *p = s, *q = s + length( s ); p < --q; ++p )
{
char c = *p;
*p = *q;
*q = c;
}
}
return s;
}
The function reverse can be called like
int main( void )
{
char s[] = "Computer";
puts( s );
puts( reverse( s ) );
}
Here is a demonstrative program.
#include <stdio.h>
size_t length( const char *s )
{
const char *p = s;
while ( *p ) ++p;
return p - s;
}
char * reverse( char *s )
{
if ( *s )
{
for ( char *p = s, *q = s + length( s ); p < --q; ++p )
{
char c = *p;
*p = *q;
*q = c;
}
}
return s;
}
int main( void )
{
char s[] = "Computer";
puts( s );
puts( reverse( s ) );
}
Its output is
Computer
retupmoC
As you can see in the both functions neither variable used as an index is present. The functions use only pointers.
Using pointes requires some care otherwise you'll get "Segmentation fault" everywhere. But let's do this. A technique to reverse the string I use, is first position one ponter to the beginning of string, and another pointer to the end of string; and promote the swap while they reach the middle of string (this way I cut the processing to a half) check this code
/* reverses a string
* !!! this function modifies input string !!!
*/
char *reverse(char *s)
{
char *h, *t, c; /* h)ead, t)ail and a temporary c */
int len = 0;
/* positioning the pointers at beginning and end of string s */
len = length(s);
h = (char*)&s[0];
t = (char*)&s[len - 1];
for (int i = 0; i < (len / 2); i++)
{
/* swap chars */
c = *h;
*h = *t;
*t = c;
/* increase h pointer, decrease t pointer */
h++;
t--;
}
return s;
}
Pure pointers... in anyform when swapping the *h and *t you can't get away from a temporary storage ( c );
Be aware the input string needs to be declared as char name[] otherwhise sigsegv follows you!
If you want to save the string for later vice printing it directly to stdout, you can do this:
char * reverse(char * I){
int i,l;
char * O;
l=strlen(I);
O=malloc(l+1);
for(i=1;i<=l;i++)O[i-1]=I[l-i];
O[l]='\0';
return O;
}
int main(){
char * p=reverse("Computer");
printf("%s",p);
free(p);
}
Related
I am using the reverse function.
void reverse(char s[]) {
int i, j;
char c;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
If I pass a string of type char a[] = "abcd" then I get on the output dcba.
But if I pass char *a = "abcd" the I get bus error.
Can I somehow reverse the string exactly of type char *?
Source code:
#include <stdio.h>
#include <string.h>
void reverse(char s[]);
int main() {
char a* = "abcd";
reverse(a);
printf("%s", a);
}
void reverse(char s[]) {
int i, j;
char c;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
There are no strings of type char *. A string is a sequence of characters terminated with the zero-character '\0'.
In this declaration (that has a typo char a* = "abcd";)
char *a = "abcd";
there is declared a pointer to a string literal. The string literal itself has the type char[5]. But used as an initializer expression it is implicitly converted to a pointer to its first element of the type char *.
And you are trying to change the string literal pointed to by the pointer within the function reverse.
However you may not change a string literal. Any attempt to change a string literal results in undefined behavior.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
What you could do is to create a new string that will store characters of the source string literal in the reverse order.
For example
char * reverse_copy( const char *s )
{
size_t n = strlen( s );
char *t = malloc( n + 1 );
if ( t != NULL )
{
t += n;
*t = '\0';
while ( *s ) *--t = *s++;
}
return t;
}
And the function can be called like
char *s = "abcd";
char *p = reverse_copy( s );
if ( p != NULL ) puts( p );
free( p );
Here is a demonstration program.
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *reverse_copy( const char *s )
{
size_t n = strlen( s );
char *t = malloc( n + 1 );
if (t != NULL)
{
t += n;
*t = '\0';
while (*s) *--t = *s++;
}
return t;
}
int main( void )
{
char *s = "abcd";
char *p = reverse_copy( s );
if (p != NULL) puts( p );
free( p );
}
The program output is
dcba
Your reverse function is ok. The problem is that you instantiated you string as a literal by doing so:
char *a = "abcd"; (it's '*a' not 'a*' btw)
You cannot modify string literals.
To test your function you could do so:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void reverse(char s[]);
int main(){
char *a = malloc(sizeof(char) * 5);
if (a == NULL)
return 1;
a[0] = 'a';
a[1] = 'b';
a[2] = 'c';
a[3] = 'd';
a[4] = '\0';
reverse(a);
printf("%s", a);
free(a);
}
void reverse(char s[]){
int i, j;
char c;
for (i = 0, j = strlen(s) - 1; i<j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
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.:)
#include <stdio.h>
#include <string.h>
//reversal function
void reverseString(char* str)
{
int l, i;
char *begin_ptr, *end_ptr, ch;
l = strlen(str);
begin_ptr = str;
end_ptr = str;
//move the ptr to the final pos
for (i = 0; i < l - 1; i++)
end_ptr++;
//pointer swaping
for (i = 0; i < l / 2; i++)
{
ch = *end_ptr;
*end_ptr = *begin_ptr;
*begin_ptr = ch;
begin_ptr++;
end_ptr--;
}
}
// Driver code
---------------------------------main---------------------------------------------------------------------------------------------------
the function call sends the address of the first string in the array
int main()
{
char *str[ ] = {"To err is human...","But to really mess things up...","One needs to know C!!"};
for(int i=0;i<3;i++)
{
reverseString(str[i]); //funtion call
printf("Reverse of the string: %s\n", str[i]);
}
return 0;
}
You may not modify a string literal. Any attempt to modify a string literal results in undefined behavior.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
You should declare a to-dimensional array like
enum { N = 32 };
char str[][N] =
{
"To err is human...",
"But to really mess things up...",
"One needs to know C!!"
};
Pay attention to that the function revreseString is too complicated. Also it is better when the function returns pointer to the reversed string. The function can be defined the following way using pointers
char * reverseString( char *s )
{
if ( *s )
{
for ( char *p = s, *q = s + strlen( s ); p < --q; ++p )
{
char c = *p;
*p = *q;
*q = c;
}
}
return s;
}
Here is a demonstrative program
#include <stdio.h>
#include <string.h>
char * reverseString( char *s )
{
if ( *s )
{
for ( char *p = s, *q = s + strlen( s ); p < --q; ++p )
{
char c = *p;
*p = *q;
*q = c;
}
}
return s;
}
int main(void)
{
enum { N = 32 };
char s[][N] =
{
"To err is human...",
"But to really mess things up...",
"One needs to know C!!"
};
for ( size_t i = 0; i < sizeof( s ) / sizeof( *s ); i++ )
{
puts( reverseString( s[i] ) );
}
return 0;
}
The program output is
...namuh si rre oT
...pu sgniht ssem yllaer ot tuB
!!C wonk ot sdeen enO
I want to add string elements successively, for example st[]="morty", and I want to repeat its elements for example seven times. It should be st[]="mortymo". I wrote a function which is at below. (The length function is strlen).
void repeat(char* st,int n){
int i,k=0,l=length(st);
char* ptr;
ptr=(char*)malloc((n+1)*sizeof(char));
for (i=0;i<n;i++){
*(ptr+i)=*(st+k);
k++;
if(k==l)k=0;
}
}
The program below repeats characters from the original string.
Comments in the code:
#include<stdio.h>
#include<stdlib.h>
char* repeat(const char* st, size_t n){
// use `const` to note that pointer `st` will not be modified
// for purity you may want to use type `size_t` since returning type of strlen is `size_t`
size_t i, k=0;
size_t l = strlen(st);
// do not use (char *) cast
char* ptr = malloc((n+1)*sizeof(char)); // allocate enough room for characters + NULL
for (i=0; i< n; i++)
{
ptr[i] = st[k]; // use index for readability
k++;
if (k == l)
k=0;
}
ptr[i] = 0; // terminate the string
return ptr;
}
int main( )
{
char *str = "12345";
str = repeat(str, 15);
printf("%s\n",str);
free (str); // free the allocated memory inside the repeat function
return 0;
}
OUTPUT:
123451234512345
In your repeat function, you allocated ptr to hold the repeated string, but you didn't return or assign it to st. You can modify your repeat function as follows:
char* repeat(char* st,int n){
int i,k=0,l=strlen(st);
char* ptr;
ptr=(char*)malloc((n+1)*sizeof(char));
for (i=0;i<n;i++){
*(ptr+i)=*(st+k);
k++;
if(k==l)k=0;
}
*(ptr+n) = '\0';
return ptr;
}
/* some code*/
char *st = "morty";
st = repeat(st, 7);
Such that you are storing the result of the repeated string in st after.
If I have understood the assignment correctly then you need a function like that one shown in the demonstrative program.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * repeat( const char *s, size_t n )
{
char *p = NULL;
size_t len = strlen( s );
if ( len == 0 ) n = 0;
p = ( char * )malloc( n + 1 );
if ( p )
{
size_t i = 0;
for ( size_t j = 0; i < n; i++ )
{
p[i] = s[j];
if ( ++j == len ) j = 0;
}
p[i] = '\0';
}
return p;
}
int main(void)
{
char *s = "Hi, Zusaetlich.";
char *p = repeat( s, 2 * strlen( s ) );
puts( p );
free( p );
return 0;
}
The program output is
Hi, Zusaetlich.Hi, Zusaetlich.
Pay attention to that the function is designed such a way that if the original string is empty then the resulted string is also empty because there is nothing to repeat.
As for your function then it has at least a memory leak because the memory allocated in the function is not freed.
Also as the original string is not changed then the corresponding parameter should be qualified with the const specifier. And the second parameter should have type size_t because at least the function strlen has the return type size_t.
So the function should be declared as it is shown in the demonstrative program.
Since you don't intend to modify the contents of st, go ahead and declare it as const. Since you intend to allocate a new string in your function, you should return it to the caller.
char *repeat(const char* st,int n){
k is unnecessary for your problem. Call the standard functions.
int i,l=strlen(st);
char* ptr;
Don't cast the result of malloc, as this can mask a fatal error in C. sizeof(char) is always 1. Check the result of the malloc call for success.
ptr=malloc(n+1);
if (ptr == NULL) return NULL;
for (i=0;i<n;i++){
Access arrays idiomatically with []. Note that k increments whenever i does, but you are applying a modulo operation of k. However, C has a modulo operator, which you can use directly on i.
ptr[i]=st[i%l];
}
Make sure the new string is NUL terminated. Your function is declared to return a result, but your implementation fails to do so.
ptr[n] = '\0';
return ptr;
}
C has many functions you can call to do the copying for you rather than the byte by byte loop you have written. There is simplicity in your implementation, but below is an alternative, that also includes additional error checking that is lacking in your solution.
(Some may balk at the use of sprintf, but it is being used correctly.)
char *
repeat (const char *st, int n) {
int l = st ? strlen(st) : 0;
char *ret = (st && n > 0 ? malloc(n+1) : 0), *p = ret;
while (ret && n > 0) {
p += sprintf(p, "%.*s", (l < n ? l : n), st);
n -= l;
}
return ret ? ret : "(nil)";
}
Try it online!
I am new with pointers on C and I am trying to write a function like strcat() but without using it. I developed the following function:
char cat(char *a, char *b) {
int i=0,cont=0,h=strlen(a)+strlen(b);
char c[h]; //new string containing the 2 strings (a and b)
for(i;i<strlen(a);++i) {
c[i] = *(a+i); //now c contains a
}
int j = i;
for(j;j<strlen(b);++j) {
c[j] = *(b+cont); //now c contains a + b
cont++;
}
return c; // I return c
}
And this is how I call the function:
printf("\Concatenazione: %c", cat(A,B));
It is now working because the final result is a weird character. How could I fix the function? Here there's the full main.
char * strcat(char *dest, const char *src)
{
int i;
int j;
for (i = 0; dest[i] != '\0'; i++);
for (j = 0; src[j] != '\0'; j++) {
dest[i+j] = src[j];
}
dest[i+j] = '\0';
return dest;
}
From your implementation it appears that your version of strcat is not compatible with the standard one, because you are looking to allocate memory for the result, rather than expecting the caller to provide you with enough memory to fit the result of concatenation.
There are several issues with your code:
You need to return char*, not char
You need to allocate memory dynamically with malloc; you cannot return a locally allocated array.
You need to add 1 for the null terminator
You need to write the null terminator into the result
You can take both parameters as const char*
You can simplify your function by using pointers instead of indexes, but that part is optional.
Here is how you can do the fixes:
char *cat(const char *a, const char *b) {
int i=0,cont=0,h=strlen(a)+strlen(b);
char *c = malloc(h+1);
// your implementation goes here
c[cont] = '\0';
return c;
}
You are returning a POINTER to the string, not the actual string itself. You need to change the return type to something like "char *" (or something equivalent). You also need to make sure to null terminate the string (append a '\0') for it to print correctly.
Taking my own advice (and also finding the other bug, which is the fact that the second for loop isn't looping over the correct indices), you end up with the following program:
#include <stdio.h>
char *cat(char *a, char *b) {
int i = 0, j = 0;
int cont = 0;
int h = strlen(a) + strlen(b) + 1;
char *result = (char*)malloc(h * sizeof(char));
for(i = 0; i < strlen(a); i++) {
result[i] = a[i];
}
for(j = i; j < strlen(b)+ strlen(a); j++) {
result[j] = b[cont++];
}
// append null character
result[h - 1] = '\0';
return result;
}
int main() {
const char *firstString = "Test First String. ";
const char *secondString = "Another String Here.";
char *combined = cat(firstString, secondString);
printf("%s", combined);
free(combined);
return 0;
}
c is a local variable. It only exists inside the function cat. You should use malloc.
instead of
char c[h];
use
char *c = malloc(h);
Also, you should add the null byte at the end. Remember, the strings in C are null-ended.
h = strlen(a) + strlen(b) + 1;
and at the end:
c[h - 1] = '\0';
The signature of cat should be char *cat(char *a, char *b);
You will get an error of
expected constant expression
for the code line char c[h];. Instead you should be using malloc to allocate any dynamic memory at run-time like::
char* c ;
c = malloc( h + 1 ) ; // +1 for the terminating null char
// do stuff
free( c ) ;
Your corrected code::
#include<stdio.h>
#include<conio.h>
#include<string.h>
#include <stdlib.h>
char* cat(char *a, char *b) {
int i=0,cont=0,h=strlen(a)+strlen(b), j;
char *c;
c = malloc( h+1 ) ;
for(i;i<strlen(a);++i) {
c[i] = *(a+i);
}
j = 0 ;
for(j;j<strlen(b);++j) {
c[i] = *(b+cont);
i++ ;
cont++;
}
c[i] = 0 ;
return c;
}
int main() {
char A[1000],B[1000];
char * a ;
printf("Inserisci la stringa 1: \n");
gets(A);
printf("Inserisci la stringa 2: \n");
gets(B);
a = cat(A,B) ;
printf("\nConcatenazione: %s", a);
free(a) ;
getch();
return 0;
}