I'm working on a project meant to create functions to perform different actions, but I'm having trouble implementing one of the functions.
int main()
{
char str1[30] = "Hello";
char str2[30] = "Goodbye old friend";
char str3[30];
char *p = strCopy(str3, "Annie");
printf("(Annie) %s\n", p);
p = strString(str3, "nn");
printf("(nnie) %s\n", p);
strCopy(str3, "HeloHellooo");
p = strString(str3, "ello");
printf("(ellooo) %s\n", p);
return 0;
}
char *strCopy(char *s1, const char *s2)
{
char *b = s1;
while (*s2!='\0')
{
*s1 = *s2;
s1++;
s2++;
}
*s1 = '\0';
return b;
}
char *strString(const char *s1, const char *s2) // returns a string that starts with the characters in *s2. For example: char *strString ("Annie", "nn") should return "nnie".)
{
char *test;
char *b = test;
while (*s1 != '\0' && *s2 != '\0')
{
if (*s1 == *s2)
{
*test = *s1;
}
s1++;
s2++;
}
*test = '\0';
return b;
}
I'm having trouble figuring out how to return a value in char *strString when constant integers are the only two parameters. When I try, the program crashes. Keep in mind that the parameters, function declarations, and what's inside main() have to be exactly as they're written. It's code given to me as part of the project. I can only manipulate what goes inside of the functions. I'm also not permitted to use arrays in any of my functions.
(The code inside of *strString is obviously not final. I'm just having trouble testing anything while I can't figure out how to return a value.)
Inside strString you use the uninitialized pointer test:
char *b = test; // oops
...
*test = *s1; // oops
You could start with:
char *test = NULL;
Then you could update test when you find your substring. At no stage do you want to write *test =, because you are not modifying either of the input strings, nor are you creating a new string. You should be returning a pointer which points into s1 at the point where the substring can be found.
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
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);
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.
I want to reverse a string, but I got a error on swap char.
Could someone help me with this?
char* reverse_char(char* src){
char *p,*q;
p = q = src;
while(*(p) != '\0'){
p++;
}
p--;
char temp = '\0';
while (p > q) {
temp = *p;
*p = *q; // I got exec bad access here ??? why
*q = temp;
p--;
q++;
}
return src;
}
This is the main method.
int main(int argc, char const* argv[])
{
char *hi = "hello world!\n";
printf("%s", hi);
reverse_char(hi);
printf("%s", hi);
return 0;
}
Replace char *hi = "hello world!\n"; with char hi[] = "hello world!\n";
"hello world!\n" is string literal and may not be writable causing the access error. Modifying the contents of string literal is undefined behavior, it may write the contents silently, raise an access error or may do something else unexpected. (So you should not write to string literal)
Summing up
char a[] = "..."; /* Contents of a can be assigned */
char *a = "..."; /* Writing to contents of memory pointed by a leads to UB */
Though string literals in C have types of non-const character arrays they may not be changed.
From the C Standard (6.4.5 String literals)
7 It is unspecified whether these arrays are distinct provided their
elements have the appropriate values. If the program attempts to
modify such an array, the behavior is undefined.
it is better to declare a pointer initialized with a string literal as having type const char * as it is done in C++. For example
const char *hi = "hello world!\n";
In this case instead of a run-time error you would get a compile-time error that would be understandable because the parameter of the function is declared as a non-const character pointer.
You have to use a character array instead of a pointer to a string literal.
For example
char hi[] = "hello world!\n";
As for the function that it has some problem when a pointer points to an empty string. In this case after operation
p--;
you can get invalid pointer that is not necessary less than src.
I would write the function the following way
char* reverse_string( char *s )
{
char *p = s;
while ( *p ) ++p;
if ( p != s )
{
for ( char *q = s; q < --p; ++q )
{
char c = *q;
*q = *p;
*p = c;
}
}
return s;
}
I'm trying to write strcpy on my own using pointers and I get an error during runtime.
void str_cpy(char **destination, const char *source) {
// char *s1 = *destination;
while (*source != '\0') {
**destination++ = *source++; //Get an error here
}
**destination = '\0';
}
I call the function as follows:
char *str = NULL;
str_cpy(&str, "String");
Is it not OK?
Thanks!
No, it's not okay. Why? Because str is a NULL pointer. It's pointing to nothing. When you try to write values into it, where will they go? It's not pointing to any allocated memory!
You first have to allocate memory for str. You can do:
char *str = malloc(strlen("String") + 1); // + 1 for the '\0' character at the end of C-style strings
Or you can do:
char str[256]; // make str large enough to hold 256 chars. Note that this is not as safe as the above version!
Also, destination should be a single pointer, not a double pointer. Well, it's not technically wrong to use a double pointer, it's just unnecessary.
Optionally, you can allocate the memory in the str_cpy function, like so:
void str_cpy(char **destination, const char *source) {
*destination = malloc(strlen(source) + 1);
// ... continue as normal
For simplicity's sake, this can be done in one line in a function.
void mystrcpy(char *dest, const char *src) {
while (*dest++ = *src++);
}
This being said, you do need to allocate memory for dest beforehand using malloc or just simply by having a character array like char dest[256].
I don't see any need to pass a pointer-to-pointer:
void str_cpy(char *dst, const char *src) {
while (*src != '\0') {
*dst++ = *src++;
}
*dst = '\0';
}
And you need to allocate memory for dst before passing:
const char *src = "String";
char *str = malloc(strlen(src)+1); //plus one for null byte
str_cpy(dst, src);
You should likely allocate some memory for that pointer before passing it off to a function that fills what it points to (which in this case, is NULL).
Example:
char *str = malloc(128);
if (str)
{
str_cpy(&str, "String");
free(str);
str = NULL;
}
I advise not doing this without also providing target-buffer size information (i.e. if you're writing your own, then boundary-check the target buffer, otherwise your version has the same security flaws as strcpy() which are bad enough as it is).
Note: Unless you're planning on changing the address held by the pointer passed as the target, you need not use a double pointer either. The double pointer usage you have prevents the traditional strcpy() usage pattern of:
char str[128];
str_cpy(&str, "Hello"); // error.
An array address cannot be passed as a pointer-to-pointer, so your code cannot fill a static array without an intermediate pointer:
char str[128];
char *p = str;
str_cpy(&p, "Hello"); //ok. passing address of pointer.
If this is not intentional (and I don't see why it could be unless you have ideas of internally emulating strdup() on a NULL pointer passage) You should address this.
Here is a complete implementation.
Good article from here. Describes timing and performance. I did not measure myself though.
http://www.howstuffworks.com/c35.htm
char* mystrcpy(char *dst, const char *src) {
char *ptr = dst;
while ((*dst++ = *src++) ) ;
return ptr;
}
int main(int argc, char *argv[]) {
const char *src = "This is C.\0";
char *dst = malloc(sizeof(char)*(strlen(src)+1)); //+1 for the null character
dst = mystrcpy(dst, src);
printf("%s",dst);
return 1;
}
Recently I faced same problem of above one using double pointer strcpy implementation
It might helpful to others below code
void strcpy_i( char **dst, const char *src )
{
*dst=(char *)malloc((strlen(src)+1)*sizeof(char));
char *tmp=*dst;
if(tmp == NULL || src == NULL)
return ;
while((*tmp++=*src++)!='\0');
}
int main()
{
char v[]="Vinay Hunachyal";
char *d=NULL;
strcpy_i(&d,v);
printf("%s",d);
return 0;
}
#include<stdio.h>
void main()
{
void mystrcpy(char *,char *);
char s1[100],s2[100];
char *p1;
char *p2;
p1=s1;
p2=s2;
printf("Enter the string to copy to s2...?\n");
scanf("%s",p1);
mystrcpy(p2,p1);
printf("S2 after copying = %s",p2);
}
void mystrcpy(char *p2,char *p1)
{
while(*p1!='\0')
{
*p2=*p1;
p2++;
p1++;
}
*p2='\0';
}
Its my solution..Simple to understand..