Unexpected segmentation fault when trying to mix some string - c

I need to manipulate an string in this way:
If the character is '+' or '-' or '/' or '*', move them to the end of buffer, if not, move to the beginning of the buffer.
My solution is quite simple:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void mix_the_string(char ** buff, char ** string)
{
printf("The string which will be printed is : %s\n",*string);
int i = 0;
int j = strlen(*string) - 1;
while(i< strlen(*string))
{
if(*string[i] != '+' || *string[i] != '-' || *string[i] != '*' || *string[i] != '/')
{
printf("in the first if, i = %d, *string[i] = '%d'\n",i,(int)*string[i]);
*buff[i] = *string[i];
}
else
{
printf("in the second if, i = %d, *string[i] = '%d'\n",i,(int)*string[i]);
*buff[j] = *string[i];
}
i++;
j--;
}
}
int main()
{
char * buff = (char *) malloc(50);
char * string = (char *) malloc(50);
string = "1+2+3";
mix_the_string(&buff,&string);
puts(buff);
free(buff);
free(string);
return 0;
}
The output of this code is:
The string which will be printed is : 1+2+3
in the first if, i = 0, *string[i] = '49'
in the first if, i = 1, *string[i] = '49'
Segmentation fault
I expected with this example that output would be like:
The string which will be printed is : 1+2+3
in the first if, i = 0, *string[i] = '49'
in the second if, i = 1, *string[i] = '43'
in the first if, i = 2, *string[i] = '50'
in the second if, i = 3, *string[i] = '43'
in the first if, i = 4, *string[i] = '51'
123++
Where am I going wrong?

There is difference between *string[i] and (*string)[i] notation. [] has higher precedence than * operator. You pass string by pointer to pointer, so you should call (*string)[i] in every line of your code.
(*string)[i] means - dereference pointer to array and get i-th element of string array
now you are doing
*string[i] - get i-th element of string array (!! but this is not array, this is only one element) and dereference first element
Do the same with buff.
And in main function you should copy 1+2+3 string literal into string buffer for example by strcpy function.

There are lot of bugs in your code.
first string = "1+2+3"; and then doing free(string) is not correct.
First copy it using strcpy(string,"1+2+3"); and then
free(string)
string is double pointer in mix_the_string(). Accessing like *string[i] is not correct because string is not array of pointer. instead of this use *(*string+i). Same applicable for buf also.
finally casting of malloc is not required. just char * buff = malloc(50); is fine.
And moreover you don't need to pass &string to mix_the_string(). just pass string thats enough.
Here is the expected code which you want
void mix_the_string(char ** buff, char ** string)
{
printf("The string which will be printed is : %s\n", *string);
int i = 0;
int j = strlen(string[0]) - 1;
while (i < strlen(string[0]))
{
if (*(*string + i) != '+' || *(*string + i) != '-' || *(*string + i) != '*' || *(*string + i) != '/')
{
printf("in the first if, i = %d, *string[i] = '%d'\n", i, *(*string + i));
*(*buff + i) = *(*string + i);
}
else
{
printf("in the second if, i = %d, *string[i] = '%d'\n", i, *(*string + i));
*(*buff + j) = *(*string + i);
}
i++;
j--;
}
}
int main()
{
char * buff = (char *)malloc(50);
char * string = (char *)malloc(50);
strcpy(string, "1+2+3");
mix_the_string(&buff, &string);
puts(buff);
free(buff);
free(string);
return 0;
}

Related

strcat adds junk to the string

I'm trying to reverse a sentence, without changing the order of words,
For example: "Hello World" => "olleH dlroW"
Here is my code:
#include <stdio.h>
#include <string.h>
char * reverseWords(const char *text);
char * reverseWord(char *word);
int main () {
char *text = "Hello World";
char *result = reverseWords(text);
char *expected_result = "olleH dlroW";
printf("%s == %s\n", result, expected_result);
printf("%d\n", strcmp(result, expected_result));
return 0;
}
char *
reverseWords (const char *text) {
// This function takes a string and reverses it words.
int i, j;
size_t len = strlen(text);
size_t text_size = len * sizeof(char);
// output containst the output or the result
char *output;
// temp_word is a temporary variable,
// it contains each word and it will be
// empty after each space.
char *temp_word;
// temp_char is a temporary variable,
// it contains the current character
// within the for loop below.
char temp_char;
// allocating memory for output.
output = (char *) malloc (text_size + 1);
for(i = 0; i < len; i++) {
// if the text[i] is space, just append it
if (text[i] == ' ') {
output[i] = ' ';
}
// if the text[i] is NULL, just get out of the loop
if (text[i] == '\0') {
break;
}
// allocate memory for the temp_word
temp_word = (char *) malloc (text_size + 1);
// set j to 0, so we can iterate only on the word
j = 0;
// while text[i + j] is not space or NULL, continue the loop
while((text[i + j] != ' ') && (text[i + j] != '\0')) {
// assign and cast test[i+j] to temp_char as a character,
// (it reads it as string by default)
temp_char = (char) text[i+j];
// concat temp_char to the temp_word
strcat(temp_word, &temp_char); // <= PROBLEM
// add one to j
j++;
}
// after the loop, concat the reversed version
// of the word to the output
strcat(output, reverseWord(temp_word));
// if text[i+j] is space, concat space to the output
if (text[i+j] == ' ')
strcat(output, " ");
// free the memory allocated for the temp_word
free(temp_word);
// add j to i, so u can skip
// the character that already read.
i += j;
}
return output;
}
char *
reverseWord (char *word) {
int i, j;
size_t len = strlen(word);
char *output;
output = (char *) malloc (len + 1);
j = 0;
for(i = (len - 1); i >= 0; i--) {
output[j++] = word[i];
}
return output;
}
The problem is the line I marked with <= PROBLEM, On the first word which in this case is "Hello", it does everything just fine.
On the second word which in this case is "World", It adds junky characters to the temp_word,
I checked it with gdb, temp_char doesn't contain the junk, but when strcat runs, the latest character appended to the temp_word would be something like W\006,
It appends \006 to all of the characters within the second word,
The output that I see on the terminal is fine, but printing out strcmp and comparting the result with expected_result returns -94.
What can be the problem?
What's the \006 character?
Why strcat adds it?
How can I prevent this behavior?
strcat() expects addresses of the 1st character of "C"-strings, which in fact are char-arrays with at least one element being equal to '\0'.
Neither the memory temp_word points to nor the memory &temp_char points to meet such requirements.
Due to this the infamous undefined behaviour is invoked, anything can happen from then on.
A possible fix would be to change
temp_word = (char *) malloc (text_size + 1);
to become
temp_word = malloc (text_size + 1); /* Not the issue but the cast is
just useless in C. */
temp_word[0] = '\0';
and this
strcat(temp_word, &temp_char);
to become
strcat(temp_word, (char[2]){temp_char});
There might be other issues with the rest of the code.
The root cause of junk characters is you use wrong input for the 2nd argument of strcat function. see explain below:
At the beginning of your function you declare:
int i, j;
size_t len = strlen(text);
size_t text_size = len * sizeof(char);
// output containst the output or the result
char *output;
// temp_word is a temporary variable,
// it contains each word and it will be
// empty after each space.
char *temp_word;
// temp_char is a temporary variable,
// it contains the current character
// within the for loop below.
char temp_char;
you can print variable's addresses in stack, they will be something like this:
printf("&temp_char=%p,&temp_word=%p,&output=%p,&text_size=%p\n", &temp_char, &temp_word,&output,&text_size);
result:
&temp_char=0x7ffeea172a9f,&temp_word=0x7ffeea172aa0,&output=0x7ffeea172aa8,&text_size=0x7ffeea172ab0
As you can see, &temp_char(0x7ffeea172a9f) is at the bottom of the stack, next 1 byte is &temp_word(0x7ffeea172aa0), next 8 bytes is &output(0x7ffeea172aa8), and so on(I used 64bit OS, so it takes 8 bytes for a pointer)
// concat temp_char to the temp_word
strcat(temp_word, &temp_char); // <= PROBLEM
refer strcat description here: http://www.cplusplus.com/reference/cstring/strcat/
the strcat second argument = &temp_char = 0x7ffeea172a9f. strcat considers that &temp_char(0x7ffeea172a9f) is the starting point of the source string, instead of adding only one char as you expect it will append to temp_word all characters starting from &temp_char(0x7ffeea172a9f) , until it meets terminating null character
The function strcat deals with strings.
In this code snippet
// assign and cast test[i+j] to temp_char as a character,
// (it reads it as string by default)
temp_char = (char) text[i+j];
// concat temp_char to the temp_word
strcat(temp_word, &temp_char); // <= PROBLEM
neither the pointer temp_word nor the pointer &temp_char points to a string.
Moreover array output is not appended with the terminating-zero character for example when the source string consists from blanks.
In any case your approach is too complicated and has many redundant code as for example the condition in the for loop and the condition in the if statement that duplicate each other.
for(i = 0; i < len; i++) {
//…
// if the text[i] is NULL, just get out of the loop
if (text[i] == '\0') {
break;
}
The function can be written simpler as it is shown in the demonstrative program below.
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
char * reverse_words( const char *s )
{
char *result = malloc( strlen( s ) + 1 );
if ( result != NULL )
{
char *p = result;
while ( *s != '\0' )
{
while ( isblank( ( unsigned char )*s ) )
{
*p++ = *s++;
}
const char *q = s;
while ( !isblank( ( unsigned char )*q ) && *q != '\0' ) ++q;
for ( const char *tmp = q; tmp != s; )
{
*p++ = *--tmp;
}
s = q;
}
*p = '\0';
}
return result;
}
int main(void)
{
const char *s = "Hello World";
char *result = reverse_words( s );
puts( s );
puts( result );
free( result );
return 0;
}
The program output is
Hello World
olleH dlroW

Insert character after every 3 characters in string

For every three characters copied from str1 to str2, the character ch is inserted into str2.
(Input1) Enter a string: abc de
(Input2) Enter a character to be inserted: #
Output: abc# de
Code:
void insertChar(char *str1, char *str2, char ch)
{
int i, j, count = 0, flag = 0;
char *ptr1, *ptr2, *ptr3;
ptr1 = str1; //Input string
ptr3 = &str2; //char string array output
for (i = 0, j = 0;*ptr1 != '\0'; ptr1++, i++, j++, ptr3++)
{
str2[j] = str1[i];
if (*ptr1 == ' ' && flag != 1)
++count;
if (flag != 1 && count%3)
{
flag = 1;
for(ptr2 = ch;*ptr2 != '\0'; ptr2++)
{
str2[++j] = *ptr2;
ptr3++;
}
str2[++j] = ' ';
ptr3++;
}
}
str2[j] = '\0';
}
However my code is unable to run. May I know what could be the issue?
Like mentioned in the comment, there are some issues with the code.
ptr3 = &str2
In this line, you are not assigning the string to ptr3. Rather you are assigning the address of the pointer that contains the start address of the string. For example: Suppose the first character in your string is located in address location 1000. Then, str3 contains the value 1000. However, str3 itself will be located somewhere else. Let's suppose it is located at 2000. Then, ptr3 contains the value 2000 and after increment, it will point to 2001 and so on. Thus, you get wrong and dangerous values.
There is also a problem in the line for(ptr2 = ch;*ptr2 != '\0'; ptr2++). You are assigning the value of ch to ptr2. This should give you a warning. Again, the memory pointed to by ptr2 is changed. So, *ptr2 tries to dereference that memory location.
The code I would use for this:
void insertChar(char *str1, char *str2, char ch) {
int count = 0;
while (*str1) {
*str2++ = *str1++;
++count;
if (count == 3) {
*str2++ = ch;
count = 0;
}
}
*str2 = '\0';
}
Some recommendations:
Try to declare the variables as close to their usage as possible.
Use -Wall option when compiling your programs

choosing between function returns

I have written a function which has two different options of what to return:
//function to compare the strings in function3
int compare(char* str, char* dest){
int answer;
int i;
int length;
length = strlen(str);
// int jlength;
// jlength = strlen(dest);
for(i=0; i<length; i++){
if(str[i] == dest[i]){
answer = 1;
}else {
answer = 2;
}
}
return answer;
}
I want to use this function later, and have different things happen depending on what the function has returned. Below are the relevant parts of how I have constructed that:
//compare the reversed str with the orignal, now stored in dest
compare(str, dest);
int function3answer;
if(compare == 1){
function3answer = 1;
}else{
function3answer = 2;
}
return function3answer;
}
When I compile I get the error:
warning: comparison between pointer and integer [enabled by default]
Adding single quotes around the 1 does not help (and also isn't really what I want because I am not referencing part of an array) nor does taking it down to one equals sign (this produces a different warning).
Thanks so much!
The error
warning: comparison between pointer and integer [enabled by default]
Comes from this line:
if(compare == 1){
You try to compare a function to an integer.
To get rid of this error, change the function using compare:
void some_function(...) {
//compare the reversed str with the orignal, now stored in dest
int compare_result = compare(str, dest);
int function3answer;
if(compare_result == 1){
function3answer = 1;
}else{
function3answer = 2;
}
return function3answer;
}
Now, the compare function. The way you implement it won't work as you expect:
If you compare "abc" and "poc", you will have in for loop:
i = 0, str[0] == 'a', dest[0] == 'p' ==> answer = 2
i = 1, str[0] == 'b', dest[0] == 'o' ==> answer = 2
i = 2, str[0] == 'c', dest[0] == 'c' ==> answer = 1
i = 3, going out for loop and returning **1**.
Worst, if you compare "a long string" with "tiny", you will get an UB when i is 4.
You could correct the compare function this way:
#define STRING_IDENTICAL 1
#define STRING_DIFFERENT 2
//function to compare the strings in function3
int compare(char* str, char* dest)
{
int answer;
int i;
int length;
length = strlen(str);
int jlength;
jlength = strlen(dest);
if (length != jlength)
{
/* since size are differents, string are differents */
return STRING_DIFFERENT;
}
for(i=0; i<length; i++){
if(str[i] != dest[i]){
/* at least one different character, string are differents */
return STRING_DIFFERENT;
}
}
/* if we reach this point, that means that string are identical */
return STRING_IDENTICAL;
}

How to swap string in C?

I am trying to make a function in C which will swap two string variables but something went wrong and the program crashes.
Please have a look at my code and tell me where I made a mistake:
#include <string.h>
void strswap(char name1[], char name2[]) // to swap two strings
{
int lengthname1, lengthname2;
lengthname1 = strlen(name1);
lengthname2 = strlen(name2);
char temporaryname1[100];
char temporaryname2[100];
int x;
int y;
// till just the declaration
for (int x = 0; x < lengthname1; lengthname1++) {
temporaryname1[x] = name1[x];
name1[x] = ' ';
}
// copying the value of name1 in temporaryname1
for (int y = 0; y < lengthname2; lengthname2++) {
temporaryname2[x] = name2[x];
name2[x] = ' ';
}
// copying the value of name2 in temporaryname2
for (int x = 0; x < lengthname1; lengthname1++) {
name1[x] = temporaryname2[x];
}
for (int y = 0; y < lengthname2; lengthname2++) {
name2[x] = temporaryname1[x];
}
}
#include <stdio.h>
int main()
{
char name[] = "hello";
char name2[] = "hi";
printf("before swapping: %s %s\n", name, name2);
strswap(name, name2);
printf("after swapping: %s %s\n", name, name2);
}
EDIT:- I have corrected the program and it is working properly. Soon my header file is going to work with some other modules. Thank you all for your help and especially to #Micheal
There are many issues:
First issue
The x variable is not initialized:
int x; int y; // first declaration of x
// till just the declaration
for(int x=0;x<lengthname1;lengthname1++)
{// ^ second declaration of x , local to the loop
temporaryname1[x]=name1[x];
name1[x]=' ';
}
// if you use x here it's the first x that has never been initialized
Second issue
This:
for (x = 0; x<lengthname1; lengthname1++)
should be:
for (x = 0; x<lengthname1 + 1; x++)
Why lengthname1 + 1 ? Because you need to copy the NUL char that terminates the string.
There are similar problems in your other for loops as well.
For example here you use y as loop variable, but in the loop you use x:
for (int y = 0; y<lengthname2 + 1; lengthname2++)
{
name2[x] = temporaryname1[x];
Third issue
In main you declare this:
char name[] = "hello";
char name2[] = "hi";
That is actually the same as
char name[6] = "hello"; // 5 chars for "hello" + 1 char for the terminating NUL
char name2[3] = "hi"; // 2 chars for "hi" + 1 char for the terminating NUL
Now even if your strswap is correct, you are trying to stuff the 6 bytes from the name array ("hello") into the 3 bytes array name2, there is not enough space in the name2 array. This is undefined behaviour.
And last but not least:
This is simply useless:
name1[x] = ' ';
And finally
You should ask yourself why you need two temporary strings (temporaryname1 and temporaryname2) in strswap() - one is enough.
void strswap(char ** name1, char ** name2)
{
char * name1_1 = malloc(strlen(*name2) + 1);
char * name2_1 = malloc(strlen(*name1) + 1);
strncpy(name1_1, *name2, strlen(*name2) + 1);
strncpy(name2_1, *name1, strlen(*name1) + 1);
*name1 = name1_1;
*name2 = name2_1;
}
int main()
{
char * name="hello";
char * name2="hi";
printf("before swapping %s %s\n",name,name2);
strswap(&name, &name2);
printf("after swapping %s %s\n",name,name2);
return 0;
}
Actually following is the safest way to swap two strings. In your cases, you were statically using strings of size 100 in size, which is not good and work in all case. Moreover, You tried to use ' ' instead of '\0' to mark end of string. String APIs uses '\0' to indicate that string has ended.

I have a string str which has two array (can be more) that I would like to convert to integer arrays in C

As an Example,
Input:
c[] = "[1,2,3][5,7,8]"
Output:
a = [1,2,3] //of type int a[]
b = [5,7,8] //of type int b[]
I have tried using strtok to remove "]". But, when I use strtok next time, I am not able to use it. If I try to print the output, I get
[1,2,3
[1
2
3
instead of
[1,2,3
[1
2
3
[5,7,8
[5
7
8
Code that I have so far
char c[] = "[1,2,3][5,7,8]";
char *token = strtok(c, "]");
for (token; token != NULL ; token = strtok(NULL, "]")){
puts(token);
char *comma = strtok(c, ",");
for (comma; comma != NULL; comma = strtok(NULL, ",")){
puts(comma);
}
}
Your problem is, that strtok has a memory. First time that you pass in a string, it is remembered and then used again and again as long as you pass in NULL as first parameter.
However, within your loop, you call strtok again with a parameter. So this new string (which is the first token only) is placed in strtok's memory, and after it is processed completely in the inner loop, there is nothing left to tokenize in the outer loop.
Have a look at this thread, it explains more detailed how strtok works.
However, you are lucky: strtok is manipulating the string you first passed in place (this is why you have to pass the string to be tokenized as char*, but the delimiters can be a const char*). So you can do this:
char c[] = "[1,2,3][5,7,8]";
char* next = c;
char* token;
while((token = strtok(next, "]")))
{
puts(token);
next += strlen(token) + 1; // move next behind the token
token = strtok(token, ",");
do
{
puts(token);
}
while((token = strtok(NULL, ",")));
}
If you are wondering about the extra parentheses, these are to prevent a warning in the compiler ("possible assignment instead of comparison").
If you are converting a string of character digits to an array of integer values, one character per value (or allowing a - before any character digit to indicate a negative value for your array), you may be better off writing a simple function to step though the string and perform your conversions manually.
An example using array indexing of the string could be written a follows. You could easily change the array index notations to pointer notation which is more intuitive to some.
#include <stdio.h>
#include <string.h>
size_t str2arr (char *d, size_t max, char *s, size_t *ofs);
int main (int argc, char **argv) {
char c[] = "[1,2,3][5,7,8]";
char *p = argc > 1 ? argv[1] : c;
size_t i, offset = 0, na = 0, nb = 0, nchr = strlen (p);
char a[nchr], b[nchr];
memset (a, 0, nchr * sizeof *a); /* zero each VLA */
memset (b, 0, nchr * sizeof *b);
na = str2arr (a, nchr, p, &offset); /* convert first segment */
nb = str2arr (b, nchr, p + offset, &offset); /* convert second segment */
for (i = 0; i < na; i++) /* output results */
printf (" a[%2zu] : % d\n", i, a[i]);
putchar ('\n');
for (i = 0; i < nb; i++)
printf (" b[%2zu] : % d\n", i, b[i]);
putchar ('\n');
return 0;
}
/** convert a string of characters to an array of values
* including accounting for negative values. the destination
* index `di` returns the number of characters conversions, the
* offset of the next segment within 's' is updated in pointer 'ofs'
*/
size_t str2arr (char *d, size_t max, char *s, size_t *ofs)
{
if (!d || !s || !*s) return 0; /* validate input */
size_t di = 0, neg = 0;
register size_t si = 0;
for (; di < max && s[si]; si++, di++) { /* for each character */
if (s[si] == ']') break;
while (s[si] && (s[si] < '0' || ('9' < s[si]))) { /* if not digit */
if (s[si] == '-') neg = 1; /* if '-' sign, set flag */
else neg = 0; /* clear if not last before digit */
si++;
}
if (!s[si]) break; /* validate not end of string */
d[di] = neg ? -(s[si] - '0') : s[si] - '0'; /* convert to digit */
neg = 0; /* reset flag */
}
*ofs = si + 1; /* update offset before return */
return di; /* return number of conversions */
}
Example Use/Output
$ ./bin/str2arr
a[ 0] : 1
a[ 1] : 2
a[ 2] : 3
b[ 0] : 5
b[ 1] : 7
b[ 2] : 8
$ ./bin/str2arr "[1,2,3,4][5,6,-5,7,-1,8,9,2]"
a[ 0] : 1
a[ 1] : 2
a[ 2] : 3
a[ 3] : 4
b[ 0] : 5
b[ 1] : 6
b[ 2] : -5
b[ 3] : 7
b[ 4] : -1
b[ 5] : 8
b[ 6] : 9
b[ 7] : 2
Look it over, compare this approach to the other answers. In C, you have as much fine-grain-control over how you parse data as you want to exercise. If you have no need to handle negative values, then the implementation is much simpler. Let me know if you have any questions.
This solution has two nested loops of strtok_s, because strtok is not re-entrant. This is MSVC, some systems implement the similar strtok_r.
I have created output in accordance with the top of your question, this can be modified to suit other output, it was not very clear. In this case, it was not really necessary to have two nested loops, but your subsequent examples confuse the issue by breaking up the comma'ed input.
#include <stdio.h>
#include <string.h>
int main(void) {
char c[] = "[1,2,3][5,7,8]";
char *tok1 = NULL;
char *tok2 = NULL;
char *end1 = NULL;
char *end2 = NULL;
int comma = 0;
char identifier = 'a';
tok1 = strtok_s(c, "[]", &end1);
while(tok1 != NULL) { // outer loop splitting [bracket] parts
printf("%c = [", identifier);
comma = 0; // control comma output
tok2 = strtok_s(tok1, ",", &end2);
while(tok2 != NULL) { // inner loop splitting ,comma, parts
if(comma) { // check if comma required
printf(",");
}
printf("%s", tok2);
comma = 1; // a comma will be needed
tok2 = strtok_s(NULL, ",", &end2);
}
printf("] //of type int %c[]\n", identifier);
identifier++;
tok1 = strtok_s(NULL, "[]", &end1);
}
return 0;
}
The simpler program where you don't need to examine within the [brackets] is
#include <stdio.h>
#include <string.h>
int main(void) {
char c[] = "[1,2,3][5,7,8]";
char *tok = NULL;
char identifier = 'a';
tok = strtok(c, "[]");
while(tok != NULL) {
printf("%c = [%s] //of type int %c[]\n", identifier, tok, identifier);
identifier++;
tok = strtok(NULL, "[]");
}
return 0;
}
In both cases the output is:
a = [1,2,3] //of type int a[]
b = [5,7,8] //of type int b[]
EDIT altered the second example to give output as per OP's recent comment above.
#include <stdio.h>
#include <string.h>
int main(void) {
char c[] = "[1,2,3][5,7,8]";
char *tok = NULL;
char identifier = 'a';
tok = strtok(c, "[]");
while(tok != NULL) {
printf("int %c[] = { %s };\n", identifier, tok, identifier);
identifier++;
tok = strtok(NULL, "[]");
}
return 0;
}
Program output:
int a[] = { 1,2,3 };
int b[] = { 5,7,8 };

Resources