C - Getting bus error when passing directly string in param - c

Making my own version of strtrim in C and its working fine but I wonder why it's working if I declare 2 array of char, like this:
static int is_set(const char *set, char c)
{
char *s;
s = (char *)set;
while (*s)
{
if (*s == c)
return (1);
s++;
}
return (0);
}
static char *ft_strsub(char const *str, unsigned int start, unsigned int end)
{
char *res;
char *s;
size_t size;
int i;
size = end - start;
res = malloc(size + 1);
if (!res)
return (res);
s = (char *)str + start;
i = 0;
while (*s && end-- > start)
res[i++] = *s++;
res[i] = 0;
return (res);
}
char *ft_strtrim(char const *s1, char const *set)
{
char *start;
char *s;
s = (char *)s1;
while (*s && is_set(set, *s))
s++;
if (!*s)
return (s);
start = s;
while (*s)
s++;
while (is_set(set, *--s))
;
if (*s)
*++s = 0;
return (ft_strsub(start, 0, s1 - s));
}
int main(void)
{
char tst[] = " xxxtripouille";
char tst2[] = " x";
char * s = ft_strtrim(tst, tst2);
printf("%s\n", s);
free(s);
return (0);
}
But not if I pass directly a string directly in param. I'm getting bus error if I do this
int main(void)
{
/*char tst[] = " xxxtripouille";
char tst2[] = " x";*/
char * s = ft_strtrim(" xxxtripouille", " x");
printf("%s\n", s);
free(s);
return (0);
}
I must have missed something in my training ^^Thank's in advance !
EDIT:
Thank's for reply !
Thank you for your advice, I took care to write them down, I thank you again, and I also found out why it wasn't working, it's because I'm trying to close the string and as you told me: it's a read-only string;
char *ft_strtrim(char const *s1, char const *set)
{
char *start;
char *s;
s = (char *)s1;
while (*s && is_set(set, *s))
s++;
if (!*s)
return (ft_strdup(s));
start = s;
while (*s)
s++;
while (is_set(set, *--s))
;
if (*s)
++s;
return (ft_strsub(start, 0, s - start));
}

Here's a few lines of code you might consider to replace your is_set() function:
static int matchAny( const char *set, char c ) {
const char *s = set;
for( ; *s && *s != c; s++ )
; // just searching
return *s != '\0'; // true = match found
}
Don't "cast away" const-ness unnecessarily. Exploit the "conditional" segment of the for() loop. Return is also a conditional: "If not reaching the end of the string of 'bad' characters, then 'c' was 'matched' in that string.
If you get "trimming the front" working, perhaps a simple "reverse string" can be used to "trim the back end, too", then "reverse string" once more. Re-use code that works.
Seeing this function, it can be made even simpler:
static int matchAny( const char *set, const char c ) {
while( *set && *set != c )
set++; // just searching
return *set != '\0'; // true = match found
}
Often the best solution is to use fewer resources (like variables) but to use them wisely. Then, flaws have fewer places to hide.

At least this statement does not make a sense
return (ft_strsub(start, 0, s1 - s));
because there can be passed a negative value s1 - s that will be converted to a very big positive value in the function ft_strsub because the corresponding parameter has an unsigned integer type.
Pay attention to that the compiler can issue a message that you are discarding the qualifier const as for example
char *s;
s = (char *)s1;
Also the function can return a pointer that does not point to a dynamically allocated array that you are tries to free with the function free
if (!*s)
return (s);
//...
free(s);

Related

Is there an easy way to remove specific chars from a char*?

char * deleteChars = "\"\'.“”‘’?:;-,—*($%)! \t\n\x0A\r"
I have this and i'm trying to remove any of these from a given char*. I'm not sure how I would go about comparing a char* to it.
For example if the char* is equal to "hello," how would I go about removing that comma with my deleteChars?
So far I have
void removeChar(char*p, char*delim){
char*holder = p;
while(*p){
if(!(*p==*delim++)){
*holder++=*p;
p++;
}
}
*holder = '\0';
A simple one-by-one approach:
You can use strchr to decide if the character is present in the deletion set. You then assign back into the buffer at the next unassigned position, only if not a filtered character.
It might be easier to understand this using two indices, instead of using pointer arithmetic.
#include <stdio.h>
#include <string.h>
void remove_characters(char *from, const char *set)
{
size_t i = 0, j = 0;
while (from[i]) {
if (!strchr(set, from[i]))
from[j++] = from[i];
i++;
}
from[j] = 0;
}
int main(void) {
const char *del = "\"\'.“”‘’?:;-,—*($%)! \t\n\x0A\r";
char buf[] = "hello, world!";
remove_characters(buf, del);
puts(buf);
}
stdout:
hello world
If you've several delimiters/characters to ignore, it's better to use a look-up table.
void remove_chars (char* str, const char* delims)
{
if (!str || !delims) return;
char* ans = str;
int dlt[256] = {0};
while (*delims)
dlt[(unsigned char)*delims++] = 1;
while (*str) {
if (dlt[(unsigned char)*str])
++str; // skip it
else //if (str != ans)
*ans++ = *str++;
}
*ans = '\0';
}
You could do a double loop, but depending on what you want to treat, it might not be ideal. And since you are FOR SURE shrinking the string you don't need to malloc (provided it was already malloced). I'd initialize a table like this.
#include <string.h>
...
char del[256];
memset(del, 0, 256 * sizeof(char));
for (int i = 0; deleteChars[i]; i++) del[deleteChars[i]] = 1;
Then in a function:
void delChars(char *del, char *string) {
int i, offset;
for (i = 0, offset = 0; string[i]; i++) {
string[i - offset] = string[i];
if (del[string[i]]) offset++;
}
string[i - offset] = 0;
}
This will not work on string literals (that you initialize with char* x = "") though because you'd end up writing in program memory, and probably segfault. I'm sure you can tweak it if that's your need. (Just do something like char *newString = malloc(strlen(string) + 1); newString[i - offset] = string[i])
Apply strchr(delim, p[i]) to each element in p[].
Let us take advantage that strchr(delim, 0) always returns a non-NULL pointer to eliminate the the null character test for every interrelation.
void removeChar(char *p, char *delim) {
size_t out = 0;
for (size_t in; /* empty */; in++) {
// p[in] in the delim set?
if (strchr(delim, p[in])) {
if (p[in] == '\0') {
break;
}
} else {
p[out++] = p[in];
}
}
p[out] = '\0';
}
Variation on #Oka good answer.
it is better way - return the string without needless characters
#include <string.h>
char * remove_chars(char * str, const char * delim) {
for ( char * p = strpbrk(str, delim); p; p = strpbrk(p, delim) )
memmove(p, p + 1, strlen(p));
return str;
}

Concat two strings through pointers not working

I made a function that concat string t to the end of string s, for my exercise I have to use pointers for this, so I did this way but its not working:
#include <stdio.h>
void strcat(char *s, char *t)
{
while (*s++);
for (*s = *t; *s = *t; s++, t++);
}
int main()
{
char c[100] = "hello";
char *w = " world\n";
strcat(c, w);
printf(c);
return 0;
}
The output of c always return "hello" instead of "hello world\n"
This while loop within the function
while (*s++);
is incorrect. After the while loop the pointer s points to after the terminating zero character '\0' due to the postfix increment operator.
The function can be declared and defined the following way
char * strcat(char *s, const char *t)
{
char *p = s;
while( *p ) ++p;
while ( ( *p++ = *t++ ) != '\0' );
return s;
}
Also you should rename the function because there is already standard string function strcat in C.
Here is a demonstration program.
#include <stdio.h>
char *string_cat( char *s, const char *t )
{
char *p = s;
while (*p) ++p;
while (( *p++ = *t++ ) != '\0');
return s;
}
int main( void )
{
char c[100] = "hello";
const char *w = " world";
puts( string_cat( c, w ) );
}
The program output is
hello world
In addition to #Vlad from Moscow good answer:
Local void strcat(char *s, char *t) assumes the strings referenced by s, t do not overlap in memory. Consider the infinite loop below if s == t.
char *p = s;
while (*p) p++;
while ((*p++ = *t++ ) != 0);
The standard C library has char *strcat(char * restrict s1, const char * restrict s2);. The restrict assumes that no-overlap and emits maybe better code> Otherwise the result in undefined behavior (UB). OP's code should also use restrict.
But what is we wanted to do strcat() and cope with overlap?
// Allow strings refenced by s,t to overlap.
char *my_strcat(char *s1, const char *s2) {
size_t s1len = strlen(s1);
size_t s2siz = strlen(s2) + 1u;
memmove(s1 + s1len, s2, s2siz);
return s1;
}
int main(void) {
char c[100] = "hello";
const char *w = c; //" world";
puts(my_strcat(c, w));
}
Output
hellohello
To handle overlap, extra work occurred to determine the size of s2.

How to reverse a string with pointers only?

I'm trying to reverse a string, but it just stays the same. I don't use any modules except <string.h> and <stdio.h>.
void rev(s){
char i, temp;
char *sf = s;
char ri = strlen((s) - 1);
char *sl = &s[ri];
for (i = 0; i < ri; i++){
if (*sf != *sl){
temp = *sf++;
s[i] = *sl--; //
s[ri--] = temp; //those two seems to be getting new characters, but it won't
}
else {
ri--;
sf++;
sl--;
}
}
printf("%s", s);
}
The function will not compile at least because the parameter does not have a type specifier.
void rev(s){
The type char has a little range of acceptable values. So you shall not use it for calculating the length of a string.
The call of strlen in this declaration
char ri = strlen((s) - 1);
invokes undefined behavior. It seems you mean
char ri = strlen(s) - 1;
that also can invoke undefined behavior for an empty string.
This loop
for (i = 0; i < ri; i++){
does not use pointers.
The function can be defined the following way as it is shown in the demonsytrative program below.
#include <stdio.h>
#include <string.h>
char * reverse( char *s )
{
if ( *s )
{
for ( char *first = s, *last = s + strlen( s ); first < --last; ++first )
{
char c = *first;
*first = *last;
*last = c;
}
}
return s;
}
int main( void )
{
char s1[] = "1";
char s2[] = "12";
char s3[] = "123";
puts( reverse( s1 ) );
puts( reverse( s2 ) );
puts( reverse( s3 ) );
}
The program output is
1
21
321
A simple solution:
char *sl = sf;
while (*sl != 0)
++ sl;
-- sl;
while (sf < sl)
{
char c = *sf;
*sf = *sl;
*sl = c;
++sf, --sl;
}
Find the end of the string by skipping all characters until you find the NUL (zero) character.
Then step back one character (decrement sl) so that you have pointers to the first and the last character of the string.
Then walk both pointers towards one another and swap characters until the pointers meet or cross.
Your code has plenty of issues (bugs 🐛):
char ri = strlen((s) - 1); has to be size_t ri = strlen((s)) - 1;
Other code is very hard to analyze as you use not self explanatory variable names.
Here you have a bit more simple code and much easier to analyse.
char *reverseString(char *str)
{
char *wrk = str, *end;
if(str && *str)
{
end = str + strlen(str) - 1;
while(end > wrk)
{
char temp = *wrk;
*wrk++ = *end;
*end-- = temp;
}
}
return str;
}
int main(void)
{
char str[] = "1234567890";
printf("reversed: %s\n", reverseString(str));
}

How to use malloc in this situation?

I'm a bit of a newbie at C, so please bear with me...
I have a function to count char in a string called char strLength, but I have to create a function that uses this function to count the number of characters in a passed string, mallocates a new string with space for a NULL terminator, copies the string and then returns the copy.
Here's what I have:
character counter
int strLength(char* toCount)
{
int count = 0;
while(*toCount != '\0')
{
count++;
toCount++;
}
return count;
}
and here's the beginning of the sought-after function
char* strCopy(char *s)
{
int length = strLength(s);
}
Since you are struggling with malloc, here is how the next line should look:
char* strCopy(char *s)
{
int length = strLength(s);
char *res = malloc(length+1);
// Copy s into res; stop when you reach '\0'
...
return res;
}
You want strdup. However, since I suspect this is a learning exercise:
char *strCopy(const char *src)
{
size_t l = strlen(src) + 1;
char *r = malloc(l);
if (r)
memcpy(r, src, l);
return r;
}
If you are curious how to copy strings yourself, you could replace the memcpy with something like:
char *dst = r;
while (*src)
*dst++ = *src++;
*dst = 0;
However I would suggest using library functions: if not strdup, then malloc + memcpy.
You can use strdup() clib call.
You can write something like:
char* strCopy(char *s) {
int length = strLength(s);
char *rc = (char *)malloc(length + 1);
return rc? strcpy(rc, s) : NULL;
}

C replace char in char array

Folks, need to search through a character array and replace any occurrence of '+','/',or'=' with '%2B','%2F', and '%2F' respectively
base64output variable looks like
FtCPpza+Z0FASDFvfgtoCZg5zRI=
code
char *signature = replace_char(base64output, "+", "%2B");
signature = replace_char(signature, "/", "%2F");
signature = replace_char(signature, "=", "%3B");
char replace_char (char *s, char find, char replace) {
while (*s != 0) {
if (*s == find)
*s = replace;
s++;
}
return s;
}
(Errors out with)
s.c:266: warning: initialization makes pointer from integer without a cast
What am i doing wrong? Thanks!
If the issue is that you have garbage in your signature variable:
void replace_char(...) is incompatible with signature = replace_char(...)
Edit:
Oh I didn't see... This is not going to work since you're trying to replace a char by an array of chars with no memory allocation whatsoever.
You need to allocate a new memory chunk (malloc) big enough to hold the new string, then copy the source 's' to the destination, replacing 'c' by 'replace' when needed.
The prototype should be:
char *replace_char(char *s, char c, char *replace);
1.
for char use '' single quotes
for char* use "" double quotes
2.
The function does include the return keyword, therefore it does not return what you'd expect
3.
These webpages have examples on string replacement
http://www.cplusplus.com/reference/cstring/strstr/
What is the function to replace string in C?
You could go for some length discussing various ways to do this.
Replacing a single char is simple - loop through, if match, replace old with new, etc.
The problem here is that the length of the "new" part is longer than the length of the old one.
One way would be to determine the length of the new string (by counting chars), and either (1) try to do it in place, or (2) allocate a new string.
Here's an idea for #1:
int replace(char *buffer, size_t size, char old, const char *newstring)
{
size_t newlen = strlen(newstring);
char *p, *q;
size_t targetlen = 0;
// First get the final length
//
p = buffer;
while (*p)
{
if (*p == old)
targetlen += newlen;
else
targetlen++;
++p;
}
// Account for null terminator
//
targetlen++;
// Make sure there's enough space
//
if (targetlen > size)
return -1;
// Now we copy characters. We'll start at the end and
// work our way backwards.
//
p = buffer + strlen(buffer);
q = buffer + targetlen;
while (targetlen)
{
if (*p == old)
{
q -= newlen;
memcpy(q, newstring, newlen);
targetlen -= newlen;
--p;
}
else
{
*--q = *p--;
--targetlen;
}
}
return 0;
}
Then you could use it this way (here's a quick test I did):
char buf[4096] = "hello world";
if (replace(buf, sizeof(buf), 'o', "oooo"))
{
fprintf(stderr, "Not enough space\n");
}
else
{
puts(buf);
}
your replace_char signature returns void
void replace_char (char *s, char find, char replace)
But, when the linker tries to resolve the following
signature = replace_char(signature, "=", '%3B');
It doesn't find any function that's called replace_char and returns int (int is the default if there's no prototype).
Change the replace_char function prototype to match the statement.
EDIT:
The warning states that your function returns char, but you use it as a char *
also, your function doesn't return anything, do you need to return something ?
It looks like you don't really understand the code that you're working with.
Fixing errors and warnings without understanding exactly what you need to do is worthless..
fix like this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *replace_char (char *str, char find, char *replace) {
char *ret=str;
char *wk, *s;
wk = s = strdup(str);
while (*s != 0) {
if (*s == find){
while(*replace)
*str++ = *replace++;
++s;
} else
*str++ = *s++;
}
*str = '\0';
free(wk);
return ret;
}
int main(void){
char base64output[4096] = "FtCPpza+Z0FASDFvfgtoCZg5zRI=";
char *signature = replace_char(base64output, '+', "%2B");
signature = replace_char(signature, '/', "%2F");
signature = replace_char(signature, '=', "%3B");
printf("%s\n", base64output);
return 0;
}
below is a code that ACTUALLY WORKS !!!!
Ammar Hourani
char * replace_char(char * input, char find, char replace)
{
char * output = (char*)malloc(strlen(input));
for (int i = 0; i < strlen(input); i++)
{
if (input[i] == find) output[i] = replace;
else output[i] = input[i];
}
output[strlen(input)] = '\0';
return output;
}

Resources