why is this C union of strings function not working - segfault? - c

This is a function to find union of strings.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
char* my_union(char* param_1, char* param_2)
{
char *res[strlen(param_1) + strlen(param_2)]; //allocate long enough string
//check if the letter is in result res string
for(int i = 0 ; i < strlen(param_1);i++){
if(strchr(*res,param_1[i]) == NULL){// this checks for duplicates
res[i] = param_1[i];
}
}
for(int i = 0 ; i < strlen(param_2);i++){
if (strchr(*res, param_2[i])== NULL){//this checks for duplicates too
*res[i] = param_2[i];
}
}
printf("%s\n", *res);
return *res;
}
int main(){
char *s1 = "zpadinton" ;
char *s2 = "paqefwtdjetyiytjneytjoeyjnejeyj";
my_union(s1,s2);// must return "zpadintoqefwjy"
//the union is zpadintoqefwjy
return 0;
}

Some fundamental bugs:
char *res[] is an array of pointers. You don't want that.
strlen(param_1) + strlen(param_2) is not long enough, you didn't allocate space for the null terminator.
return *res; returning a pointer to a local variable is always wrong, because that variable goes out of scope when the function returns.
You either need to let the called do the allocation and write to one of the passed parameters, or you need to allocate memory for the string dynamically.
Notably, since you incorrectly used an array of pointers, stuff like res[i] = param_1[i]; should not compile cleanly. You will get warnings "assignment from incompatible type"/"pointer from integer without a cast" or something like that.
Always read and correct warnings. For a beginner, a warning is pretty much 100% certain equal a bug. Crank up your warning level to the max and even better, block invalid code from compiling in the first place. For example gcc/clang/icc: -std=c11 -pedantic-errors -Wall -Wextra -Werror.

char *res[strlen(param_1) + strlen(param_2)]; //allocate long enough string
You cannot allocate dynamic length on stack. You need to allocate it on heap, like this:
char *res = malloc(strlen(param_1) + strlen(param_2)+1);
(Note: add an extra byte for the null terminator)
You need to set the buffer to zeros to be able to use strchr later:
memset(res, 0, strlen(param_1) + strlen(param_2)+1);
if(strchr(*res,param_1[i]) == NULL){// this checks for duplicates
res[i] = param_1[i];
}
strchr should take res, not *res.
Assignment to index i would not work, as you need to add the new character at the end of the result string.
int j=0;
for(int i = 0 ; i < strlen(param_1);i++){
if(strchr(res,param_1[i]) == NULL){// this checks for duplicates
res[j] = param_1[i];
j++;
}
}
for(i = 0 ; i < strlen(param_2);i++){
if (strchr(res, param_2[i])== NULL){//this checks for duplicates too
res[j] = param_2[i];
j++;
}
}
printf("%s\n", res);
return res;

Related

Removing similar letters on a word using pointers

i just want to ask,whats wrong with my code and why does it say segmentation fault(core dumped)? Im trying to sift all the similar letters and only print the dissimilar ones. Here's my code(with skeleton code from my prof) And here's the original instruction: "to remove all occurrences of c in s and returns the result."
#include <stdio.h>
char* clean(char* s,int c);
int main()
{
clean("banana",'x');
return 0;
}
char* clean(char* s,int c)
{
for(int i = 0; i < 6; i++)
{
if(s[i] != c)
{
printf("c",s);
s[i]++;
}
}
return s;
}
Because in
s[i]++;
s refers to "banana" (a string literal), and you're trying to modify it. Modifying a string literal has undefined behavior, and on many systems it'll just crash because the compiler places string literals in read-only memory.
With gcc you can use -Wwrite-strings to get warnings about code like that.
The segmentation fault
You get a segmentation fault because the string "banana" is a literal, which means that it is read-only.
In your code, you try to do s[i]++, which increases the value of one of the characters.. Which you can't do, because of it being read-only. without it your code compiled and ran fine.
The solution
Now to answer your question. You specified the solution should return the result, not print it out, and since the literal is read-only, we need to create a new string. I chose to use malloc so that the size of the new string matches that of the old one, but you can also allocate a fixed buffer.
The following code prints "bnn":
#include <stdio.h>
#include <string.h> // For strlen
#include <stdlib.h> // For malloc & free
char* clean(char* s, int c);
int main() {
char* result = clean("banana", 'a');
printf("%s\n", result);
free(result); // Everything allocated with malloc must be freed.
return 0;
}
char* clean(char* s, int c) {
int length = strlen(s), i = 0, j = 0;
char* result = malloc(length + 1); // +1 for null terminator.
memset(result, 0, length + 1);
for (i; i < length; i++) {
if (s[i] != c) {
result[j] = s[i];
j++;
}
}
return result;
}

c function convert "fffoootoo" to "foto" (leaves out following repeating characters)

The task would be to remove following characters that are repeating from a char array, like "deeeciddeee" -> "decide" or "phhhonne" -> "phone".
I have a function that crashes the console, and I can't spot the bug:
char* my_unique(char *first, char *last) {
char* ret=first;
for(int i=0; first+i!=last; i++){
if(first[i]==first[i+1]){
for(int j=i; first+j!=last; j++)
first[j]=first[j+1];
last--;
}
}
return ret;
}
it is called this way:
char* a="oooat";
a=my_unique(a, a+strlen(a));
cout<<a;
please help me!
Besides a small bug (you should add the line i--; after last--;, because you're deleting the character at possition i, so what has been the character at i+1 became the new character at possition i. If you don't decrease i, it will be increased and you jump over a character) the code runs perfectly fine IF it is called with
const char* b = "oooat";
char* a = new char[strlen(b) + 1];
for (size_t c = 0; c < strlen(a) + 1; c++) { a[c] = b[c]; }
a = my_unique(a, a + strlen(a));
cout << a;
delete[] a;
Notice that I've used a edit-able copy of the string, as the literal itself is of type const char* and therefor can't be changed at all. And as I said, this works perfectly fine and prints "oat", just as expected, without any crash. So your problem might be that you try to edit a const string literal? In that case you might consider to copy it, as I did, or use std::string (if you code in C++).
There are many beginner mistakes in the code.
Let me point you one by one.
char* a="oooat";
a=my_unique(a, a+strlen(a));
cout<<a;
When you declare a string like this : char* a="oooat", a is a string literal. The memory for the string is allocated into text section of the program. Which basically means you cannot modify the values inside the strings. You can only read from them. Hence when you are passing pointer a to the function and modifying it, it will result in segmentation fault(Illegal access to memory).
Why do you need a ret pointer here? char* ret=first;
You are passing a pointer and modifying the value inside it. Hence the new data will be reflected in the calling function and we need not return it explicitly. So, it is redundant.
Overall logic can be simplified as well
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MYSTR "ooooat"
void my_unique(char *first, char *last) {
int size = last - first;
int i = 0, j = 0, k = 0;
for (; i < size; i++, j++) {
first[j] = first[i];
// Continue to check how many repetitions are there
while (i + 1 < size && (first[i] == first[i+1])) i++;
}
// In the end terminate the string with a NULL.
first[j] = '\0';
return;
}
int main()
{
char a[] = MYSTR;
my_unique(a, a+strlen(a));
printf("%s", a);
return 0;
}
This is in C. There are simpler ways of doing this in C++, and the code can definitely be condensed but has been left simpler for readability.
#include <stdlib.h>
char* fix(char *input) {
char *lookahead = input;
char *newchar, *ret;
// Determine Max Return String Length
int len = 0;
while (*lookahead != '\0') {
len++;
lookahead++;
};
// allocate max possible memory needed and set the pointers
ret = malloc(len);
newchar = ret;
lookahead = input;
*newchar = *lookahead; // copy the first character
while (*lookahead != 0) {
lookahead++; // incrementing this ptr first starts lookahead at 2nd character and
// ensures the null terminator gets copied before the while loop ends
if (*newchar != *lookahead) { // only copy new characters to new return string
newchar++;
*newchar = *lookahead;
};
};
return ret;
};
I'll try to give my answer so that it makes the as little changes as possible to your original code, while using the simplest methods.
The main problem has already been identified by the previous comments - you cannot alter a string literal.
Also, the line of code
i--;
has to be placed as well, with the reason well clarified above.
While making an editable version of the string may be a good way of fixing the problem, a more straightforward way would be to make it a local string, as such :
char b[] = "oooat";
but doing this will make it incompatible with the return type of your my_unique function (char*). But why would you need a return type in the first place, if you are fixing the string itself?
My final code would look like this :
void my_unique(char *first, char *last) {
char* ret=first;
for(int i=0; first+i!=last; i++){
if(first[i]==first[i+1]){
for(int j=i; first+j!=last; j++)
first[j]=first[j+1];
last--;
i--;
}
}
}
making the function return void.
Hope this helps.

Int to char array as a function returning an array of char the simple way

I have been looking on internet for this and so far i just found a lot of questions for specific answer and not a general one.
i am kind of rusty on C. And i want to make a function that will return an array of char.
this is what i got and is not working. basically a way to convert a byte array to an array of chars to do atoi later..
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char *get_char(int my_byte[], int packetsize)
{
char *array_char=(char *) malloc(sizeof(char)*10); //trying this but didnt work
// char array_char[10]; //i had it like this before(was told to do it)
for(int i=0;i<10;i++)
{
array_char[i]=my_byte[i]+0;
}
return array_char;
}
int main()
{
int byte_array[]={1,2,3,4,5,6,7,8,9,0};
char *temp;
char data;
temp=get_char(byte_array,10);
data=*temp;
printf("String point %s ",data);
}
Two fixes:
As you want to convert to char, then
array_char[i]=my_byte[i]+0; should be array_char[i]=my_byte[i]+'0'; Note '0' is character (that will be converted to int) instead of numeric 0 (which doesn't do anything).
Also you must free temp pointer in main as that memory is dynamically allocated in get_char() function.
Edit: just notice another issue in your get_char()
char *array_char=(char *) malloc(sizeof(char)*10);
should be
char *array_char= malloc(sizeof(char)*(packetsize+1));
After the for loop, ensure the buffer is NUL-terminated:
array_char[packetsize] = '\0';
Notice that your packetsize is never used - you should get some compiler warning about it. It's bad to hard code 10 in your malloc - it's actually the whole idea of parsing the packetsize as a parameter - so use it properly.
You need to watch out for these things:
You need to add a null-terminating character at the end of *array_char, otherwise using this pointer allocated from the heap will cause undefined behaviour.
You can simply allocate *array_char like this:
char *array_char = malloc(packetsize+1);
As sizeof(char) is 1, and +1 for trailing nullbyte.
You also don't need to cast return of malloc().
Instead of passing 10 as packetsize to get_char(), you should pass this size as sizeof(arr) / sizeof(arr[0], which is the calculated size of the array. This can be a size_t variable declared somewhere or even a macro.
malloc() needs to be checked, as it can return NULL if unsuccessful.
You need to free() temp at some point in the program.
array_char[i]=my_byte[i]+0; needs to be array_char[i]=my_byte[i]+'0'; instead, as '0' is the ascii code for a zero character.
char data needs to be char *data, as temp is a pointer.
If you compile with -Wall -Wextra, you will see that this line:
data=*temp;
Is dangerous, and will trigger warnings of making pointers from integers without a cast. It will most likely lead to a segmentation fault. If temp and data are both pointers, then you can simply use:
data=temp;
Which sets data to the address of temp. Sometimes this is written as data = &(*temp);, but this is harder to read. Although their is no need for data, and using temp alone should be fine.
Your code can then look like this:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))
char *get_char(int my_byte[], size_t packetsize) {
char *array_char = malloc(packetsize+1);
const char ascii = '0';
size_t i;
if (!array_char) {
printf("Cannot allocate %zu bytes\n", packetsize+1);
exit(EXIT_FAILURE);
}
for(i = 0; i < packetsize; i++) {
array_char[i] = my_byte[i] + ascii;
}
array_char[i] = '\0'; /* or array_char[packetsize] = '\0' */
return array_char;
}
int main(void) {
int byte_array[]={1,2,3,4,5,6,7,8,9,0};
char *temp, *data;
temp = get_char(byte_array, ARRAYSIZE(byte_array));
data = temp;
printf("String point %s\n", data);
printf("String converted into number = %d\n", atoi(data));
free(temp);
temp = NULL;
return 0;
}
You can also look into strtol, which is better than using atoi() in terms of error checking.
It is Not Wise Idea to Return a Array From A Function. So how to return a string then? As most of libc functions use we can use some thing like that (i.e) passing a buffer along with our input and expect function to use output buffer to give us result.
Some issue to take care while coding
write your logic first.
try to use available functions from libc.
while dealing with byte data/binary data be take precaution of buffer overflow.
don't allocate in a function and de-allocate in another function.
Below is Example of your code with modification.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include <stdint.h>
int get_char(uint8_t my_byte[], int packetsize, char *buffer, int max_buffer)
{
int byte_itr, buf_itr;
char temp_buf[16]={0x00};
for(byte_itr=0, buf_itr=0; byte_itr<packetsize && max_buffer > buf_itr; byte_itr++)
{
memset(temp_buf, 0x00, sizeof(temp_buf));
char temp_ch = my_byte[byte_itr];
snprintf(temp_buf, sizeof(temp_buf), "%d", temp_ch);
if( buf_itr+strlen(temp_buf) >=max_buffer){
break;
}else{
buf_itr += strlen(temp_buf);
strcat(buffer, temp_buf);
if(byte_itr+1 < packetsize){
strcat(buffer, ",");
buf_itr += 1;
}
}
}
return buf_itr;
}
int main()
{
uint8_t byte_array[]={1,2,3,4,5,6,7,8,9,0};
char char_array[32]={0x00};
int len = get_char(byte_array, 10, char_array, sizeof(char_array));
printf("String point %s : len %d\n", char_array, len);
}
NOTE:
when length return and size of output buffer same then buffer full condition happened.

Concatenate string with error

I have this code in C but I don't know why I get an error like Argument of type “char” is incompatible with parameter of type const char*
char number_string[size] = { NULL };
int counter = 0;
for (counter = 0; counter < strlen(input_string - 1); counter++)
{
temp = input_string[counter];
if (isdigit(temp))
{
strcat(number_string, temp); //temp variable has the error only in this line
}
}
You're mixing char and char *. The declaration of number_string[] might not warn you, because NULL may simply be defined as 0, which is legal as a char and as a pointer. But the variable temp is definately a problem: You don't show its declaration, but its first assignment makes it a char, and its second use in strcat assumes it is a char *.
If you want to add a single character at a time to a string, you'll have to do it by hand, something like this:
int nslen = strlen(number_string);
for ...
number_string[nslen++] = temp
number_string[nslen] = '\0';
char *strcat(char *dest, const char *src); expects its last argument to have type const char *
temp is a variable of type char.
So you're getting the error Argument of type “char” is incompatible with parameter of type const char*.
You could try something like this...
strncat(number_string, &temp, 1)
I usually favor something like...
sprintf (buffer, "%s%c", number_string, temp)
This is not an answer to your problem with types; it is a suggestion to tackle the problem in another way.
You want to fill a string with all numeric digits from the input string. There is no standard function to do this.
You could use strcat, but that function operates on strings, which must be zero-terminated. You could create a temporary string of two chars – one digit and one null-terminator – but that would be ineffective. strcat also requires you to ensure that you don't overflow the char buffer that you append to.
In cases like yours, it is usually easier to tackle the problem on a low level, where you create the char array yourself, one character at a time. For example, you can iterate through the input string with i and copy all digits to the number string by means of a second index, j:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int main()
{
size_t size = 12;
char number[size];
const char *input = "Alpha 123/Bravo 456/Charlie 789/Delta 007";
int i, j;
j = 0;
for (i = 0; input[i] != '\0'; i++) {
if (isdigit((unsigned char) input[i]) && j + 1 < size) {
number[j++] = input[i];
}
}
number[j] = '\0';
puts(number);
return 0;
}
Note how the code keeps track of the characters in the number string and how it takes care not to overflow the buffer. The number string may be truncated, but it will always be null-terminated.
I've also used input[i] != '\0' to detect the end of the string (which is, by definition, the null terminator '\0') instead of calling strlen(input), which always starts looking for the terminator fro the beginning of the string.
There are multiple problems in your code:
calling strlen(input_string - 1) most likely invokes undefined behaviour. You probably meant strlen(input_string) - 1 which would still cause the loop to run too far if input_string is an empty string;
calling strlen for each iteration is inefficient anyway;
calling isdigit(temp) is incorrect if temp is a char and char is signed by default;
strcat cannot be used the way you call it;
you should check for potential buffer overflow if all digits do not fit in the destination array.
Here is a much simpler function to extract all digits from the input_string:
char number_string[size];
int i, j;
for (i = j = 0; input_string[i] != '\0'; i++) {
unsigned char uc = input_string[i];
if (j < size - 1 && isdigit(uc)) {
number_string[j++] = uc;
}
}
number_string[j] = '\0';

structs with flexible char array member

I’m studying flexible array members. I've written the code below based on a 2 line example in the book I'm studying from. The code compiles with gcc -Wall with no errors and also executes without error.
However I don’t know what the (n) at the end of this malloc call is for. I assume if I'm storing a string the the flexible array, I'm supposed to call strlen() on the string and use the returned value for (n). The code seems to work no matter what value I assign to (n) and even works when there is no (n).
struct vstring *str = malloc(sizeof(struct vstring) + n);
Is the value needed or not?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct vstring{
int len;
char chars[]; /* c99 flexible array member to store a variable string */
};
int main()
{
char input_str[20];
int n = 0; /* what should n be it doesn’t seem to matter what value I put in here */
struct vstring * array[4]; /* array of pointers to structures */
int i = 0;
while ( i < 4 )
{
printf("enter string :");
scanf("%s",input_str);
struct vstring *str = malloc(sizeof(struct vstring) + n );
strcpy(str->chars,input_str);
str->len = strlen(input_str);
array[i] = str;
i++;
}
for ( i = 0 ; i < 4 ; i++) {
printf("array[%d]->chars = %s len = %d\n", n, array[i]->chars, array[i]->len);
}
return 0;
}
Yes, you need to allocate enough memory to store your string. So n on your case should be
strlen(input_str)+1.
What you are doing is writing into unallocated memory and invoking undefined behaviour. The code might work, but it is wrong.
You also have a typo(?) in your malloc call. It should be
struct vstring *str = malloc( sizeof(struct vstring) + n );
And don't forget that inputting more than 19 characters with the scanf call will also cause undefined behaviour as you will write out of bounds of your array. You could avoid that with %19s as the conversion specification. You should also check that the scanf() was successful.

Resources