Something's wrong with my reverse string function - c

So it seems it works when I reversed "hello", but it prints out something weird like
"ol▒eh"
in the middle. It's gone when I fixed
i< length/2;
to
i<= length/2;
Isn't the first one supposed to be the right one?
what's the ▒ character mean in C? is it something like Null?
void reverse_copy(char dest[], const char src[]){
size_t i;
char temp;
size_t length = (size_t)strlen(src);
for(i = 0; i <= length/2; i++){ /*?? why i<length/2 is not working*/
dest[i] = src[length-i-1];
temp = src[i];
dest[length-i-1] = temp;
}
}

The main problem with i< length/2; is that it may leave out the "middle" element in case of odd string lenght of src. Hence, the middle element in dest may remain uninitialized, showing up as some "arbitrary" ASCII value then.
But in general, your code is appropriate for reverse_in_place, where you have to take care of not overwriting something that you need later in the loop for copying.
If you do a reverse_copy, however, it is sufficient - or better - to simply have one reverse loop:
void reverse_copy(char dest[], const char src[]){
size_t i;
size_t length = strlen(src);
for(i = 0; i < length; i++){
dest[i] = src[length-i-1];
}
dest[i] = '\0';
}

With :
for(i = 0; i < length/2; i++){
you never set the middle character (for odd lengths) in dest.
With your example "hello", length/2 is 2, so you set (for i = 0) :
dest[0] = src[5-0-1]; // dest[0] = src[4]
dest[5-0-1] = src[0]; // dest[4] = src[0]
and then (for i = 1) :
dest[1] = src[5-1-1]; // dest[1] = src[3]
dest[5-1-1] = src[1]; // dest[3] = src[1]
and that's it. You never set dest[2].

Because i<length/2 is already based on integer division, i.e: it will floor the result. This will skip the middle element in case of odd length strings.

To understand what is happening in the code, a debugger would help.
You need to step through the code line by line and watch what is the value of i and length - i - 1.
The reason a strange character appears in the middle is that if length is odd then the middle item is skipped when the condition is <.
For example, when length == 5 then 5/2 == 2 (because of integer division 2.5 comes out to be 2 ).
So analysing the loop:
i=0
is i < 2. Yes, so continue code block.
dest[0] = src[4]
temp = src[0]
dest[4] = temp
i++ i is 1
is i < 2. Yes, so continue code block.
dest[1] = src[3]
temp = src[1]
dest[3] = temp
i++ i is 2
is i < 2. No, so exit the loop
So looking at the steps (especialialy steps 3,5,8,10) only dest[0], dest[1], dest[3], dest[4] are written from the source when checking <.
Destination 2 is not changed.
This problem does not arise for even numbers.
As dest[2] was not updated then the character which was already there, is been displayed. Which could be any random character. If it was initialized to 0 (a null) then that is the character that represents 0.
But looking at that character it looks more like a value 177 (extended ASCII codes :http://www.asciitable.com/)
Also I find this definition of reverse_copy very error prone, as it it doesn't know how big the destination buffer is. It can overwrite something if it is too small.
In this case I would use a sentinel to mark the end of the string, and use a while loop:
void reverse_copy(char dest[], const char src[])
{
const char* src_end = src + strlen(src) - 1;
--src;
while (src_end > src)
{
*dest = *src_end;
++dest;
--src_end;
}
*dest = '\0';
}

Related

Simulate strrev() function, crazy output

I should simulate the operation of the strrev() function with an inscription from me. However, I don't understand why I have a series of special characters that don't make sense as output until you stop the program completely. I also tried to see if the problem was in the index "i" with the commented line of code, but it's ok. What could be the problem? thanks!
void strrev_new(char *s_to_rev) {
int i = 0;
int length = 0;
length = strlen(s_to_rev);
for (i = 0; i < length; i++) {
s_to_rev[length - i] = s_to_rev[i];
// printf("%d ----- %d\n", (length-i), i);
}
}
You have an off-by-one error, since strlen() returns the length of the string (e.g. 5 for hello), but the last index in the string is 4 (counting from 0).
Try
s_to_rev[length - 1 - i] = s_to_rev[i];
Your code has two problems. The first, brilliantly spotted by #AKX, is that you write starting from str[length] character instead of str[length-1] (in C array indexes start from 0).
The second problem is a consequence of the fact you are trying to reverse the string in place, that is without using a auxiliary array.
With the loop
for (i = 0; i < length; i++) {
s_to_rev[length - i] = s_to_rev[i];
}
you correctly start updating the last elements of the array. But as soon as you reach the half of the string, the characters at s_to_rev[i] are not the original ones anymore, as you updated them previously!
Try instead traversing half the string and swapping characters (just use a temporary char variable):
for (i = 0; i < length/2; i++) {
char tmp = s_to_rev[length - i -1],
s_to_rev[length - i -1] = s_to_rev[i];
s_to_rev[i] = tmp;
}

How to reset a string in C

I have this string
char currentString[212] = { 0 };
and after I'm using it once, I want to reset it.
I tried many ways, such as:
for (int k = 0; k < strlen(currentString); k++)
{
currentString[k] = '\0';
}
but it won't go over the loop more than once, and it give '\0' only to the first char, the rest remain the same.
and I also tried:
currentString[0] = '\0';
yet I get the same result.
any suggestions for what can I do?
thanks!
strlen will find the length by searching for the first occurrence of \0. So if you want to reset the whole array, you should change strlen(currentString) to sizeof currentString. However, do note that this will not work with pointers.
If you pass the array to a function, you cannot determine the size of the array afterwards, so this will not work:
void foo(char * arr) {
for (int k = 0; k < sizeof arr; k++)
arr[k] = '\0';
}
Instead you need to do like this:
void foo(char * arr, size_t size) {
for (int k = 0; k < size; k++)
arr[k] = '\0';
}
But of course there's no reason to write custom functions for this when memset is available.
Imagine char currentString[] = "abc"; and then running you loop:
k = 0
initialy strlen(currentString) = 3, there are 3 characters before '\0' byte. the loop condition k < strlen(currentString) is true
k = 0 -> currentString[0] = '\0'
k++ -> k = 1
then strlen(currentString) = 0 (as the first byte of currentString is equal to '\0', there are no characters before '\0')
the loop condition is false k < strlen(currentString) -> 1 < 0
So the loop will always run only one time.
If you want to write only zero bytes to a memory region, use memset
memset(currentString, 0, sizeof(currentString));
will set the memory region as pointed to by currentString pointer with sizeof(currentString) bytes to zeros.
Setting the first byte to zero:
currentString[0] = '\0';
maybe considered enough to "clear a string".
Setting the first byte to '\0' wont clear out the currentString.You may think that because ANSI C thinks that is a string terminator and if you print your string it will show empty.But if you check the second byte you will see the second char from your string. As other's said the best option to wipe out the string is:
memset(currentString, 0, sizeof(currentString));
And is way safer and faster.Also in ANSI C 0 and '\0' are the same.
to zero the whole array
char arr[SOMESIZE];
/* ... */
memset(arr, 0, sizeof(arr));
pointer - you need to know the size of the allocated memory as sizeof will return the size of the pointer itself only, not the referenced object;
char *p = malloc(SIZE);
/* ..... */
memset(p, 0 , SIZE);
It is never a good decision to calculate anything again and again. Instead you should calculate the strlen() only once.
That being said, in your case, doing so will solve the problem, as the reason it didn't work was that strlen() returned 0 right after the first round, since the length of the string became 0.
int n = strlen(currentString);
for (int k = 0; k < n; k++)
{
currentString[k] = '\0';
}

Swapping elements of char array in C

I have this code:
char *sort(char *string){ //shell-sort
int lnght = length(string) - 1; // length is my own function
int gap = lnght / 2;
while (gap > 0)
{
for (int i = 0; i < lnght; i++)
{
int j = i + gap;
int tmp =(int)string[j];
while (j >= gap && tmp > (int)string[j - gap])
{
string[j] = string[j - gap]; // code fails here
j -= gap;
}
string[j] = (char)tmp; // and here as well
}
if (gap == 2){
gap = 1;
}
else{
gap /= 2.2;
}
}
return string;
}
The code should sort (shell-sort) the characters in the string, given the ordinal value (ASCII value). Even though the code is pretty simple, it still fails at lines I've commented - segmentation fault. I've spent plenty of time with this code and still can't find the problem.
As you say in comment , you call our function like this -
char *str = "test string";
sort(str);
String literal is in read-only memory and creates a pointer str to that, thus it cannot be modified , and your function modifies it . Therefore ,it can result in segmentation fault .
Declare like this -
char str[] = "test string";
In situations like this look at your statements not so much as executable code, but as mathematical boundary conditions. I've replaced the monstrous name lnght with length for readability purposes.
Here are the relevant conditions that affect the value of j when entering the while loop, relative to the length.
i < length;
gap = length / 2;
j = i + gap;
Now we plug in a value. Consider the case where length == 10. Then presumably the maximum index in your array is 9 which is also the highest value that i can take on.
Then we also have that gap == 5 and so after entering the while loop j == i + gap == 9 + 5. Clearly 9 + 5 > 10. The rest is left as an exercise to the programmer.
How do you test your function? With a static string (i.e. char *buffer = "test string";) ?
Because on first loop at least j and j-gap should be inside the string boundaries. So if you get a segfault I guess it is because of a bad string (statics can't be modified).
Replacing length() by strlen() and calling it with a well-created test string lead me to a valid result:
"adgfbce" → "gfedcba"

realloc() seems to affect already allocated memory

I am experiencing an issue where the invocation of realloc seems to modify the contents of another string, keyfile.
It's supposed to run through a null-terminated char* (keyfile), which contains just above 500 characters. The problem, however, is that the reallocation I perform in the while-loop seems to modify the contents of the keyfile.
I tried removing the dynamic reallocation with realloc and instead initialize the pointers in the for-loop with a size of 200*sizeof(int) instead. The problem remains, the keyfile string is modified during the (re)allocation of memory, and I have no idea why. I have confirmed this by printing the keyfile-string before and after both the malloc and realloc statements.
Note: The keyfile only contains the characters a-z, no digits, spaces, linebreaks or uppercase. Only a text of 26, lowercase letters.
int **getCharMap(const char *keyfile) {
char *alphabet = "abcdefghijklmnopqrstuvwxyz";
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++) {
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
}
int letter;
int count = 0;
unsigned char c = keyfile[count];
while (c != '\0') {
int arr_count = charmap[c][0];
arr_count++;
charmap[c] = realloc(charmap[c], (arr_count+1)*sizeof(int));
charmap[c][0] = arr_count;
charmap[c][arr_count] = count;
c = keyfile[++count];
}
// Just inspecting the results for debugging
printf("\nCHARMAP\n");
for (int i = 0; i < 26; i++) {
letter = (int) alphabet[i];
printf("%c: ", (char) letter);
int count = charmap[letter][0];
printf("%d", charmap[letter][0]);
if (count > 0) {
for (int j = 1; j < count+1; j++) {
printf(",%d", charmap[letter][j]);
}
}
printf("\n");
}
exit(0);
return charmap;
}
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
You are writing beyond the end of your charmap array. So, you are invoking undefined behaviour and it's not surprising that you are seeing weird effects.
You are using the character codes as an index into the array, but they do not start at 0! They start at whatever the ASCII code for a is.
You should use alphabet[i] - 'a' as your array index.
The following piece of code is a source of troubles:
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++)
charmap[...] = ...;
If sizeof(int) < sizeof(int*), then it will be performing illegal memory access operations.
For example, on 64-bit platforms, the case is usually sizeof(int) == 4 < 8 == sizeof(int*).
Under that scenario, by writing into charmap[13...25], you will be accessing unallocated memory.
Change this:
int **charmap = malloc(26*sizeof(int));
To this:
int **charmap = malloc(26*sizeof(int*));

subtract 2 numbers using char arrays

I wanted to subtract two char arrays which have numeric values. I am doing it because I want to subtract big numbers. When I compile this program,it does not show any errors but in the execution it crashes.
I tried to do as following pseudo code
foreach character(right2left)
difference=n1[i]-n2[i]//here suppose they are integers
if(difference<0)
{
n1[i-1]--;
difference+=10;
}
result[i]=diff;
I wrote pseudo code for clarity.
int subtract(char *n1,char *n2,int n1Len,int n2Len){
int diff;
int max=n1Len;
char* res = (char*)malloc (max+2);
memset(res, '0', max +1);
res[max] = '\0';
int i=n1Len - 1, j = n2Len - 1, k = max;
for (; i >= 0 && j >=0; --i, --j, --k) {
if(i >= 0 && j>=0)
{
diff=(n1[i]-'0') - (n2[i]-'0') ;
if(diff<0)
{
int temp=n1[i-1]-'0';
temp=temp-1;
n1[i-1]=temp+'0';
diff+=10;
}
res[i]=diff+'0';
}
else
res[i]=n1[i];
}
return atoi(res);
}
int main(void) {
int t=subtract("55","38",2,2);
printf("%d\n", t);
}
There are a few visible mistakes. Hopefully these will provide you with some pointers:
You are passing string literals to the function & trying to modify them in the function. That is not valid and will most likely cause segmentation fault. Instead of int t=subtract("55","38",2,2); Maybe you can try:
char a[] = "55";
char b[] = "38";
int t=subtract(a,b,strlen(a), strlen(b));
max should be n1Len+1 to accommodate terminating NUL character in res char array. You can set it to 0 rather than '0' when initializing. res[max] = '\0'; invokes undefined behavior as you access out of bound element, get rid of it. So use memset(res,0,max) instead. Or use calloc instead of malloc+memset as suggested by #pmg.
Don't typecast return value of malloc or calloc when coding in C
for (; i >= 0 || j >=0; --i, --j, --k) should actually be for (; i >= 0 && j >=0; --i, --j, --k) as neither i nor j should be 0. You need to work on the function logic wherein i!=j.
diff=n1[i]-'0'+n2[i]-'0' should be diff=(n1[i]-'0') - (n2[i]-'0') as you are subtracting and not adding the digits
res[i]=diff is incorrect as you are setting the integer result as character value. Change it to res[i]=diff+'0' to set the character value
Hopefully this will get you started.
Hope this helps!
char* res = (char*)malloc (max);
memset(res, '0', max-1); // set the result to all zeros
res[max] = '\0';
Let's say max is 3.
You set res[0], and res[1] to 0. Then you set the inexistent res[3] to 0.
res[2] is still uninitialized.
Try calloc instead, and don't forget space for the zero string terminator :)
Also, casting the return value from malloc (or calloc) is, at best, redundant and may hide an error the compiler would have caught if the cast wasn't there.
char *res = calloc(max + 1, 1); // allocate and initialize to 0
This
diff=n1[i]-'0'+n2[i]-'0';
should be the difference
diff = (n1[i] - '0') - (n2[j] - '0');
(besides subtracting and not adding, the index for n2 ought to be j, I think). With adding, you can get non-digit characters in the result, and atoi() stops at the first of them, if that's the very first, it returns 0.
Also, you should check that n2 is indeed not longer than n1, or you'll write out of bounds.
diff=n1[i]-'0'+n2[i]-'0';
this does not give the difference.It should be
diff = (n1[i] - '0') - (n2[j] - '0');

Resources