It's a home work. I want to implement memcpy(). I was told memory area must not overlap. Actually I don't understand what that means, because this code works fine, but there is a possibility of memory overlap. How to prevent it?
void *mem_copy(void *dest, const void *src, unsigned int n) {
assert((src != NULL) && (n > 0));
int i = 0;
char *newsrc = (char*)src;
char *newdest = (char*)dest;
while (i < n) {
newdest[i] = newsrc[i];
i++;
}
newdest[i]='\0';
return newdest;
}
When source and destination memory blocks overlap, and if your loop copies one element after the other starting from index 0, it works for dest < source, but not for dest > source (because you overwrite elements before having copied them) and vice versa.
Your code starts copying from index 0, so you can simply test which situations work and which not. See the following test code; It shows how moving a test string forward fails, whereas moving the string backwards works fine. Further, it shows how moving the test string forward works fine when copying from backward:
#include <stdio.h>
#include <string.h>
void *mem_copy(void *dest, const void *src, size_t n) {
size_t i = 0;
char* newsrc = (char*)src;
char* newdest = (char*)dest;
while(i < n) {
newdest[i] = newsrc[i];
i++;
}
return newdest;
}
void *mem_copy_from_backward(void *dest, const void *src, size_t n) {
size_t i;
char* newsrc = (char*)src;
char* newdest = (char*)dest;
for (i = n; i-- > 0;) {
newdest[i] = newsrc[i];
}
return newdest;
}
int main() {
const char* testcontent = "Hello world!";
char teststr[100] = "";
printf("move teststring two places forward:\n");
strcpy(teststr, testcontent);
size_t length = strlen(teststr);
printf("teststr before mem_copy: %s\n", teststr);
mem_copy(teststr+2, teststr, length+1);
printf("teststr after mem_copy: %s\n", teststr);
printf("\nmove teststring two places backward:\n");
strcpy(teststr, testcontent);
length = strlen(teststr);
printf("teststr before mem_copy: %s\n", teststr);
mem_copy(teststr, teststr+2, length+1);
printf("teststr after mem_copy: %s\n", teststr);
printf("move teststring two places forward using copy_from_backward:\n");
strcpy(teststr, testcontent);
length = strlen(teststr);
printf("teststr before mem_copy: %s\n", teststr);
mem_copy_from_backward(teststr+2, teststr, length+1);
printf("teststr after mem_copy: %s\n", teststr);
}
Output:
move teststring two places forward:
teststr before mem_copy: Hello world!
teststr after mem_copy: HeHeHeHeHeHeHeH
move teststring two places backward:
teststr before mem_copy: Hello world!
teststr after mem_copy: llo world!
move teststring two places forward using copy_from_backward:
teststr before mem_copy: Hello world!
teststr after mem_copy: HeHello world!
So one could write one function, which decides whether to start copying from index 0 or from index n depending on whether the caller wants to copy forward or backward. The tricky thing is to find out whether the caller will copy forward or backward, since a pointer arithmetic on src and dest like if (src < dest) copy_from_backward(...) is actually not permitted in every case (cf. the standard, e.g. this draft):
6.5.9 Equality operators
When two pointers are compared, the result depends on the relative
locations in the address space of the objects pointed to. If two
pointers to object or incomplete types both point to the same object,
or both point one past the last element of the same array object, they
compare equal. If the objects pointed to are members of the same
aggregate object, pointers to structure members declared later compare
greater than pointers to members declared earlier in the structure,
and pointers to array elements with larger subscript values compare
greater than pointers to elements of the same array with lower
subscript values. All pointers to members of the same union object
compare equal. If the expression P points to an element of an array
object and the expression Q points to the last element of the same
array object, the pointer expression Q+1 compares greater than P. In
all other cases, the behavior is undefined.
Though I've never been in a situation where src < dest did not give me the desired results, comparing two pointers this way is actually undefined behaviour if they do not belong to the same array.
Hence, if you ask "how to prevent it?", I think that the only correct answer must be: "It's subject to the caller, because function mem_copy cannot decide whether it may compare src and dest correctly."
Actually I don't understand what doest that mean [for memory to overlap]
Consider this example:
char data[100];
memcpy(&data[5], &data[0], 95);
From the program's point of view, the range of addresses from src to src+n must not overlap the range from dest to dest+n.
if there is possibility of memory overlap, how to prevent it?
You can make your algorithm work with or without an overlap by deciding to copy overlapping regions from the back if src has numerically lower address than dest.
Note: Since you are doing memcpy, not strcpy, forcing null termination with newdest[i]='\0' is incorrect, and needs to be removed.
There are some issues in your re-implementation of memcpy():
The size argument n should have type size_t. The index variable i should have the same type as the size argument.
It is OK to pass a count of 0. Indeed your code would behave correctly in this case, remove the test from the assert().
Avoid casting away the const qualifier unless absolutely necessary.
Do not tack a '\0' at the end of the destination, it is incorrect and will cause buffer overruns.
Here is a corrected version:
void *mem_copy(void *dest, const void *src, size_t n) {
assert(n == 0 || (src != NULL && dest != NULL));
size_t i = 0;
const char *newsrc = (const char *)src;
char *newdest = (char *)dest;
while (i < n) {
newdest[i] = newsrc[i];
i++;
}
return dest;
}
Regarding the potential overlap between the source area and the destination area, your code's behavior will be surprising if the destination pointer is greater than the source, but within the source area:
char buffer[10] = "12345";
printf("before: %s\n", buffer);
mem_copy(buffer + 1, buffer, 5);
printf("after: %s\n", buffer);
Will output:
before: 12345
after: 111111
There is no completely portable way to test for such overlap, but it is quite easy on non exotic architectures at some small cost in execution time and code size. The semantics of memcpy() is that no such test is assumed to be performed by the library, so the programmer should only call this function if there is no possibility for source and destination areas to overlap. When in doubt, use memmove() that handles overlapping areas correctly.
If you wish to add an assert for this, here is a mostly portable one:
assert(n == 0 || newdest + n <= newsrc || newdest >= newsrc + n);
Here is a simple rewrite of memmove(), albeit not completely portable:
void *mem_move(void *dest, const void *src, size_t n) {
assert(n == 0 || (src != NULL && dest != NULL));
const char *newsrc = (const char *)src;
char *newdest = (char *)dest;
if (newdest <= newsrc || newdest >= newsrc + n) {
/* Copying forward */
for (size_t i = 0; i < n; i++) {
newdest[i] = newsrc[i];
}
} else {
/* Copying backwards */
for (size_t i = n; i-- > 0;) {
newdest[i] = newsrc[i];
}
}
return dest;
}
Related
char *ft_between(char *str, size_t from, size_t to)
{
char *between;
between = malloc(16);
while ((from >= 0) && (from < to) && (to < ft_strlen(str)))
{
*(between++) = str[from++];
}
*between = '\0';
printf("%s\n", between); // print nothing
printf("%s\n", between - 16); // print between but never had to do this before...
return (between);// even on calling function the pointer still at end of string
}
I think it's because I changed the address of between using ++ but I usually do that and never had this behavior... is that because of malloc ???
Is there someting I missed ?
Is thear a way to "rewind" the string lol
If I do it via a counter ie. between[counter++] = str[from++]; it works but I wanted to do via pointers as it's faster... from what I've red !
In this example str is iterate with ++ until the end to add char
but when return in calling function a printf will print all str
void ft_nbr2str(char *str, size_t nbr, char *base, size_t base_len)
{
if (nbr >= base_len)
{
ft_nbr2str(str, (nbr / base_len), base, base_len);
while (*str != '\0')
str++;
*str = base[nbr % base_len];
}
else
*str = base[nbr];
}
I think it's because I changed the address of between using ++
It's because you modified the value of between via the ++ operator. That value is the address of something else. The address of between or any other object cannot be modified.
but I usually do that and never had this behavior.
The behavior you describe is absolutely normal, so either no, you don't usually do that, or yes you did have that behavior. In your code, you will observe the same effect on from. I really don't fathom why immediately after you execute *between = '\0'; you would expect printf("%s\n", between) to print a non-empty string. malloc has nothing in particular to do with it.
I speculate that in other cases you may have instead modified a copy of your pointer, which, naturally, does not modify the original pointer. Possibly you did this by passing your pointer (by value) to another function. Example:
void strcpy_range(char *dest, char *src, size_t from, size_t to) {
while ((from >=0) && (from < to) && (src[from] != '\0'))
{
*(dest++) = src[from++]; // dest is modified
}
*dest = '\0';
}
char *ft_between(char *str, size_t from, size_t to)
{
char *between = malloc(16);
strcpy_range(between, str, from, to);
printf("%s\n", between); // prints the extracted substring
return between; // returns a pointer to the extracted substring
}
If you want to rescue your original version without introducing a new function, then use a temporary variable to track the current location in the substring. For example,
char *ft_between(char *str, size_t from, size_t to)
{
char *between = malloc(16);
char *temp = between;
while ((from >=0) && (from < to) && (to < ft_strlen(str)))
{
*(temp++) = str[from++];
}
*temp = '\0';
printf("%s\n", between); // prints the extracted substring
return between; // returns the extracted substring
}
Addendum
The alternative example added to the question demonstrates exactly the form I speculated you might have used. The (non-)effect on the caller's copy of the pointer in that case is not analogous to or even related to the modification of the function parameter observed during execution of the first function presented in the question.
After you've incremented the pointer, it now points to a different region of memory. Since the pointer is of type char, summing one unit is the same as summing sizeof(char) units, which turns out to still be 1; to 'rewind' it, as you say, you'd just have to subtract 16 * sizeof(char) = 16 (notice you're dereferencing the pointer summed by 16, so it makes perfect sense to subtract 16 to get it back to its position, or subtract however many times you want so that it points to the location you expect it to)
After this statement
*between = '\0';
the pointer between points to an empty string. So this call of printf:
printf("%s\n", between); // print nothing
indeed will output nothing.
And this return statement
return (between);// even on calling function the pointer still at end of string
returns this pointer to an empty string.
Pay attention to that this condition
(from >=0)
does not make sense because objects of the unsigned type size_t can not be negative.
Also it is unclear why there is used the magic number 16
between = malloc(16);
and
printf("%s\n", between - 16); // print between but never had to do this before...
And the function should not output any message. It is the caller of the function that will decide whether to output something.
The function can be declared and defined the following way
char * ft_between( const char *str, size_t from, size_t to )
{
char *between;
size_t n = ft_strlen( str );
if ( n < to ) to = n;
if ( to <= from )
{
between = calloc( 1, sizeof( char ) );
}
else
{
between = malloc( to - from + 1 );
if ( between != NULL )
{
char *p = between;
while ( from != to ) *p++ = str[from++];
*p = '\0';
}
}
return between;
}
For some functions for string manipulation, I try to rewrite the function output onto the original string. I came up with the general scheme of
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *char_repeater(char *str, char ch)
{
int tmp_len = strlen(str) + 1; // initial size of tmp
char *tmp = (char *)malloc(tmp_len); // initial size of tmp
// the process is normally too complicated to calculate the final length here
int j = 0;
for (int i = 0; i < strlen(str); i++)
{
tmp[j] = str[i];
j++;
if (str[i] == ch)
{
tmp[j] = str[i];
j++;
}
if (j > tmp_len)
{
tmp_len *= 2; // growth factor
tmp = realloc(tmp, tmp_len);
}
}
tmp[j] = 0;
char *output = (char *)malloc(strlen(tmp) + 1);
// output matching the final string length
strncpy(output, tmp, strlen(tmp));
output[strlen(tmp)] = 0;
free(tmp); // Is it necessary?
return output;
}
int main()
{
char *str = "This is a test";
str = char_repeater(str, 'i');
puts(str);
free(str);
return 0;
}
Although it works on simple tests, I am not sure if I am on the right track.
Is this approach safe overall?
Of course, we do not re-write the string. We simply write new data (array of the characters) at the same pointer. If output is longer than str, it will rewrite the data previously written at str, but if output is shorter, the old data remains, and we would have a memory leak. How can we free(str) within the function before outputting to its pointer?
A pair of pointers can be used to iterate through the string.
When a matching character is found, increment the length.
Allocate output as needed.
Iterate through the string again and assign the characters.
This could be done in place if str was malloced in main.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *char_repeater(char *str, char ch)
{
int tmp_len = strlen(str) + 1; // initial size of tmp
char *find = str;
while ( *find) // not at terminating zero
{
if ( *find == ch) // match
{
tmp_len++; // add one
}
++find; // advance pointer
}
char *output = NULL;
if ( NULL == ( output = malloc(tmp_len)))
{
fprintf ( stderr, "malloc peoblem\n");
exit ( 1);
}
// output matching the final string length
char *store = output; // to advance through output
find = str; // reset pointer
while ( *find) // not at terminating zero
{
*store = *find; // assign
if ( *find == ch) // match
{
++store; // advance pointer
*store = ch; // assign
}
++store; // advance pointer
++find;
}
*store = 0; // terminate
return output;
}
int main()
{
char *str = "This is a test";
str = char_repeater(str, 'i');
puts(str);
free(str);
return 0;
}
For starters the function should be declared like
char * char_repeater( const char *s, char c );
because the function does not change the passed string.
Your function is unsafe and inefficient at least because there are many dynamic memory allocations. You need to check that each dynamic memory allocation was successful. Also there are called the function strlen also too ofhen.
Also this code snippet
tmp[j] = str[i];
j++;
if (str[i] == ch)
{
tmp[j] = str[i];
j++;
}
if (j > tmp_len)
//...
can invoke undefined behavior. Imagine that the source string contains only one letter 'i'. In this case the variable tmp_len is equal to 2. So temp[0] will be equal to 'i' and temp[1] also will be equal to 'i'. In this case j equal to 2 will not be greater than tmp_len. As a result this statement
tmp[j] = 0;
will write outside the allocated memory.
And it is a bad idea to reassign the pointer str
char *str = "This is a test";
str = char_repeater(str, 'i');
As for your question whether you need to free the dynamically allocated array tmp
free(tmp); // Is it necessary?
then of course you need to free it because you allocated a new array for the result string
char *output = (char *)malloc(strlen(tmp) + 1);
And as for your another question
but if output is shorter, the old data remains, and we would have a
memory leak. How can we free(str) within the function before
outputting to its pointer?
then it does not make a sense. The function creates a new character array dynamically that you need to free and the address of the allocated array is assigned to the pointer str in main that as I already mentioned is not a good idea.
You need at first count the length of the result array that will contain duplicated characters and after that allocate memory only one time.
Here is a demonstration program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * char_repeater( const char *s, char c )
{
size_t n = 0;
for ( const char *p = s; ( p = strchr( p, c ) ) != NULL; ++p )
{
++n;
}
char *result = malloc( strlen( s ) + 1 + n );
if ( result != NULL )
{
if ( n == 0 )
{
strcpy( result, s );
}
else
{
char *p = result;
do
{
*p++ = *s;
if (*s == c ) *p++ = c;
} while ( *s++ );
}
}
return result;
}
int main( void )
{
const char *s = "This is a test";
puts( s );
char *result = char_repeater( s, 'i' );
if ( result != NULL ) puts( result );
free( result );
}
The program output is
This is a test
Thiis iis a test
My kneejerk reaction is to dislike the design. But I have reasons.
First, realloc() is actually quite efficient. If you are just allocating a few extra bytes every loop, then chances are that the standard library implementation simply increases the internal bytecount value associated with your memory. Caveats are:
Interleaving memory management.Your function here doesn’t have any, but should you start calling other routines then keeping track of all that becomes an issue. Anything that calls other memory management routines can lead to the next problem:
Fragmented memory.If at any time the available block is too small for your new request, then a much more expensive operation to obtain more memory and copy everything over becomes an issue.
Algorithmic issues are:
Mixing memory management in increases the complexity of your code.
Every occurrence of c invokes a function call with potential to be expensive. You cannot control when it is expensive and when it is not.
Worst-case options (char_repeater( "aaaaaaaaaa", 'a' )) trigger worst-case potentialities.
My recommendation is to simply make two passes.
This passes several smell tests:
Algorithmic complexity is broken down into two simpler parts:
counting space required, and
allocating and copying.
Worst-case scenarios for allocation/reallocation are reduced to a single call to malloc().
Issues with very large strings are reduced:
You need at most space for 2 large strings (not 3, possibly repeated)
Page fault / cache boundary issues are similar (or the same) for both methods
Considering there are no real downsides to using a two-pass approach, I think that using a simpler algorithm is reasonable. Here’s code:
#include <stdio.h>
#include <stdlib.h>
char * char_repeater( const char * s, char c )
{
// FIRST PASS
// (1) count occurances of c in s
size_t number_of_c = 0;
const char * p = s;
while (*p) number_of_c += (*p++ == c);
// (2) get strlen s
size_t length_of_s = p - s;
// SECOND PASS
// (3) allocate space for the resulting string
char * dest = malloc( length_of_s + number_of_c + 1 );
// (4) copy s -> dest, duplicating every occurance of c
if (dest)
{
char * d = dest;
while (*s)
if ((*d++ = *s++) == c)
*d++ = c;
*d = '\0';
}
return dest;
}
int main(void)
{
char * s = char_repeater( "Hello world!", 'o' );
puts( s );
free( s );
return 0;
}
As always, know your data
Whether or not a two-pass approach actually is better than a realloc() approach depends on more factors than what is evident in a posting on the internet.
Nevertheless, I would wager that for general purpose strings that this is a better choice.
But, even if it isn’t, I would argue that a simpler algorithm, splitting tasks into trivial sub-tasks, is far easier to read and maintain. You should only start making tricky algorithms only if you have use-case profiling saying you need to spend more attention on it.
Without that, readability and maintainability trumps all other concerns.
I understand that issues with string-reversal in C are numerous on this site, but I seem to be having a uniquely odd problem: the reversal logic simply does not run despite seeming to be set up correctly.
I wanted to write a program to reverse a given string in-place, by moving two pointers from the front and back of the string together:
void reverse_inplace_pointer(char *string_in, int length) {
char *back_ptr = string_in + (length - 1);
for (int i = 0; i < length / 2; i++) {
char swap_char = *back_ptr;
/* Dereference pointers to double-check values */
printf("\nCurrent front char: %c\nCurrent back char: %c", *string_in, *back_ptr);
/* Swap */
*back_ptr = *string_in;
*string_in = swap_char;
printf("\nSwap %u successful.", i + 1);
string_in++;
back_ptr--;
}
}
Calling this with a test string:
char *test_string = "TestMeTestMe";
nets the result:
Current front char: T
Current back char: e
And then execution just stops, at the assignment of the character pointed to by *string_in to the location pointed to by *back_ptr. If I remove the assignment, the program will quite happily move the pointers along the test string and print out each character without issue.
I looked for other examples of reassigning elements in arrays and found the logic in tutorials to be almost identical to mine. I copied this example wholesale as a test:
void steal_this_function(char *string) {
printf("\nEntering function:");
int length, c;
char *begin, *end, temp;
length = strlen(string);
begin = string;
end = string;
for (c = 0; c < (length - 1); c++) {
end++;
}
for (c = 0; c < length / 2; c++) {
temp = *end;
*end = *begin;
*begin = temp;
begin++;
end--;
}
}
The result is the same - it stops on the first iteration and the program exits. The only difference here is the reuse of the original pointer string, which is then walked to the end of the string with a for-loop.
Clearly I have some confusion here with pointers, but my understanding was that *back_ptr = *string_in; effectively says dereference the pointer string_in, then go to the location pointed to by back_ptr and store whatever it contains there.
I'm also compiling with the -Wall flag on GCC and it isn't finding any issues. This is frustrating because it should be very simple.
the function requires modifiable char array. It cannot be called when parameter is a pointer to thestring literal.
Some additional remarks.
This kind of function should return the reversed string. It is possible to use it as a parameter of another functions.
strlen returns size_t not int. Use the correct type in the loops as well
#include <string.h>
#include <stdio.h>
char *reverse(char *str)
{
char *end = str + strlen(str) - !!*str;
char *wrk = str;
while(end > wrk)
{
char tmp = *wrk;
*wrk++ = *end;
*end-- = tmp;
}
return str;
}
int main(void)
{
char str[] = "0123456789";
printf("%s\n", reverse(str));
}
Calling this with a test string: char *test_string = "TestMeTestMe";
That's the culprit, as noted in the comments, it declares a pointer to the memory where the string literal "TestMeTestMe" is stored, which you are not allowed to modify.
You should declare an array instead.
char test_string[] = "TestMeTestMe"; // Now test_string is a modifiable
// NULL-terminated char[13]
I'm also compiling with the -Wall flag on GCC and it isn't finding any issues.
-Wall it's not enough, but you can use -Wwrite-strings to catch this particular issue (see e.g. here).
So I've looked around on SO and can't find code that answers my question. I have written a function that is supposed to reverse a string as input in cmd-line. Here is the function:
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string)];
for (x = strlen(string) - 1; x > 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
string = line;
}
When I call my reverse() function, the string stays the same. i.e., 'abc' remains 'abc'
If more info is needed or question is inappropriate, let me know.
Thanks!!
You're declaring your line array one char shorter remember the null at the end.
Another point, it should be for (x = strlen(string) - 1; x >= 0; x--) since you need to copy the character at 0.
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string) + 1];
for (x = strlen(string) - 1; x >= 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
for(x = 0; x < strlen(string); x++)
{
string[x] = line[x];
}
}
Note that this function will cause an apocalypse when passed an empty string or a string literal (as Bobby Sacamano said).
Suggestion you can probably do: void reverse(char source[], char[] dest) and do checks if the source string is empty.
I think that your answer is almost correct. You don't actually need an extra slot for the null character in line. You just need two minor changes:
Change the assignment statement at the bottom of the procedure to a memcpy.
Change the loop condition to <-
So, your correct code is this:
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string)];
for (x = strlen(string) - 1; x >= 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
memcpy(string, line, sizeof(char) * strlen(line));
}
Since you want to reverse a string, you first must decide whether you want to reverse a copy of the string, or reverse the string in-situ (in place). Since you asked about this in 'C' context, assume you mean to change the existing string (reverse the existing string) and make a copy of the string in the calling function if you want to preserve the original.
You will need the string library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Array indexing works, and this version takes that approach,
/* this first version uses array indexing */
char*
streverse_a(char string[])
{
int len; /*how big is your string*/
int ndx; /*because 'i' is hard to search for*/
char tmp; /*hold character to swap*/
if(!string) return(string); /*avoid NULL*/
if( (len=strlen(string)) < 2 ) return(string); /*one and done*/
for( ndx=0; ndx<len/2; ndx++ ) {
tmp=string[ndx];
string[ndx]=string[len-1-ndx];
string[len-1-ndx]=tmp;
}
return(string);
}
But you can do the same with pointers,
/* this is how K&R would write the function with pointers */
char*
streverse(char* sp)
{
int len, ndx; /*how big is your string */
char tmp, *bp, *ep; /*pointers to begin/end, swap temporary*/
if(!sp) return(sp); /*avoid NULL*/
if( (len=strlen(bp=sp)) < 2 ) return(sp); /*one and done*/
for( ep=bp+len-1; bp<ep; bp++, ep-- ) {
tmp=*bp; *bp=*ep; *ep=tmp; /*swap*/
}
return(sp);
}
(No, really, the compiler does not charge less for returning void.)
And because you always test your code,
char s[][100] = {
"", "A", "AB", "ABC", "ABCD", "ABCDE",
"hello, world", "goodbye, cruel world", "pwnz0r3d", "enough"
};
int
main()
{
/* suppose your string is declared as 'a' */
char a[100];
strcpy(a,"reverse string");
/*make a copy of 'a', declared the same as a[]*/
char b[100];
strcpy(b,a);
streverse_a(b);
printf("a:%s, r:%s\n",a,b);
/*duplicate 'a'*/
char *rp = strdup(a);
streverse(rp);
printf("a:%s, r:%s\n",a,rp);
free(rp);
int ndx;
for( ndx=0; ndx<10; ++ndx ) {
/*make a copy of 's', declared the same as s[]*/
char b[100];
strcpy(b,s[ndx]);
streverse_a(b);
printf("s:%s, r:%s\n",s[ndx],b);
/*duplicate 's'*/
char *rp = strdup(s[ndx]);
streverse(rp);
printf("s:%s, r:%s\n",s[ndx],rp);
free(rp);
}
}
The last line in your code does nothing
string = line;
Parameters are passed by value, so if you change their value, that is only local to the function. Pointers are the value of the address of memory they are pointing to. If you want to modify the pointer that the function was passed, you need to take a pointer to that pointer.
Here is a short example of how you could do that.
void reverse (char **string) {
char line = malloc(strlen(*string) + 1);
//automatic arrays are deallocated once the function ends
//so line needs to be dynamically or statically allocated
// do something to line
*string = line;
}
The obvious issue with this is that you can initialize the string with static memory, then this method will replace the static memory with dynamic memory, and then you'll have to free the dynamic memory. There's nothing functionally wrong with that, it's just a bit dangerous, since accidentally freeing the string literal is illegal.
char *test = "hello";
reverse(test);
free(test); //this is pretty scary
Also, if test was allocated as dynamic memory, the pointer to it would be lost and then it would become a memory leak.
My code is at the bottom. I'm trying to take two strings and change them as per the edit transcript that I receive. I wrote my code but I don't understand why I'm getting such a weird output. My goal is to first store the values of the two strings then make them into a 2D array afterwards, but I'm failing on part one of that goal. Here's the problem:
Create a function that meets the following:
Input: an edit transcript, and 2 original strings (3 strings)
Output: a 2 d array containing the two alignments post edit
Example:
s1 = “vintner”
s2 = “writers”
trans = “RIMDMDMMI”
R stands for "replace"
I stands for "insert"
M stands for "match"
D stands for "delete"
Answer:
alignment={“v_intner_”,
“wri_t_ers”}; //return a 2d array
Function prototype:
char** getAlignment(char* s1, char* s2, char* s3);
Here's my code below:
char TestS1[] = "vintner";
char TestS2[] = "writers";
char TestS3[] = "RIMDMDMMI";
char twoDarray[2][10];
char** getAlignment(char* s1, char* s2, char* s3){
char transTemp[n];
char s1Temp[n];
char s2Temp[n];
char sOne[n];
char sTwo[n];
strcpy(sOne, s1);
strcpy(sTwo, s2);
int jj;
strcpy(transTemp, s3);
int kk;
for(jj=0, kk=0; jj<n, kk<n; jj++, kk++){
if(transTemp[jj]=='R')
{
s1Temp[kk] = sOne[jj];
s2Temp[kk] = sTwo[jj];
}
if(transTemp[jj]=='I'){
s1Temp[kk] = '_';
s1Temp[kk+1] = sOne[jj];
s2Temp[kk] = sTwo[jj];
kk++;
}
if(transTemp[jj] == 'M'){
s1Temp[kk] = sOne[jj];
s2Temp[kk] = sTwo[jj];
}
if(transTemp[jj] == 'D'){
s2Temp[kk] = '_';
s2Temp[kk+1] = sTwo[jj];
s1Temp[kk] = sOne[jj];
kk++;
}
}
printf("\ns1Temp = %s\n", s1Temp);
printf("\ns2Temp = %s\n", s2Temp);
return 0;
}
main()
{
printf("The new method returns: ", getAlignment(TestS1,TestS2,TestS3));
return 0;
}
Your question really has two parts: How can I return two strings? And why don't I get the desired output?
Strings in C are character arrays. You seldom return strings. It is more common to pass a character array to a function, together with its maximum length, and fill that array. The functions in <string.h> do that. A good design model is, in my opinion, snprintf: It fills the buffer, takes care not to overflow it, ensures that the result is properly null-terminated and returns the number of characters written had the buffer been big enough. That last property allows you to pass a length of null (and as a special case the NULL pointer) to find out how many chars you need and allocate memory as appropriate.
So the prototype for your function could look like this:
int getAlignment(char *res1, char *res2, size_t n,
const char* s1, const char* s2, const char* trans);
Except that the resulting strings could be of different length in your case.
You can also return strings, but you'll either have to return new memory allocated with malloc on the heap, which means the client code must explicitly free it, or pointers into already existing memory. You can, of course, only return one string.
You can return multiple values from a function as a struct. Structs do not decay into pointers when passing them to or returning them from functions. I'll use that approach in my example below.
As for the second question: Your main problem is that you have three strings - two source strings and one translation string - but keep only two indices. All strings are traversed independently; there is no synchronisation between the strings' indices.
You append to the result strings as you go. The "driving" string is the trenslation string, so you should traverse only that with the main loop.
Another thing to note is that you don't need to make copies of the source strings. This is not only inneccessary, it is also dangerous, because strcpy could overflow the buffers. Taking care of overflow with strncpy couldtruncate the input strings.
I've updated your implementation:
#include <stdio.h>
#include <string.h>
#define N 10
struct Result {
char res1[N];
char res2[N];
};
struct Result getAlignment(const char* s1, const char* s2, const char* trans)
{
struct Result res;
int j1 = 0; // index into s1
int j2 = 0; // index into s2
int n = N - 1; // leave 1 char for null terminator
while (*trans) {
if (*trans == 'R') {
if (j1 < n) res.res1[j1++] = *s1++;
if (j2 < n) res.res2[j2++] = *s2++;
}
if (*trans == 'I'){
if (j1 < n) res.res1[j1++] = '_';
if (j1 < n) res.res1[j1++] = *s1++;
if (j2 < n) res.res2[j2++] = *s2++;
}
if (*trans == 'M') {
if (j1 < n) res.res1[j1++] = *s1++;
if (j2 < n) res.res2[j2++] = *s2++;
}
if (*trans == 'D') {
if (j1 < n) res.res1[j1++] = *s1++;
if (j2 < n) res.res2[j2++] = '_';
if (j2 < n) res.res2[j2++] = *s2++;
}
trans++;
}
// null-terminate strings
res.res1[j1] = '\0';
res.res2[j2] = '\0';
return res;
}
int main()
{
char *str1 = "vintner";
char *str2 = "writers";
char *trans = "RIMDMDMMI";
struct Result res = getAlignment(str1, str2, trans);
printf("%s\n%s\n\n", res.res1, res.res2);
return 0;
}
Things to note:
The translation string is traversed via pointer, which saves an index.
The result strings are appended to only if there is enough space. You can change N to 5 and see how the result strings are truncated after 4 characters, thus losing information, but preventing buffer overflows.
Both result-string indices and source string pointers are incrementes as you go.
The source strings are only read from. (That's why copying doesn't make sense.) So they should be const char * in the function signature.