Yesterday I had to solve an exam exercise, which, unfortunately, I failed..
The exercise was to create a function in C with the following rules:
Write a function that takes a string and displays the string in reverse
order followed by the newline.
Its prototype is constructed like this : char *ft_rev_print (char *str)
It must return its argument
Only allowed to use function 'write'(so no printf or others)
With that information I wrote :
int ft_strlen(char *str) /*to count the length of the original string*/
{
int i;
i = 0;
while (str[i])
i++;
return (i);
}
char *ft_rev_print (char *str)
{
int i;
i = ft_strlen(str);
while (i)
{
write (1, (str +1), 1);
i--;
}
return (str); /*returning its argument */
}
int main(void) /*IT HAD TO WORK WITH THIS MAIN, DID NOT WROTE THIS MYSELF!*/
{
ft_rev_print("rainbow dash");
write(1, "\n", 1);
return (0);
}
I tried for ages to get it to work, but failed.. So now I'm breaking my head over this. What did i do wrong ? What did i miss?
Thanks in advance !
Your while loop is wrong, you start with i=0 and iterate while it's not zero, so no iterations will be done.
What you should do is:
initialize i so that it is the index of the last character
loop as long as it's a valid index
print the i-th character, not always the second (at index one)
char *ft_rev_print (char *str)
{
int i;
i = ft_strlen(str) - 1; // <-- Initialize i to be the index of the last character
while (i >= 0) // <-- Loop as long as it's valid
{
write (1, (str+i), 1); // <-- Print the i-th character
i--;
}
return (str);
}
For starters your teachers are not enough qualified. The functiuon should be declared at least like
char * ft_rev_print( const char *str );
^^^^^
because the passed string is not changed within the function.
You forgot to call the function ft_strlen.
It seems you mean
i = ft_strlen( str );
As a result this loop
i = 0;
while (i)
{
//...
}
is never executed because initially i is equal to 0 and the condition of the loop at once evaluates to false.
Also in this call
write (1, (str +1), 1);
^^^^^^^^
you are always trying to output the second symbol of the string.
Also the output of the new line character '\n' should be within the function according to its description.
The function can look the following way as it is shown in the demonstrative program below (instead of the non-standard function write I will use the function putchar but you can substitute it for write yourself)
#include <stdio.h>
char * ft_rev_print( const char *str )
{
const char *p = str;
while ( *p ) ++p;
while ( p != str ) putchar( *--p ); // substitute the call of putchar for write( 1, *--p, 1 )
putchar( '\n' ); // substitute the call of putchar for write( 1, "\n", 1 )
return ( char * )str;
}
int main(void)
{
ft_rev_print( "rainbow dash" );
return 0;
}
The program output is
hsad wobniar
Hey I have tried your question and there is a small point I would like to add,In your question you have written the length of your string and also the part below in your code:
write (1, (str +1), 1);
was not correct so I corrected it and in that basically we are adding the back of previous characters like this and error was in a while loop condition:
write (1,(str+i),1);
You can full prototype here:
char *ft_rev_print (char *str)
{
int i;
i = ft_strlen(str);//returning the length of string
while (i>=0)
{
write (1,(str+i),1);
i--;
}
return (str); /*returning its argument */
}
Related
I wanted to write a little program which should reverse characters of a string using the snprintf() function in the following way. That's where I noticed something strange.
int main() {
char dest[5] = "";
char source[5] = "abc";
for (int i = 0; i < 4; i++) {
char c = source[i];
snprintf(dest, 5, "%c%s", c, dest); //here, the current character gets appended in front
//of the String "dest" using snprintf() "recursively"
}
}
What the program should output: cba
The actual output: ccba
When debugging the program you can see that the lowest two bytes (dest[0] and dest[1]) always carry the same information.
Does someone know why this happens and how to prevent this?
If I don't use dest twice in the argument but instead use a temporary buffer like: snprintf(temporary, 5, "%c%s", c, dest) and snprintf(dest, 5, "%s", temporary) directly afterwards everything works as expected.
What you are doing is not allowed by the C standard. From section 7.21.6.5 regarding the snprintf function:
The snprintf function is equivalent to fprintf , except that the
output is written into an array (specified by argument s ) rather than
to a stream. If n is zero, nothing is written, and s may be a null
pointer. Otherwise, output characters beyond the n-1 st are
discarded rather than being written to the array, and a null character
is written at the end of the characters actually written into
the array. If copying takes place between objects that overlap,
the behavior is undefined.
So you can't have the destination be one of the sources. You need to write to a temp string.
If the source and destination overlap, memmove can be used.
#include <stdio.h>
#include <string.h>
int main(){
char dest[5] = "";
char source[5] = "abc";
size_t len = strlen ( source);
for ( size_t i = 0; i < len; i++) {
memmove ( &dest[1], &dest[0], len);//move len bytes in array of [5]
dest[0] = source[i];//set first byte
dest[len] = 0;//ensure zero terminator
printf ( "%s\n", dest);
}
}
If recursion is desired then this can be used.
#include <stdio.h>
size_t strreverse(char *str, size_t index, char *dest) {
char ch = str[index];
if(str[index] =='\0') {
return 0;
}
index = strreverse ( str, index + 1, dest);//recursion
dest[index] = ch;
return index + 1;
}
int main ( void) {
char text[] = "abc";
char result[sizeof text] = "";
strreverse ( text, 0, result);
printf("%s\n", text);
printf("%s\n", result);
return 0;
}
#include <stdio.h>
int
main() {
char string[] = "my name is geany";
int length = sizeof(string)/sizeof(char);
printf("%i", length);
int i;
for ( i = 0; i<length; i++ ) {
}
return 0;
}
if i want to print "my" "name" "is" and "geany" separate then what do I do. I was thinking to use a delimnator but i dont know how to do it in C
start with a pointer to the begining of the string
iterate character by character, looking for your delimiter
each time you find one, you have a string from the last position of the length in difference - do what you want with that
set the new start position to the delimiter + 1, and the go to step 2.
Do all these while there are characters remaining in the string...
I needed to do this because the environment was working in had a restricted library that lacked strtok. Here's how I broke up a hyphen-delimited string:
b = grub_strchr(a,'-');
if (!b)
<handle error>
else
*b++ = 0;
c = grub_strchr(b,'-');
if (!c)
<handle error>
else
*c++ = 0;
Here, a begins life as the compound string "A-B-C", after the code executes, there are three null-terminated strings, a, b, and c which have the values "A", "B" and "C". The <handle error> is a place-holder for code to react to missing delimiters.
Note that, like strtok, the original string is modified by replacing the delimiters with NULLs.
This breaks a string at newlines and trims whitespace for the reported strings. It does not modify the string like strtok does, which means this can be used on a const char* of unknown origin while strtok cannot. The difference is begin/end are pointers to the original string chars, so aren't null terminated strings like strtok gives. Of course this uses a static local so isn't thread safe.
#include <stdio.h> // for printf
#include <stdbool.h> // for bool
#include <ctype.h> // for isspace
static bool readLine (const char* data, const char** beginPtr, const char** endPtr) {
static const char* nextStart;
if (data) {
nextStart = data;
return true;
}
if (*nextStart == '\0') return false;
*beginPtr = nextStart;
// Find next delimiter.
do {
nextStart++;
} while (*nextStart != '\0' && *nextStart != '\n');
// Trim whitespace.
*endPtr = nextStart - 1;
while (isspace(**beginPtr) && *beginPtr < *endPtr)
(*beginPtr)++;
while (isspace(**endPtr) && *endPtr >= *beginPtr)
(*endPtr)--;
(*endPtr)++;
return true;
}
int main (void) {
const char* data = " meow ! \n \r\t \n\n meow ? ";
const char* begin;
const char* end;
readLine(data, 0, 0);
while (readLine(0, &begin, &end)) {
printf("'%.*s'\n", end - begin, begin);
}
return 0;
}
Output:
'meow !'
''
''
'meow ?'
use strchr to find the space.
store a '\0' at that location.
the word is now printfable.
repeat
start the search at the position after the '\0'
if nothing is found then print the last word and break out
otherwise, print the word, and continue the loop
Reinventing the wheel is often a bad idea. Learn to use implementation functions is also a good training.
#include <string.h>
/*
* `strtok` is not reentrant, so it's thread unsafe. On POSIX environment, use
* `strtok_r instead.
*/
int f( char * s, size_t const n ) {
char * p;
int ret = 0;
while ( p = strtok( s, " " ) ) {
s += strlen( p ) + 1;
ret += puts( p );
}
return ret;
}
I am following a pdf to study recursion and string manipulation and I stumbled upon this.. I usually have an understanding of how a recursive function will behave (still not that good) but can't figure this out. This reverses the string. Well, prints it reversed.
void reverse(const char *const sptr);
int main()
{
char sentence[80];
printf("Enter a line of text:\n");
gets(sentence);
printf("\nThe reversed version:\n");
reverse(sentence);
puts("");
return 0;
}
void reverse(const char *const sptr)
{
if(sptr[0] == '\0')
{
return;
}
else{
reverse(&sptr[1]);
putchar(sptr[0]);
}
}
I don't really understand how putchar works in this occasion. Can anyone explain it to me? I imagine this isn't just for putchar, how does the function behave when another command line is written "after" the recalling of the function?
It has nothing to do with putchar, it has everything to do with recursion.
Say you give it the string "1234" - or lets call it ['1','2','3','4','\0']
The first time reverse is called, it is called with the argument sptr pointing to ['1','2','3','4','\0'];
Execution reaches the recursive call to reverse and this time uses the offset 1, hence the argument becomes ['2','3','4','\0']
The process is repeated until a '\0' is found and now the function returns to the previous caller which prints the last character, returns to previous caller, which prints the 2:nd last character and so on until top reverse call is reached which prints the first character, and then exists.
Perhaps printing some additional debug info will make it easier to understand.
#include <stdio.h>
void reverse(const char *const sptr);
int recursion_level;
int main()
{
recursion_level=0;
char sentence[80]="1234";
// printf("Enter a line of text:\n");
// gets(sentence);
printf("\nThe reversed version:\n");
reverse(sentence);
puts("");
return 0;
}
void reverse(const char *const sptr)
{
recursion_level++;
printf("reverse entered, recursion level:%d , sptr:%s \n",recursion_level, sptr);
if(sptr[0] == '\0')
{ recursion_level--;
return;
}
else{
reverse(&sptr[1]);
putchar(sptr[0]);
}
printf("\n reverse exits, recursion level:%d , \n",recursion_level);
recursion_level--;
}
Which generates the following output
The reversed version:
reverse entered, recursion level:1 , sptr:1234
reverse entered, recursion level:2 , sptr:234
reverse entered, recursion level:3 , sptr:34
reverse entered, recursion level:4 , sptr:4
reverse entered, recursion level:5 , sptr:
4
reverse exits, recursion level:4 ,
3
reverse exits, recursion level:3 ,
2
reverse exits, recursion level:2 ,
1
reverse exits, recursion level:1 ,
this works because of that :
reverse(&sptr[1]);
putchar(sptr[0]);
you first call on the next characters then you print the first, so
the first printed character will be the last
then you come back and write the previous being just before the last
...
then you come back and print the first character
&sptr[1] is equivalent of sptr + 1 so that point to the address of the next character
if you reverse the lines and do that :
putchar(sptr[0]);
reverse(&sptr[1]);
you print the characters in the initial order
When you don't understand just execute the program into a debugger step by step
It is very simple.
You recursively call the function with the string as a parameter. Every call the the string is one char shorter (as you pass the pointer to the second char in the string . When it is zero length the first return occurs. The string has a length 1 and it is only the last character of the string. You print it (as you print the first character), then you return to the instance where the string was 2 chars long - you print it the first char whicg is the second from the end. Then you return and the string is 3 chars long you print the first char again and this repeats until you print all chars form the string in the reverse order.
You do not need the else statement at all as if the condition is met the control will never reach that statements
void reverse(const char *const sptr)
{
if(!sptr[0]) return;
reverse(&sptr[1]);
putchar(sptr[0]);
}
int main()
{
reverse("Hello World");
return 0;
}
You can also add the check if the parameter is not NULL
void reverse(const char *const sptr)
{
if(!sptr && sptr[0] == '\0') return;
reverse(&sptr[1]);
putchar(sptr[0]);
}
As all others are saying, this is simple.
But the best thing to explain is to see you how that work in adding some log in your C code.
#include "pch.h"
#include <stdio.h>
#include <stdlib.h>
void reverse(const char *const sptr);
void openTraceFile();
void closeTraceFile();
void enterFunction();
void exitFunction();
void writeMessage(const char* s);
int main()
{
char sentence[80];
printf("Enter a line of text:\n");
//gets(sentence);
fgets(sentence, 80, stdin);
openTraceFile();
printf("\nThe reversed version:\n");
reverse(sentence);
puts("");
closeTraceFile();
return 0;
}
static FILE* logfile;
static int iNrSpaces = 0;
void reverse(const char *const sptr)
{
enterFunction();
if (sptr[0] == '\0')
{
writeMessage("end of string");
exitFunction();
return;
}
reverse(&sptr[1]);
putchar(sptr[0]);
char s[80];
sprintf(s,"putchar( %c )", sptr[0]);
writeMessage(s);
exitFunction();
}
void openTraceFile()
{
logfile = fopen("reverse.log", "w");
if (logfile == NULL)
{
printf("Error! Could not open file\n");
exit(-1);
}
void closeTraceFile()
{
fclose(logfile);
}
void enterFunction()
{
writeMessage(">> reverse()");
iNrSpaces += 4;
writeMessage("{");
}
void exitFunction()
{
writeMessage("}");
iNrSpaces -= 4;
}
void writeMessage(const char* s)
{
for (int i = 0; i < iNrSpaces; i++)
{
fputc(' ',logfile);
}
fprintf(logfile, s);
fputc('\n', logfile);
}
When you execute this program in entering "help", you will obtain following lines in reverse.log file.
>> reverse()
{
>> reverse()
{
>> reverse()
{
>> reverse()
{
>> reverse()
{
>> reverse()
{
end of string
}
putchar( \0 )
}
putchar( p )
}
putchar( l )
}
putchar( e )
}
putchar( h )
}
If now, you extract putchar() call without changing order of execution, you will obtain
putchar( p );
putchar( l );
putchar( e );
putchar( h );
that is the inversed string !
I hope that this explanation using LOG has helped you to understand this problem.
Little remark: \0 character is returner first in your example !
#include <stdio.h>
int
main() {
char string[] = "my name is geany";
int length = sizeof(string)/sizeof(char);
printf("%i", length);
int i;
for ( i = 0; i<length; i++ ) {
}
return 0;
}
if i want to print "my" "name" "is" and "geany" separate then what do I do. I was thinking to use a delimnator but i dont know how to do it in C
start with a pointer to the begining of the string
iterate character by character, looking for your delimiter
each time you find one, you have a string from the last position of the length in difference - do what you want with that
set the new start position to the delimiter + 1, and the go to step 2.
Do all these while there are characters remaining in the string...
I needed to do this because the environment was working in had a restricted library that lacked strtok. Here's how I broke up a hyphen-delimited string:
b = grub_strchr(a,'-');
if (!b)
<handle error>
else
*b++ = 0;
c = grub_strchr(b,'-');
if (!c)
<handle error>
else
*c++ = 0;
Here, a begins life as the compound string "A-B-C", after the code executes, there are three null-terminated strings, a, b, and c which have the values "A", "B" and "C". The <handle error> is a place-holder for code to react to missing delimiters.
Note that, like strtok, the original string is modified by replacing the delimiters with NULLs.
This breaks a string at newlines and trims whitespace for the reported strings. It does not modify the string like strtok does, which means this can be used on a const char* of unknown origin while strtok cannot. The difference is begin/end are pointers to the original string chars, so aren't null terminated strings like strtok gives. Of course this uses a static local so isn't thread safe.
#include <stdio.h> // for printf
#include <stdbool.h> // for bool
#include <ctype.h> // for isspace
static bool readLine (const char* data, const char** beginPtr, const char** endPtr) {
static const char* nextStart;
if (data) {
nextStart = data;
return true;
}
if (*nextStart == '\0') return false;
*beginPtr = nextStart;
// Find next delimiter.
do {
nextStart++;
} while (*nextStart != '\0' && *nextStart != '\n');
// Trim whitespace.
*endPtr = nextStart - 1;
while (isspace(**beginPtr) && *beginPtr < *endPtr)
(*beginPtr)++;
while (isspace(**endPtr) && *endPtr >= *beginPtr)
(*endPtr)--;
(*endPtr)++;
return true;
}
int main (void) {
const char* data = " meow ! \n \r\t \n\n meow ? ";
const char* begin;
const char* end;
readLine(data, 0, 0);
while (readLine(0, &begin, &end)) {
printf("'%.*s'\n", end - begin, begin);
}
return 0;
}
Output:
'meow !'
''
''
'meow ?'
use strchr to find the space.
store a '\0' at that location.
the word is now printfable.
repeat
start the search at the position after the '\0'
if nothing is found then print the last word and break out
otherwise, print the word, and continue the loop
Reinventing the wheel is often a bad idea. Learn to use implementation functions is also a good training.
#include <string.h>
/*
* `strtok` is not reentrant, so it's thread unsafe. On POSIX environment, use
* `strtok_r instead.
*/
int f( char * s, size_t const n ) {
char * p;
int ret = 0;
while ( p = strtok( s, " " ) ) {
s += strlen( p ) + 1;
ret += puts( p );
}
return ret;
}
I'm creating a DataStage parallel routine, which is a C or C++ function that is called from within IBM (formerly Ascential) DataStage. It is failing if one of the strings passed in is zero length. If I put this at the very first line of the function:
return strlen(str);
then it returns 0 for the calls that pass in empty values into str. If I put this at the first line, however...
if (strlen(str)==0) {return 0;}
then it does not return and goes into an infinite loop
I'm baffled - it works fine in a test harness, but not in DataStage.
Maybe there is something odd about the way DataStage passes empty strings to C routines?
int pxStrFirstCharList(char *str, char *chars )
{
if (strlen(str)==0) {return 0;}
if (strlen(chars)==0) {return 0;}
int i = 0;
//Start search
while (str[i]) //for the complete input string
{
if (strchr(chars, str[i]))
{
return i+1;
}
++i;
}
return 0;
}
There is a builtin function for what you are doing, it's called strcspn. This function takes two strings, and searches the first one for the first occurance of any of the characters of the second string.
I suggest using that than RYO...
http://www.cplusplus.com/reference/clibrary/cstring/strcspn/
How about this?
int pxStrFirstCharList(char *str, char *chars )
{
if (str && chars && (0 != strlen(str)) && (0 != strlen(chars)))
{
int i = 0;
//Start search
while (str[i]) //for the complete input string
{
if (strchr(chars, str[i]))
{
return i+1;
}
++i;
}
}
return 0;
}
Also, I don't quite get the point of the while loop ... (and no, I don't mean that this could be written as for). What I mean is that on one hand you are doing a search (strstr) that itself will be implemented as a loop and still you have some outer loop. Could it be that you actually wanted to have chars in its place, i.e.:
int pxStrFirstCharList(char *str, char *chars )
{
if (str && chars && (0 != strlen(str)) && (0 != strlen(chars)))
{
int i = 0;
//Start search
while (chars[i]) //for the complete input string
{
if (strchr(str, chars[i]))
{
return i+1;
}
++i;
}
}
return 0;
}
...? That is, look for each of the characters within chars inside the string denoted by str ...
If NULL is not explicitly part of the game, at least during development phase, it's always a good idea to add a precondition check on pointers received by a function:
int pxStrFirstCharList(char *str, char *chars )
{
if (!str)
return -1;
if (!chars)
return -2;
....
(The negative values -1 and -2 than tell the caller that something went wrong)
Or doing it in a more relaxed way, silently accepting NULL pointer strings as ""-string:
int pxStrFirstCharList(char *str, char *chars )
{
if (!str)
return 0;
if (!chars)
return 0;
...
If you are the only one using this API you could #ifndef BUILD_RELEASE these checks away for a release build if anything is tested stable.
I guess it is the strlen's issue when the length of the string is 0. For example,
char s1[0];
char *s2="a";
printf("%d %s\n", sizeof(s1), s1);//0 #
printf("%d %s\n", strlen(s1), s1);//3 #
printf("%d %s\n", sizeof(s2), s2);//8 a
printf("%d %s\n", strlen(s2), s2);// 1 a
You will get a weird answer for using strlen and you can check its source code in detail(https://code.woboq.org/userspace/glibc/string/strlen.c.html). In nutshell, you can use sizeof instead of strlen for char string or avoid 0 length case by using strlen.