This question already has an answer here:
Why can I not modify a string literal in c?
(1 answer)
Closed 2 years ago.
I'm kind of struggling with some of the basics for C. I tried to compile this program and it came up with a Signal 11 Error. I know this is to do with memory allocation, but I'm not sure how to use malloc() correctly to make this work. Can someone help?
{
char *string = "Lol";
convert_lower(string);
printf("%s\n", string);
return 0;
}
char *convert_lower(char *word) {
for ( ; *word; ++word) *word = tolower((char)*word); // J.F. Sebastian
return word;
}
You are giving convert_lower() a pointer to a string literal, so it will try to modify read-only memory. That is why you get the runtime error.
You need to make a writable copy of the string literal's data before you can then modify it, eg:
char *literal = "Lol";
char *string = malloc(strlen(literal)+1);
strcpy(string, literal);
convert_lower(string);
printf("%s\n", string);
free(string);
Which can be simplified using strdup() instead, which will handle the allocation and copy for you:
char *string = strdup("Lol");
convert_lower(string);
printf("%s\n", string);
free(string);
And then, you can simplify this further by just not allocating any dynamic memory at all:
char string[] = "Lol";
convert_lower(string);
printf("%s\n", string);
There are at least two serious errors.
The first one is that you may not change a string literal. Any attempt to change a string literal results in undefined behavior.
char *string = "Lol";
convert_lower(string);
The second one is that within the function the passed pointer is changed. so the function returns a pointer that points to the terminating zero instead of pointing to the beginning of the string.
And instead of casting to char
tolower((char)*word)
you need to cast to unsigned char
tolower( (unsigned char)*word)
The function can be defined like
char * convert_lower( char *word )
{
for ( char *p = word; *p; ++p )
{
*p = tolower( ( unsigned char )*p );
}
return word;
}
and called like
char string[] = "Lol";
puts( convert_lower(string) );
If you want to make a copy of the original string converting all characters to the lower case then the function can look the following way
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
//...
char * convert_lower( const char *word )
{
char *result = malloc( strlen( word ) + 1 );
if ( result != NULL )
{
char *p = result;
while ( ( *p++ = tolower( ( unsigned char )*word++ ) ) != '\0' );
}
return result;
}
And the function can be called like
char *string = "Lol";
char *lower_case_string = convert_lower( string );
if ( lower_case_string != NULL ) puts( lower_case_string );
free( lower_case_string );
Related
So, I'm trying to code a strcat function using pointers, just for studying purposes.
#include <stdio.h>
#include <string.h>
char *strcpyy(char *dest, char *orig){
char *tmp = dest;
while (*dest++ = *orig++);
return tmp;
}
char *strcatt(char *dest, char *orig){
strcpyy(dest + strlen(dest), orig);
return dest;
}
int main(){
char *a = "one";
char *b = "two";
printf("%s", strcatt(a,b));
}
When I run this code, the output is empty. Can anyone point out the problem?
String literals are read-only. Any attempt to write to a string literal will invoke undefined behavior, which means that your program may crash or not behave as intended.
Therefore, you should not use a pointer to a string literal as the first argument to strcat or your equivalent function. Instead, you must provide a pointer to an object which is writable and has sufficient space for the result (including the terminating null character), for example a char array of length 7. This array can be initialized using a string literal.
Therefore, I recommend that you change the line
char *a = "one";
to the following:
char a[7] = "one";
After making this change, your program should work as intended.
You declared two pointers to string literals
char *a = "one";
char *b = "two";
You may not append one string literal to another.
Instead you need to define the variable a as a character array large enough to contain the appended string literal pointed to by the pointer b.
And the both functions should be declared like
char *strcpyy(char *dest, const char *orig);
char *strcatt(char *dest, const char *orig);
Also as you are using standard C string functions like strlen
strcpyy(dest + strlen(dest), orig);
then it will be logically consistent to use standard C function strcpy instead of your own function strcpyy.
Otherwise without using standard string functions your function strcatt can look the following way
char * strcatt( char *s1, const char *s2 )
{
char *p = s1;
while ( *p ) ++p;
while ( ( *p++ = *s2++ ) != '\0' );
return s1;
}
Here is a demonstration program.
#include <stdio.h>
char * strcatt( char *s1, const char *s2 )
{
char *p = s1;
while ( *p ) ++p;
while ( ( *p++ = *s2++ ) != '\0' );
return s1;
}
int main( void )
{
char a[7] = "one";
const char *b = "two";
puts( strcatt( a, b ) );
}
The program output is
onetwo
You cannot modify "string literals". Those are not mutable.
The usual idiom for this sort of operation is to build up a string in a temporary working buffer that should be pre-dimensioned large enough to hold all that is required.
The following also shows more obvious code in both your functions.
#include <stdio.h>
char *strcpyy( char *dst, const char *org ) {
for( char *p = dst; (*p++ = *org++) != '\0'; /**/ )
; // loop
return dst;
}
char *strcatt( char *dst, const char *org ) {
char *p = dst;
while( *p != '\0' )
p++; //loop
while( (*p = *org++) != '\0' )
p++; // loop
return dst;
}
int main(){
const char *a = "one ";
const char *b = "two ";
const char *c = "three";
char wrk[ 64 ]; // sufficient mutable space defined
printf( "%s\n", strcatt( strcatt( strcpyy( wrk, a ), b ), c ) );
return 0;
}
one two three
#include <stdio.h>
#include <stdlib.h>
char concaten(const char *str1,const char *str2);
int main()
{
printf("%s",concaten("Code","blocks"));
return 0;
}
char concaten(const char *str1,const char *str2) {
int i=0,j=0;
char *result;
while(*str1){
result[i++]=str1[i++];
}
while(*str2){
result[i+j++]=str2[j++];
}
return result;
}
I wrote this function to get two strings and add them to another third string; I don't understand where I am going wrong, as it doesn't print anything.
There are a number of problems with your concaten function.
First, it should be returning a char* pointer, not a char; thus, the declaration should look like this:
char* concaten(const char* str1, const char* str2);
Next, the function will need to allocate memory in which to store the concatenated strings; this can be done with the malloc() function, and the number of characters required will be the sum of the lengths of the two input strings plus one, for the required nul-terminator.
Third, the logic of your two loops is wrong. You are incrementing i and j twice per loop but not incrementing either of the source pointers.
Finally, you must add a nul-terminator at the end of your new string.
Here's a version with the above fixes applied:
char* concaten(const char* str1, const char* str2)
{
int i = 0, j = 0;
char* result = malloc(strlen(str1) + strlen(str2) + 1); // allow space for nul-terminator
while (*str1) {
result[i++] = *str1++; // Only increment i once and "str1" once
}
while (*str2) {
result[i + j++] = *str2++; // Only increment j once and "str2" once
}
result[i + j] = '\0'; // Add required nul-terminator
return result;
}
Also, as you have allocated memory (with the malloc call), you should release that when you're done with the data, using a call to free. Here's how your main might work:
int main(void)
{
char* answer = concaten("Code", "blocks");
printf("%s", answer);
free(answer);
return 0;
}
Note: You can also remove the j variable entirely, and just re-use the result[i++] expression in the second loop. I've left it in so that you can more easily relate my code to your own.
Your function has the return type char
char concaten(const char *str1,const char *str2);
but within the function you are returning the variable result
return result;
declared like a pointer of the type char *
char *result;
So the compiler will issue a message that you are trying to convert a pointer to an integer.
The function must be declared like
char * concaten(const char *str1,const char *str2);
The pointer result is not initialized and has an indeterminate value. You need to allocate memory where you will write concatenated strings.
The while loops in the function will be infinite if str1 and/or str2 are not empty strings due to conditions
while(*str1){
and
while(*str2){
These statements
result[i++]=str1[i++];
and
result[i+j++]=str2[j++];
invoke undefined behavior not only because the pointer result is not initialized but also because there is no sequence point between left and write operands where there is used the postfix increment operator ++.
Also the result string must be zero terminated.
If you are not allowed to use standard C string functions then your function can be implemented for example the following way
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = 0;
size_t n2 = 0;
while ( str1[n1] ) ++n1;
while ( str2[n2] ) ++n2;
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
char *p = result;
while ( *str1 ) *p++ = *str1++;
do
{
*p++ = *str2;
} while ( *str2++ );
}
return result;
}
Also you should not forget to free the allocated memory when the result string is not needed any more.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = 0;
size_t n2 = 0;
while ( str1[n1] ) ++n1;
while ( str2[n2] ) ++n2;
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
char *p = result;
while ( *str1 ) *p++ = *str1++;
do
{
*p++ = *str2;
} while ( *str2++ );
}
return result;
}
int main(void)
{
char *result = concatenate( "Code ", "blocks" );
if ( result != NULL ) puts( result );
free( result );
return 0;
}
The program output is
Code blocks
If you may use standard C string functions then the function concatenate can look as it is shown in the demonstrative program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * concatenate( const char *str1, const char *str2 )
{
size_t n1 = strlen( str1 );
size_t n2 = strlen( str2 );
char *result = malloc( n1 + n2 + 1 );
if ( result != NULL )
{
memcpy( result, str1, n1 );
memcpy( result + n1, str2, n2 + 1 );
}
return result;
}
int main(void)
{
char *result = concatenate( "Code ", "blocks" );
if ( result != NULL ) puts( result );
free( result );
return 0;
}
The program output is the same as shown above that is
Code blocks
Aside from the fact that your function should not return char but char*, the expression result[i++] = str1[i++]; is not correct it lacks a sequence point. Furthermore result is an unitialized pointer, it cannot hold any data, you would need to make it point to some valid memory location.
You could do something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* concatenate(const char* str1, const char* str2 ){
char* result = malloc(strlen(str1) + strlen(str2) + 1);
if (result){
char* temp = result;
while (*str1 != '\0'){
*temp++ = *str1++;
}
while (*str2 != '\0'){
*temp++ = *str2++;
}
*temp = '\0'; // don't forget to null terminate the string
}
return result; // if memory allocation fails a null pointer is returned
}
The direct usage of the function in the printf statement will not allow you to free the memory and you would have a memory leak if the program didn't finish immediately, in these cases it's best to have the returned pointer assigned as to not lose track of the allocated memory:
int main(void){
char *result = concatenate("Code", "blocks");
if(result){
printf("%s", result);
free(result);
}
return EXIT_SUCCESS;
}
I am trying to implement the strcat function by myself, using pointers.
My pseudo-code is:
receive 2 char pointers (which means 2 strings (= 2 char arrays)).
create a copy of the dest array
realloc() space of dest to be the sum of the sizes of source string + dest string, because I need the dest string to include in the end both of the strings.
create a pointer that points to the first index of the dest string, this pointer will be returned as the function's return value.
run 2 simple while loops on both source and copy of dest strings, and while doing this, copy all contents to the dest
free all allocated pointers
return the address that points to the first index of the dest string by return the created pointer that was created for this exact purpose.
So. I wrote this:
char *StrCat(char *dest, const char *src)
{
size_t size = strlen(dest) + strlen(src) + 1;
char *temp = (char *) malloc(strlen(dest) + 1);
char *start = dest;
dest = (char *) realloc(dest, size);
strcpy(temp,dest);
while (*src)
{
*dest = *src;
src++;
dest++;
}
while (*temp)
{
*dest = *temp;
temp++;
dest++;
}
*dest = '\0';
free(temp);
return start;
}
void strcatTest()
{
char source[] = "this is source of cat";
char dest4[100] = "this is dest of cat";
StrCat(dest4,source);
puts(dest4);
}
and I'm getting:
realloc(): invalid pointer.
I know there are a lot of working and better implementations of strcat around the internet and on stackoverflow, but my purpose here is to understand what did I do wrong and why I failed to create a working implementation.
What went wrong with my understanding of starcat()?
Beware, you can only use realloc on a pointer that has previously been alloc-ed, and not on a pointer to an automatic or static storage array.
So those lines are enough to cause the error:
char *StrCat(char *dest, const char *src)
{
...
dest = (char *) realloc(dest, size);
...
char dest4[100] = "this is dest of cat";
StrCat(dest4,source);
This is the reason why the standard library does not try to reallocate...
From the C Standard (7.23.3.1 The strcat function)
2 The strcat function appends a copy of the string pointed to by s2
(including the terminating null character) to the end of the string
pointed to by s1. The initial character of s2 overwrites the null
character at the end of s1. If copying takes place between objects
that overlap, the behavior is undefined.
So within the standard C string function strcat neither allocation or reallocation memory is present. The destination character array shall be initially large enough to accommodate within itself one more string.
Take into account that you need to rename your function. Otherwise there will be a conflict between your function and the standard function.
The function definition can look the following way (without using any standard string function)
char * new_strcat( char *s1, const char *s2 )
{
char *p = s1;
while ( *p ) ++p;
while ( ( *p++ = *s2++ ) );
return s1;
}
Here is a demonstrative program.
#include <stdio.h>
char * new_strcat( char *s1, const char *s2 )
{
char *p = s1;
while ( *p ) ++p;
while ( ( *p++ = *s2++ ) );
return s1;
}
int main(void)
{
char s1[14] = "Hello ";
const char *s2 = "World!";
puts ( new_strcat( s1, s2 ) );
return 0;
}
The program output is
Hello World!
If you want to write a new function that combines two strings creating a new character array (such a function is a different function compared with the standard string function strcat) then the function can be declared and defined the following way (in this implementation there is used the standard string function strlen though it can be written without using the function).
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * combine( const char *s1, const char *s2 )
{
char *result = malloc( strlen( s1 ) + strlen( s2 ) + 1 );
if ( result )
{
char *p = result;
while ( *s1 ) *p++ = *s1++;
while ( ( *p++ = *s2++ ) );
}
return result;
}
int main(void)
{
const char *s1 = "Hello ";
const char *s2 = "World!";
char *p = combine( s1, s2 );
if ( p ) puts( p );
free( p );
return 0;
}
Again the program output is
Hello World!
As for your function then it is incorrect and does not make any sense. For example the user can pass to the function a character array with automatic storage duration. You may not reallocate it dynamically using realloc.
Or you at first assign the pointer start with the value of the pointer dest but then the memory pointed to by the pointer dest was reallocated. As a result the pointer start has an invalid value that is returned from the function.
char *start = dest;
dest = (char *) realloc(dest, size);
//...
return start;
Also you are trying to copy strings in the reverse order relative to each other, At first you are copying the string pointed to by the pointer src and then the string that initially was pointed to by the pointer dest. S0 you are not appending the second string to the tail of the first string.
Pay attention to your function declaration
char *StrCat(char *dest, const char *src);
The first parameter without the qualifier const means that the string pointed to by the pointer dest will be changed in place. So even the function declaration contradicts your function implementation because you are not changing the destination character array in place. You are trying to create a new character array.
this program it suppose to print Hello World but guess what exited, segmentation fault why is that happening ?
#include <stdio.h>
#include <string.h>
char f(char *a, char *b)
{
int i , m, n;
m = strlen(a);
n = strlen(b);
for (i = 0; i<=n; i++)
{
a[m+i] = b[i];
}
}
int main() {
char*str1 = "hello ";
char*str2 = "world!";
str1=f(str1, str2);
printf("%s", str1);
return 0;
}
You are not allowed to modify string literals. Use arrays with enough elements instead for strings to be modified.
Also assigning the return value of f to str1 is a bad idea because no return statement is executed in the function f and using its return value invokes undefined behavior. The return type should be changed to void if you are not going to return anything.
#include <stdio.h>
#include <string.h>
void f(char *a, char *b)
{
int i , m, n;
m = strlen(a);
n = strlen(b);
for (i = 0; i<=n; i++)
{
a[m+i] = b[i];
}
}
int main() {
char str1[16] = "hello ";
char*str2 = "world!";
f(str1, str2);
printf("%s", str1);
return 0;
}
First of all, this:
char*str1 = "hello ";
is a pointer to constant data, which means that you can't change the string "hello "
This is a constant pointer to variable data:
char str1[] = "hello ";
Which means that str1 always points to the same address in memory, but you can modify the content of that chunk of memory.
However str1 will have a fixed size of 7 characters (don't forget to count \0), so you can't append another string to it.
You could define a size #define SIZE 20 large enough to store both strings and declare
char str1[SIZE] = "hello ";
Or you could declare str1 as a VLA (variable length array) after having declared the string to append:
char*str2 = "world!";
char str1[strlen("hello ")+strlen(str2)+1] = "hello ";
Where the +1 is for \0.
Is it important that you copy characters one by one?
Because if it's not you can just copy one string to another like this.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char str1[] = "hello ";
char str2[] = "world!";
char *result = malloc(strlen(str1) + strlen(str2) + 1);
strcpy(result, str1);
strcat(result, str2);
printf("%s", result);
return 0;
}
First you are not allowed to change a constant string, that is undefined behaviour.
Secondly your f function has no return statement and thus returns random data, making the str1 variable in main point to random memory. Using it then also has undefined behaviour.
To fix it you should allocate new memory and concatenate the string into that
char* f(const char *s1, const char *s2)
{
char *s = malloc(strlen(s1) + strlen(s2) +1);
if (s) {
strcpy(s, s1);
strcat(s, s2);
}
return s;
}
The extra one byte allocated is for the terminating zero.
Both arguments are const as there is no reason to modify them, which allows both arguments to be literal strings.
For starters you may not change string literals (in this case the string literal pointed to by the pointer str1).
char*str1 = "hello ";
char*str2 = "world!";
Any attempt to change a string literal results in undefined behavior.
You need to allocate a character array large enough to store the result string with the appended string literal pointed to by the pointer str2.
Secondly there is already the standard C function strcat that performs the required task. If you have to write such a function yourself then it seems you should not use any string function as for example strlen.
And the return type char of your function does not make a sense. And moreover actually your function returns nothing.
So this assignment
str1=f(str1, str2);
results in undefined behavior.
The function and the program in whole can be written the following way without using standard string functions.
#include <stdio.h>
char * f( char *s1, const char *s2 )
{
char *p = s1;
while ( *p ) ++p;
while ( ( *p++ = *s2++ ) );
return s1;
}
int main(void)
{
char s1[14] = "Hello ";
char *s2 = "World!";
puts( f( s1, s2 ) );
return 0;
}
The program output is
Hello World!
Pay attention to that the second function parameter shall have the qualifier const because the pointed string is not changed within the function. And the function return type should be char * that is the function should return the result string.
I'm trying to write a function that removes whitesapces from a string and convert it to lower case.
My code below doesn't return anything. Why?
char *removeSpace(char *st);
int main(void){
char *x = "HeLLO WOrld ";
x = removeSpace(x);
printf("output: %s\n", x);
}
char *removeSpace(char *st)
{
int c = 0;
char *s = malloc(sizeof(strlen(st)+1));
for (int x = 0; x < strlen(st); x++)
{
if (st[x] != ' ')
{
s[x] = tolower(st[x]);
}
}
st= s;
st= s;
return st;
}
The malloc statement uses sizeof unnecessarily as mentioned in the comments. You also have an error in the assignment of characters to the new string:
s[x] = tolower(st[x]);
You use the same index to the new string s as the old string st. This isn't right as soon as you remove any spaces. So for example indexes 0 through 4 line up between the two strings as you copy hello but then you skip a space at index 5 and then you want to assign the w at st[6] to s[5]. This means you need a separate index to track where you are in the destination string. So you need something like this code, which cleans up malloc(), adds the missing header includes, and introduces a new index for the output string:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *removeSpace(char *st);
int main(void){
char *x = "HeLLO WOrld ";
x = removeSpace(x);
printf("output: %s\n", x);
}
char *removeSpace(char *st)
{
size_t len = strlen(st);
int newStrIdx = 0;
char *s = malloc(len+1);
for (int x = 0; x < len; x++)
{
if (st[x] != ' ')
{
s[newStrIdx++] = tolower(st[x]);
}
}
s[newStrIdx] = '\0';
return s;
}
Oh, and you forgot the null-terminate the output string, which I added at the end.
char *s = malloc(sizeof(strlen(st)+1));
you have a couple of nested expressions, and you jumped exactly the wrong way in the comment thread (I guess it was 50:50).
strlen(st) is the number of characters in the string st
strlen(st)+1 is the correct number of characters to allocate for a copy
... looking good so far!
sizeof(strlen(st)+1) is the size in bytes required to represent the type of that value. So if size_t is an 4-byte unsigned int, this sizeof expression is just 4.
The value of the string length is thrown away at this point.
Now, you want to allocate enough bytes for the string, not enough bytes to save the string's length as a size_t value. Just remove the sizeof entirely.
Oh, and also - st = s doesn't do anything here. The variable st is local inside the function, and doesn't affect anything outside. Returning s is sufficient.
For starters if you want to create a copy of a string then the function declaration shall look like
char * removeSpace( const char *st);
that is the original string is not changed within the function.
And as you are passing to the function a string literal
char *x = "HeLLO WOrld ";
x = removeSpace(x);
then indeed it may not be changed within the function. Any attempt to change a string literal results in undefined behavior.
The expression used in the call of malloc
sizeof(strlen(st)+1)
is equivalent to the expression
sizeof( size_t )
due to the fact that the function strlen has the return type size_t.
So this expression does not yield the length of the source string.
Moreover there is no need to allocate a string with the size equal to the size of the source string because the destination string can have much less characters (due to removing spaces) than the source string.
The assignment in the if statement
if (st[x] != ' ')
{
s[x] = tolower(st[x]);
}
uses an invalid index in the expression s[x]. That is as a result the destination string will contain gaps with uninitialized characters.
Also the terminating zero character '\0' is not appended to the destination string
Take into account that the set of white space characters includes other characters as for example the tab character '\t' apart from the space character ' '.
The function can be defined the following way.
char * removeSpace( const char *st )
{
size_t n = 0;
for ( const char *src = st; *src; ++src )
{
if ( !isspace( ( unsigned char )*src ) ) ++src;
}
char *result = malloc( n + 1 );
result[n] = '\0';
for ( char *dsn = result; *st; ++st )
{
if ( !isspace( ( unsigned char )*st ) )
{
*dsn++ = tolower( ( unsigned char )*st );
}
}
return result;
}
And the function can be called like
char *st = "HeLLO WOrld ";
char *dsn = removeSpace( st );
puts( dsn );
free( dsn );
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
char * removeSpace( const char *st )
{
size_t n = 0;
for ( const char *src = st; *src; ++src )
{
if ( !isspace( ( unsigned char )*src ) ) ++src;
}
char *result = malloc( n + 1 );
result[n] = '\0';
for ( char *dsn = result; *st; ++st )
{
if ( !isspace( ( unsigned char )*st ) )
{
*dsn++ = tolower( ( unsigned char )*st );
}
}
return result;
}
int main(void)
{
char *st = "HeLLO WOrld ";
char *dsn = removeSpace( st );
puts( dsn );
free( dsn );
return 0;
}
Its output is
helloworld