I'm trying to append a string, but whenever it gets to the strcat() function the program exits without printing the string.
I've been running this code from within Visual Studio as well as from its own .exe file and get the same result. Can anybody tell me what I'm doing wrong?
main()
{
char str[100], slash = 'H';
gets(str);
printf("\n\n%s\n\n%i\n\n", str, strlen(str));
strcat(str, slash);
printf("\n%s", str);
}
The function gets is unsafe and is not supported by the C Standard. Instead you standard C function fgets.
The function strcat is declared the following way
char * strcat(char * restrict s1, const char * restrict s2);
That is it expects two arguments of the pointer type char * that point to string literal. However the variable slash is declared as having the type char
char str[100], slash = 'H';
Instead you should write the declaration the following way
char str[100], *slash = "H";
Now the variable slash has the pointer type char * and points to the string literal "H".
Pay attention to that you should guarantee that the array str has a space to accommodate the string literal.
Also to output the returned value of the function strlen you have to use the conversion specifier zu instead of i or d
printf("\n\n%s\n\n%zu\n\n", str, strlen(str));
Also bear in main that according to the C Standard the function main without parameters shall be declared like
int main( void )
Actually if you need to append only one character to a string then it can be done without the function strcat the following way
#include <stdio.h>
#include <string.h>
int main( void )
{
char str[100], slash = 'H';
fgets( str, sizeof( str ), stdin );
size_t n = strcspn( str, "\n" );
if ( n != sizeof( str ) - 1 )
{
str[n] = slash;
str[++n] = '\0';
}
printf( "\n%s\n", str );
}
char * strcat ( char * destination, const char * source ); this is a declaration of strcat(), so the second type should be a pointer to c-string, but you are passing a single character.
#include <stdio.h>
#include <string.h>
int main()
{
char str[100], slash[] = "H";
fgets(str, sizeof(str), stdin);
str[strcspn(str, "\n")] = '\0';
printf("\n\n%s\n\n%i\n\n", str, strlen(str));
strcat(str, slash);
printf("\n%s", str);
return 0;
}
Just convert slash to be c-string instead of a single character.
Related
I need to concatenate the strings "abc" and "def" using strlen but without using strcat. Can someone show me what is wrong with my main() function?
#include <stdio.h>
#include <string.h>
void strconcatenate(char *string1, char *string2) {
int i;
int j = strlen(string1);
for (i = 0; string2[i]; i++) {
string1[i+j] = string2[i];
}
string1[i + j]= '\0';
}
int main() {
char string1[3] = "abc";
string1[strlen(string1) - 1] = '\0';
char string2[3] = "def";
string2[strlen(string2) - 1] = '\0';
strconcatenate(string1, string2);
printf("Resultant string = %s\n", string1);
return 0;
}
In main you declared character arrays with three elements
char string1[3] = "abc";
//...
char string2[3] = "def";
These arrays do not contain strings because they do not store the terminating zero character '\0' of the string literals used as initializers. The first array contains three characters { 'a', 'b', 'c' } and the second array contains these three characters { 'd', 'e', 'f' }.
Then you applied the standard string function strlen to these arrays which expects that passed to it arrays contains strings. That is the function calculates the number of characters in a string by counting characters until the terminating zero character is encountered. As the arrays do not contain the terminating zero character '\0' that is as the arrays do not contain strings the calls of strlen invoke undefined behavior.
If you want to append one string to another string then the destination character array shall have enough size to be able to accommodate the second string.
Thus the function main can look the following way
int main( void ) {
char string1[7] = "abc";
char string2[] = "def";
printf( "Resultant string = %s\n", strconcatenate( string1, string2 ) );
return 0;
}
Now the both arrays string1 and string2 contain strings. Moreover the array string1 reserved enough space to accommodate the string stored in the array string2.
The declaration of the function strconcatenate should be similar to the declaration of the standard C function strcat.
char * strconcatenate( char *string1, const char *string2 );
That is the second parameter should have the qualifier const because the passed array is not changed within the function and the function should return the destination array that will contain the concatenated strings.
The function definition will look the following way
char * strconcatenate( char *string1, const char *string2 )
{
for ( char *p = string1 + strlen( string1 ); *p++ = *string2++; );
return string1;
}
There are multiple problems in your code:
[major] char string1[3] = "abc"; defines the destination array with a size of 3 bytes which does not have a enough space for the string "abc" including its null terminator and definitely not long enough to receive the extra characters from string2 at the end. Change this to char string1[7] = "abc";
[major] char string2[3] = "def"; defines the source array with a size of 3 bytes which does not have a enough space for the string "def" including its null terminator, hence will not be null terminated.
[major] string1[strlen(string1) - 1] = '\0'; overwrites the last character of abc, this not necessary.
[major] same remark for string2[strlen(string2) - 1] = '\0';
[minor] string2 should be defined as const char * in void strconcatenate(char *string1, char *string2)
[minor] i and j should be defined with type size_t
[minor] for compatibility with the standard function strcat, strconcatenate should have a return type of char * and return the pointer to the destination array. Yet, As commented by Jonathan Leffler, a more useful design for the return value is to return a pointer to the null at the end of the concatenated string. You already know where the start of the string is; that isn't very interesting information. But knowing where the end of the string is after concatenation — that is useful information which only the concatenate function is privy to. –
Here is a modified version:
#include <stdio.h>
#include <string.h>
char *strconcatenate(char *string1, const char *string2) {
char *p = string1 + strlen(string1);
while ((*p = *string1) != '\0') {
string1++;
p++;
}
return string1; /* strcat semantics */
//return p; /* alternative semantics */
}
int main() {
char string1[7] = "abc";
char string2[] = "def";
strconcatenate(string1, string2);
printf("Resultant string = %s\n", string1);
return 0;
}
Since string1 and string2 has 3 characters so you need to declare it with 4 bytes('\0') and also when you need not to subtract 1 from strlen because it points to '\0' character.
Below is the change to the code.
int main()
{
char string1[4] = "abc";
string1[strlen(string1)] = '\0';
char string2[4] = "def";
string2[strlen(string2)] = '\0';
strconcatenate(string1,string2);
printf("Resultant string = %s\n",string1);
return 0;
}
I have been doing well with the string functions I've been making recently. So then, I made my 3rd string function that is basically like strcpy():
#include <stdio.h>
#include <string.h>
const char *copy_str(const char *str);
int main() {
char name[256];
printf("What's your name? "); scanf("%255s", name);
char result[256] = copy_str(name);
printf("Hello, %s.\n", result);
}
const char *copy_str(const char *str) {
return str;
}
I didn't try using the strcpy() function itself in the function I made, because I'm not really familiar with it. Then I got an error:
copy_str.c:9:10: error: array initializer must be an initializer list or string literal
char result[256] = copy_str(name);
^
I fixed it this way:
int main() {
char name[256];
printf("What's your name? "); scanf("%255s", name);
char result[256];
strcpy(name, result);
printf("Hello, %s.\n", result);
}
But then the output went like this:
What's your name? builderman
Hello, .
No string for some reason. Even if you typed in a different string, it would be the same result.
Q: Why did strcpy() ruin my string? What ways can I improve my function?
I will try to answer your questions in parts.
copy_str.c:9:10: error: array initializer must be an initializer list or string literal
char result[256] = copy_str(name);
the meaning of this error is that in C syntax you cannot initialize an array the way that you did.
you can only initialize an array of chars with either a literal like so:
char name[] = "Hello";
or an initialization list like so:
char name[] = {'H', 'e', 'l', 'l', 'o'};
you can't initialize an array with a pointer to a char (which is the return value of your function)
strcpy doesn't magically copy an entire string to another one, it iterates over the string until it finds a NULL terminator '\0'
so a simple implementation of strcpy can be:
char *StrCpy(char *dest, const char *src)
{
char *returned_str = dest;
while (*src != '\0')
{
*dest = *src;
src++;
dest++;
}
*dest = '\0';
return returned_str;
}
so strcpy iterates over the strings and copies each character from src to dest.
a problem may arise when sending an array of characters that doesn't have a null terminator at the end of the array, which is how a string should be terminated in C.
The function strcpy has the following declaration
char *strcpy(char * restrict s1, const char * restrict s2);
That is the first parameter defines the array where the string pointed to by the second parameter is copied.
However in your program
int main() {
char name[256];
printf("What's your name? "); scanf("%255s", name);
char result[256];
strcpy(name, result);
printf("Hello, %s.\n", result);
}
you are copying the non-initialized character array result into the array name.
You have to write
strcpy( result, name );
instead of
strcpy(name, result);
As for the first program then the function copy_str is redundant. You could just write
char result[256] = name;
However the initializer has the type char * due to the implicit conversion of an array to pointer to its first element. So such initialization of an array is incorrect. You may initialize a character array with a string literal like
char s[] = "Hello";
But you may not use a character pointer to initialize a character array like
char t[] = "Hello";
char s[] = t;
Your function copy_str could look the following way
char * copy_str( char *dsn, const char *src )
{
for ( char *p = dsn; ( *p++ = *src++ ); );
return dsn;
}
I have recently begun working on learning the C language and have repeatedly run into an error in which calling the strcat function from the <string.h> module results in a segmentation fault. I've searched for the answers online, including on this stackoverflow post, without success. I thought this community might have a more personal insight into the problem, as the general solutions don't seem to be working. Might be user error, might be a personal issue with the code. Take a look.
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
char *buffer = malloc(strlen(new)+1);
for (int i=0; i<strlen(*str); i++){
if(buffer!=NULL){
if(str[i]!=" "){
strcat(new,str[i]); //Segmentation fault
}
}
}
free(buffer);
return new;
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
puts(new);
}
I've placed a comment on the line I've traced the segmentation fault back to. The following is some Java to make some sense out of this C code.
public class deblank {
public static void main(String[]args){
String str = "This has space in it.";
System.out.println(removeBlanks(str));
}
public static String removeBlanks(String str){
String updated = "";
for(int i=0; i<str.length(); i++){
if(str.charAt(i)!=' '){
updated+=str.charAt(i);
}
}
return updated;
}
}
Any insights into this error will be much appreciated. Please point out typos as well... I've been known to make them. Thanks.
OK, let's do this.
#include <stdio.h>
#include <string.h>
char * deblank(const char str[]){
char *new[strlen(str)];
^ This line creates an array of pointers, not a string.
char *buffer = malloc(strlen(new)+1);
malloc is undeclared. Missing #include <stdlib.h>. Also, you should check for allocation failure here.
strlen(new) is a type error. strlen takes a char * but new is (or rather evaluates to) a char **.
for (int i=0; i<strlen(*str); i++){
strlen(*str) is a type error. strlen takes a char * but *str is a char (i.e. a single character).
i<strlen(...) is questionable. strlen returns size_t (an unsigned type) whereas i is an int (signed, and possibly too small).
Calling strlen in a loop is inefficient because it has to walk the whole string to find the end.
if(buffer!=NULL){
This is a weird place to check for allocation failure. Also, you don't use buffer anywhere, so why create/check it at all?
if(str[i]!=" "){
str[i]!=" " is a type error. str[i] is a char whereas " " is (or rather evaluates to) a char *.
strcat(new,str[i]); //Segmentation fault
This is a type error. strcat takes two strings (char *), but new is a char ** and str[i] is a char. Also, the first argument to strcat must be a valid string but new is uninitialized.
}
}
}
free(buffer);
return new;
new is a local array in this function. You're returning the address of its first element, which makes no sense: As soon as the function returns, all of its local variables are gone. You're returning an invalid pointer here.
Also, this is a type error: deblank is declared to return a char * but actually returns a char **.
}
int main(void){
char str[] = "This has spaces in it.";
char new[strlen(str)];
*new = deblank(str);
This is a type error: *new is a char but deblank returns a char *.
puts(new);
puts takes a string, but new is essentially garbage at this point.
}
You can't use strcat like you did, it is intended to catenate a C-string at the end of another given one. str[i] is a char not a C-string (remember that a C-string is a contiguous sequence of chars the last being the NUL byte).
You also cannot compare strings with standard comparison operators, if you really need to compare strings then there is a strcmp function for it. But you can compare chars with standard operators as char is just a kind of integer type.
This should do the trick:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char * deblank(const char str[]) {
char *buffer = malloc(strlen(str)+1); // allocate space to contains as much char as in str, included ending NUL byte
for (int i=0, j=0; i<strlen(str)+1; i++) { // for every char in str, included the ending NUL byte
if (str[i]!=' ') { // if not blank
buffer[j++] = str[i]; // copy
}
}
return buffer; // return a newly constructed C-string
}
int main(void){
char str[] = "This has spaces in it.";
char *new = deblank(str);
puts(new);
free(new); // release the allocated memory
}
So, not sure whether this helps you, but a C code doing the same as your Java code would look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *removeBlanks(const char *str)
{
char *result = malloc(strlen(str) + 1);
if (!result) exit(1);
const char *r = str;
char *w = result;
while (*r)
{
// copy each character except when it's a blank
if (*r != ' ') *w++ = *r;
++r;
}
*w = 0; // terminate the result to be a string (0 byte)
return result;
}
int main(void)
{
const char *str = "This has spaces in it.";
char *new = removeBlanks(str);
puts(new);
free(new);
return 0;
}
I would'nt recommend to name a variable new ... if you ever want to use C++, this is a reserved keyword.
I tried compiling with warnings enabled, here are some you should fix.
You need to include stdlib.h
char *new[strlen(str)] creates an array of char* not of char, so not really a string. Change it to char new[strlen(str)].
To check if str[i] is a space, you compare it to the space character ' ', not a string whose only character is a space " ". So change it to str[i]!=' '
strcat takes a string as the second argument and not a character, like you're giving it with str[i].
Also, what are you using buffer for?
Another mistake, is that you probably assumed that uninitialized arrays take zero values. The new array has random values, not zero/null. strcat concatenates two strings, so it would try to put the string in its second argument at the end of the first argument new. The "end" of a string is the null character. The program searches new for the first null character it can find, and when it finds this null, it starts writing the second argument from there.
But because new is uninitialized, the program might not find a null character in new, and it would keep searching further than the length of new, strlen(str), continuing the search in unallocated memory. That is probably what causes the segmentation fault.
There can be three approaches to the task.
The first one is to update the string "in place". In this case the function can look something like the following way
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char s[] )
{
size_t i = 0;
while ( s[i] and not isblank( s[i] ) ) ++i;
if ( s[i] )
{
size_t j = i++;
do
{
if ( not isblank( s[i] ) ) s[j++] = s[i];
} while( s[i++] );
}
return s;
}
int main(void)
{
char s[] = "This has spaces in it.";
puts( s );
puts( deblank( s ) );
return 0;
}
The program output is
This has spaces in it.
Thishasspacesinit.
Another approach is to copy the source string in a destination character array skipping blanks.
In this case the function will have two parameters: the source array and the destination array. And the size of the destination array must be equal to the size of the source array because in general the source array can not have blanks.
#include <stdio.h>
#include <ctype.h>
#include <iso646.h>
char * deblank( char *s1, const char *s2 )
{
char *t = s1;
do
{
if ( not isblank( *s2 ) ) *t++ = *s2;
} while ( *s2++ );
return s1;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char s2[sizeof( s1 )];
puts( s1 );
puts( deblank( s2, s1 ) );
return 0;
}
The program output will be the same as shown above.
Pay attention to this declaration
char s2[sizeof( s1 )];
The size of the destination string in general should be not less than the size of the source string.
And at last the third approach is when inside the function there is created dynamically an array and pointer to the first element of the array is returned from the function.
In this case it is desirable at first to count the number of blanks in the source array that to allocated the destination array with the appropriate size.
To use the functions malloc and free you need to include the following header
#include <stdlib.h>
The function can be implemented as it is shown in the demonstrative program.
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <iso646.h>
char * deblank( const char *s )
{
size_t n = 1; /* one byte reserved for the terminating zero character */
for ( const char *t = s; *t; ++t )
{
if ( not isblank( *t ) ) ++n;
}
char *s2 = malloc( n );
if ( s2 != NULL )
{
char *t = s2;
do
{
if ( not isblank( *s ) ) *t++ = *s;
} while ( *s++ );
}
return s2;
}
int main(void)
{
char s1[] = "This has spaces in it.";
char *s2 = deblank( s1 );
puts( s1 );
if ( s2 ) puts( s2 );
free( s2 );
return 0;
}
The program output is the same as for the two previous programs.
As for the standard C function strcat then it cats two strings.
For example
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
puts( strcat( s1, s2 ) );
return 0;
}
The destination array (in this case s1) must have enough space to be able to append a string.
There is another C function strncat in the C Standard that allows to append a single character to a string. For example the above program can be rewritten the following way
#include <stdio.h>
#include <string.h>
int main(void)
{
char s1[12] = "Hello ";
char *s2 = "World";
for ( size_t i = 0; s2[i] != '\0'; i++ )
{
strncat( s1, &s2[i], 1 );
}
puts( s1 );
return 0;
}
But it is not efficient to use such an approach for your original task because each time when the function is called it has to find the terminating zero in the source string that to append a character.
you can try recursively
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void deblank(const char* str, char *dest) {
if (!*str) {*dest = '\0';return;}
// when we encounter a space we skip
if (*str == ' ') {
deblank(str+1, dest);
return;
}
*dest = *str;
deblank(str+1, dest+1);
}
int main(void) {
const char *str = "This has spaces in it.";
char *output = malloc(strlen(str)+1);
deblank(str, output);
puts(output);
free(output);
}
On const char * const str = ch; there is a warning:initialization makes pointer from integer without a cast.
If I change it to const char * const str = (char*)ch the warning will be cast to pointer from integer of different size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
FILE *file1;
char ch;
char array[100];
file1 = fopen("list.txt","r");
while((ch=fgetc(file1))!=EOF)
{
const char * const str = (char*)ch;
const char * const delim = "\n";
char * const dupstr = strdup(str);
char *saveptr = NULL;
char *substr = NULL;
int count = 0;
printf("original string is %s",dupstr);
substr = strtok_r(dupstr, delim, &saveptr);
do{
printf("#%d filename is %s\n", count++, substr);
substr = strtok_r(NULL, delim, &saveptr);
}
while(substr);
free (dupstr);
return 0;
}
fclose(file1);
return 0;
}
ch=fgetc(file1))!=EOF is incorrect because ch is a char, but EOF is an int. This is the very reason why fgetc and similar functions return an int. Easiest way to fix the current code is probably to use a temporary int, then copy that to a char inside the loop.
const char * const str = (char*)ch;. Casting from a character to a pointer doesn't make any sense. This is the reason for the warning. If you want to create a temporary string consisting of one character, you should do something like char str[2] = {ch, '\0'}. That way you don't have to use strdup either.
ch is a char (an integral type) and you try to convert it to a pointer type char * (a type that can store an address). These two types are of very different nature, and it is forbidden by the standard to do such. At least convert the address of ch : (char *)&ch.
Beware this will not save your code as you are trying to use a char as a C-string. Again these are of different kind. A char is just something that let you store the code value of a character. A C-string is a sequence of characters terminated by a NUL one.
Suggestions (we don't really know what you try to achieve) : use an array of chars, read a full line from your opened file with fgets, etc.
There are a lot of find/replace functions available on the internet, but i can't find why this is not working...( my own solution )
Here is what i tried
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* strrpl(char *str, char* find, char *replace)
{
int i;
char *pt = strstr(str, find), *firstStr;
firstStr = (char* )malloc(100 * sizeof(char));
// copy just until i find what i need to replace
// i tried to specify the length of firstStr just with pt - str
strncpy(firstStr, str, strlen(str) - strlen(pt));
strcat(firstStr, replace);
strcat(firstStr, pt + strlen(find));
for(i = 0; i < strlen(firstStr); i++)
str[i] = firstStr[i];
return str;
}
int main()
{
char *s, *s1, *s2;
s = (char* )malloc(100 * sizeof(char));
s1 = (char* )malloc(100 * sizeof(char));
s2 = (char* )malloc(100 * sizeof(char));
scanf("%s", s1);
scanf("%s", s2);
scanf("%s", s);
printf("%s", strrpl(s, s1, s2));
return 0;
}
The compilation gives me the error "segmentation fault" but i can't figure what memmory is he trying to alloc and he can't. I overrided a memory block or something? Please help :)
Thanks
I overrided a memory block or something?
You have:
A potential buffer overflow when you allocate firstStr. Who says the result will be less than 100 characters?
Another potential buffer overflow when you copy the answer back to the input string. Who says it will fit?
A potential buffer overflow each time you use scanf.
A memory leak each time you call malloc.
An inefficient implementation of strcpy just before return str;.
A crash (formally, undefined behaviour) when the input string does not contain the replacement string. strstr returns NULL when there is no match and you never check for it.
A potential issue with strncpy which leaves your string not NUL-terminated if there's not enough space for NUL.
Here is the immediate problem: when strstr returns NULL, your code does not pay attention. Add this line:
char *pt = strstr(str, find), *firstStr;
if (!pt) return str;
Another problem is that the call of strncpy is incorrect:
strncpy(firstStr, str, strlen(str) - strlen(pt));
it will leave firstStr unterminated, because str is longer than the substring being copied. The subsequent call
strcat(firstStr, replace);
will operate on a string that is not null-terminated, causing undefined behavior.
"Shotgun" approach to fixing it would be to use calloc instead of malloc to put zeros into firstStr. A more precise approach would be placing '\0' at the end of the copied substring.
With these fixes in place, your code runs OK (demo). However, there are several issues that need to be addressed:
You do not free any of the resources that you allocate dynamically - this results in memory leaks.
You do not compute how much memory to allocate - If a 5-character string is replaced for a 100-character string in a 100-character string, you overrun the temporary buffer.
You are using strncpy incorrectly - the function is intended for fixed-length strings. Use memcpy instead.
You are using strcat instead of memcpy or strcpy - this is inefficient.
You have not checked for the return value of strstr.
Try the below code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* strrpl(char *str, char* find, char *replace)
{
int i;
char *pt = strstr(str, find);
char *firstStr;
if(pt == NULL){
printf("cannot find string \n");
return NULL;
}
firstStr = (char* )malloc(100 * sizeof(char));
// copy just until i find what i need to replace
// i tried to specify the length of firstStr just with pt - str
strncpy(firstStr, str, strlen(str) - strlen(pt));
strcat(firstStr, replace);
strcat(firstStr, pt + strlen(find));
for(i = 0; i < strlen(firstStr); i++)
str[i] = firstStr[i];
return str;
}
int main()
{
char *s, *s1, *s2, *s3;
s = (char* )malloc(100 * sizeof(char));
s1 = (char* )malloc(100 * sizeof(char));
s2 = (char* )malloc(100 * sizeof(char));
s3 = (char* )malloc(100 * sizeof(char));
scanf("%s", s);//input string
scanf("%s", s1);//string to find
scanf("%s", s2);//string to replace
s3 = strrpl(s, s1, s2);
if(s3 != NULL)
printf("%s \n",s3);
return 0;
}