I am trying to write a function that will split a string along a space (' ') without changing the original string, put all of the tokens into an array, then return that array. The problem I am running into is in returning the pointer. Below is my code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split_line(char *ln) {
char **tokens, *tok, line[256];
int j;
strcpy(line, ln);
tokens = calloc(64, sizeof(char*));
for (int i = 0; i < 64; i++)
tokens[i] = calloc(64, sizeof(char));
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++)
tokens[j] = tok;
tokens[j] = NULL;
return tokens;
}
int main(void) {
char **cut_ln, *input;
input = "Each word in this string should be an element in cut_ln.";
cut_ln = split_line(input);
printf("`%s`\n", input);
for (int i = 0; cut_ln[i] != NULL; i++)
printf("[%d]: `%s`\n", i, cut_ln[i]);
return 0;
}
When run, this gives:
`This word in this string should be an element in cut_ln.`
[0]: `This`
[1]: `wo1`
[2]: `�={G+s`
[3]: `G+s`
[4]: `string`
[5]: ``
[6]: `0����`
[7]: `��`
[8]: ``
[9]: ``
[10]: ``
When I try to print the contents of tokens in the split_line function, it gives the expected result. However, when tokens is returned and assigned to a variable, and then printed, it gives the result above as demonstrated. What am I doing wrong?
When you return tokens, it contains the pointers returned from strtok, which are pointers into line. But line no longer exists at this point.
You allocated memory and made the various elements of tokens point to that allocated memory. Don't overwrite those values with the values returned from strtok.
For starters the function should be declared at least like
char ** split_line( const char *ln );
because the passed string is not being changed within the function.
The function will be a more flexible if to declare a second parameter that will specify delimiters.
char ** split_line( const char *ln, const char *delim );
Secondly it is a bad idea to use magic numbers like 64 or 256. The function will not work if the passed string contains more than 63 tokens or when the string for example contains at least one token the length of which is greater than 63.
You dynamically allocated 64 arrays in this loop
for (int i = 0; i < 64; i++)
tokens[i] = calloc(64, sizeof(char));
and assigned their addresses to elements of the array pointed to by the variable tokens. But at once you reassigned the pointers with addresses inside the local array line.
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++)
tokens[j] = tok;
So the function produces numerous memory leaks. And the returned array of pointers will contain invalid pointers because the local array line will not be alive after exiting the function.
Also this statement
tokens[j] = NULL;
is redundant. Using calloc you already set initially all pointers to NULL.
The function can look the following way as it is shown in the demonstrative program below.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char ** split_line( const char *s, const char *delim )
{
size_t n = 0;
for ( const char *p = s; *p; )
{
p += strspn( p, delim );
if ( *p )
{
++n;
p += strcspn( p, delim );
}
}
char **tokens = calloc( n + 1, sizeof( char * ) );
if ( tokens )
{
size_t i = 0;
int success = 1;
for ( const char *p = s; success && *p; i += success )
{
p += strspn( p, delim );
if ( *p )
{
const char *q = p;
p += strcspn( p, delim );
tokens[i] = malloc( p - q + 1 );
if ( ( success = tokens[i] != NULL ) )
{
memcpy( tokens[i], q, p - q );
tokens[i][p-q] = '\0';
}
}
}
if ( !success )
{
for ( char **p = tokens; *p; ++p )
{
free( *p );
}
free( tokens );
}
}
return tokens;
}
int main(void)
{
const char *s = "Each word in this string should be an element in cut_ln.";
char **tokens = split_line( s, " " );
if ( tokens )
{
for ( char **p = tokens; *p; ++p )
{
puts( *p );
}
for ( char **p = tokens; *p; ++p )
{
free( *p );
}
}
free( tokens );
return 0;
}
The program output is
Each
word
in
this
string
should
be
an
element
in
cut_ln.
Related
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
I'm learning C now, and i have one question in my program.
I need to reverse string like
I like dogs -> I ekil sgod
I wrote this code
char end[MAX_LEN];
char beg[MAX_LEN];
char* piece = strtok(str, " ");
strcpy(end, piece);
strcpy(beg, piece);
char* pbeg = beg;
char* prev = piece;
int n = strlen(piece)-1;
i = 0;
int n = 0;
while (piece != NULL) {
//printf("\n%s", piece);
while (piece[i] != '\0') {
*(prev + n -i ) = *(pbeg + i);
i++;
}
printf("\n%s", piece);
piece = strtok(NULL, " ");
strcpy(beg, piece); // also in this moment in debugging i saw this error ***Exception thrown at 0x7CBAF7B3 (ucrtbased.dll) in лаб131.exe: 0xC0000005: Access violation reading location 0x00000000.***
}
But it returns only the first lexeme reversed.
You are getting an exception because you are not checking whether the pointer piece is equal to NULL when you are using it in the call of strcpy
piece = strtok(NULL, " ");
strcpy(beg, piece);
Also within the while loop you forgot to reset the variables i and n and the pointer prev.
To use the function strtok is a bad idea because the source string can contain adjacent space characters that should be preserved in the result string. Also you have too many arrays and pointers that only confuse readers of the code.
Here is a demonstration program that shows how the task can be easy done.
#include <stdio.h>
#include <string.h>
void reverse_n( char s[], size_t n )
{
for ( size_t i = 0; i < n / 2; i++ )
{
char c = s[i];
s[i] = s[n-i-1];
s[n-i-1] = c;
}
}
int main(void)
{
char input[] = "I like dogs";
const char *separator = " \t";
for ( char *p = input; *p; )
{
p += strspn( p, separator );
char *q = p;
p += strcspn( p, separator );
reverse_n( q, p - q );
}
puts( input );
return 0;
}
The program output is
I ekil sgod
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 was trying to reverse a sentence word by word. (how are you -> you are how) First of all I create a char sentence and reverse and temp. Sentence given by user to reverse. Temp catches the word to change the location in the sentence.Then I use strcat to concatenate each word. Here is the problem. I can find the word which is end of the sent(takes input) but when I'm trying to concatenate to reverse, it add this word to sentence and an error occurs. What's the problem?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* subs(char* temp, char* src, int start, int end);
int main() {
char sent[15]; //input sentence
char rev[15]; // output sentence
char *temp=(char*)calloc(1,sizeof(char)); //for the word
scanf(" %[^\n]%*c", &sent); // takin' input
int i, end, start;
i = strlen(sent);
//find the beggining and ending of the indexes of the word in sentence
while (i > 0) {
while (sent[i] == ' ') {
i--;
}
end = i-1;
while (sent[i] != ' ') {
i--;
}
start = i + 1;
//add the word to temp and concatenate to reverse
temp=subs(temp, sent, start, end);
strncat(rev, temp,end-start+3);
}
rev[strlen(sent)] = '\0';
printf("%s", rev);
return 0;
}
char* subs(char* temp, char* src, int start, int end) {
int i = 0, control;
// resize the temp for the wırd
temp = (char*)realloc(temp,end-start+3);
for (; i < (end - start) + 1; i++) {
control = (start + i);
temp[i] = src[control];
}
//adding blank and null character to end of the word.
temp[i] = ' ';
temp[++i] = '\0';
return temp;
}
I will just copy my good answer from this question that was not yet closed Reverse a string without strtok in C
. So I can not use this reference to close your question as a duplicate.
A standard approach is to reverse each word within a string and then to reverse the whole string.
The standard C function strtok is not appropriate in this case. Instead use the standard C functions strspn and strcspn.
Here you are.
#include <stdio.h>
#include <string.h>
static char * reverse( char *s, size_t n )
{
for ( size_t i = 0; i < n / 2; i++ )
{
char c = s[ i ];
s[ i ] = s[ n - i - 1 ];
s[ n - i - 1 ] = c;
}
return s;
}
char * reverse_by_words( char *s )
{
const char *delim = " \t";
char *p = s;
while ( *p )
{
p += strspn( p, delim );
if ( *p )
{
char *q = p;
p += strcspn( p, delim );
reverse( q, p - q );
}
}
return reverse( s, p - s );
}
int main(void)
{
char s[] = "5 60 +";
puts( s );
puts( reverse_by_words( s ) );
return 0;
}
The program output is
5 60 +
+ 60 5
If you want to keep leading and trailing spaces as they were in the original string then the functions can look the following way
#include <stdio.h>
#include <string.h>
static char *reverse( char *s, size_t n )
{
for ( size_t i = 0; i < n / 2; i++ )
{
char c = s[i];
s[i] = s[n - i -1 ];
s[n - i - 1] = c;
}
return s;
}
char * reverse_by_words( char *s )
{
const char *delim = " \t";
char *first = s, *last = s;
for ( char *p = s; *p; )
{
p += strspn( p, delim );
if ( last == s ) first = last = p;
if ( *p )
{
char *q = p;
p += strcspn( p, delim );
last = p;
reverse( q, p - q );
}
}
reverse( first, last - first );
return s;
}
int main(void)
{
char s[] = "\t\t\t5 60 +";
printf( "\"%s\"\n", s );
printf( "\"%s\"\n", reverse_by_words( s ) );
return 0;
}
The program output is
" 5 60 +"
" + 60 5"