subtract 2 numbers using char arrays - c

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');

Related

Something's wrong with my reverse string function

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';
}

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';
}

Print a Char Array of Integers in C

For class, I am required to create a function that converts an Integer into it's corresponding Binary number. However, I am forced to use the given main and parameters for the to_binary function. The whole problem requires me to print out the 32 bit binary number, but to break it up, I am just trying to print out the Char Array, that I thought I filled with Integers (perhaps the issue). When I do compile, I receive just a blank line (from the \n) and I am wondering how I can fix this. All I want to do is to be able to print the binary number for 5 ("101") yet I can't seem to do it with my professor's restrictions. Remember: I cannot change the arguments in to_binary or the main, only the body of to_binary. Any help would be greatly appreciated.
#include<stdio.h>
void to_binary(int x, char c[]) {
int j = 0;
while (x != 0) {
c[j] x = x % 2;
j++;
}
c[33] = '\0';
}
int main() {
int i = 5;
char b[33];
to_binary(i,b);
printf("%s\n", b);
}
This is the answer to your question.
void to_binary(int x, char c[]) {
int i =0;
int j;
while(x) {
/* The operation results binary in reverse order.
* so right-shift the entire array and add new value in left side*/
for(j = i; j > 0; j--) {
c[j] = c[j-1];
}
c[0] = (x%2) + '0';
x = x/2;
i++;
}
c[i]=0;
}
the problem is in the code below:
while (x != 0) {
c[j] = x % 2; // origin: c[j] x = x % 2; a typo?
j++;
}
the result of x % 2 is a integer, but you assigned it to a character c[j] —— integer 1 is not equal to character '1'.
If you want to convert a integer(0-9) to a character form, for example: integer 7 to character '7', you can do this:
int integer = 7;
char ch = '0' + integer;
One of the previous answers has already discussed the issue with c[j] x = x % 2; and the lack of proper character conversion. That being said, I'll instead be pointing out a different issue. Note that this isn't a specific solution to your problem, rather, consider it to be a recommendation.
Hard-coding the placement of the null-terminator is not a good idea. In fact, it can result in some undesired behavior. Imagine I create an automatic char array of length 5. In memory, it might look something like this:
Values = _ _ _ _ _
Index = 0 1 2 3 4
If I were to populate the first three indexes with '1', '0', and '1', the array might look like so:
Values = 1 0 1 _ _
Index = 0 1 2 3 4
Let's say I set index 4 to contain the null-terminator. The array now looks like so:
Values = 1 0 1 _ \0
Index = 0 1 2 3 4
Notice how index three is an open slot? This is bad. In C/C++ automatic arrays contain garbage values by default. Furthermore, strings are usually printed by iterating from character to character until a null-terminator is encountered.
If the array were to look like it does in the previous example, printing it would yield a weird result. It would print 1, 0, 1, followed by an odd garbage value.
The solution is to set the null-terminator directly after the string ends. In this case, you want your array to look like this:
Values = 1 0 1 \0 _
Index = 0 1 2 3 4
The value of index 4 is irrelevant, as the print function will terminate upon reading index 3.
Here's a code example for reference:
#include <stdio.h>
int main() {
const size_t length = 4;
char binary[length];
size_t i = 0;
while (i < length - 1) {
char c = getchar();
if (c == '0' || c == '1')
binary[i++] = c;
}
binary[i] = '\0';
puts(binary);
return 0;
}
#include<stdio.h>
int binary(int x)
{
int y,i,b,a[100];
if(x<16)
{
if(x%2==1)
a[3]=1;
if(x/2==1||x/2==3 || x/2==5 || x/2==7)
a[2]=1;
if(x>4 && x<8)
a[1]=1;
else if(x>12 && x<16)
a[1]=1;
if(x>=8)
a[0]=1;
}
for(i=0;i<4;i++)
printf("\t%d",a[i]);
printf("\n");
}
int main()
{
int c;
printf("Enter the decimal number (less than 16 ):\n");
scanf("%d",&c);
binary(c);
}
this code might help it will simply convert the decimal number less than 16 into the 4 digit binary number.if it contains any error than let me know

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"

longest common subsequence: why is this wrong?

int lcs(char * A, char * B)
{
int m = strlen(A);
int n = strlen(B);
int *X = malloc(m * sizeof(int));
int *Y = malloc(n * sizeof(int));
int i;
int j;
for (i = m; i >= 0; i--)
{
for (j = n; j >= 0; j--)
{
if (A[i] == '\0' || B[j] == '\0')
X[j] = 0;
else if (A[i] == B[j])
X[j] = 1 + Y[j+1];
else
X[j] = max(Y[j], X[j+1]);
}
Y = X;
}
return X[0];
}
This works, but valgrind complains loudly about invalid reads. How was I messing up the memory? Sorry, I always fail at C memory allocation.
The issue here is with the size of your table. Note that you're allocating space as
int *X = malloc(m * sizeof(int));
int *Y = malloc(n * sizeof(int));
However, you are using indices 0 ... m and 0 ... n, which means that there are m + 1 slots necessary in X and n + 1 slots necessary in Y.
Try changing this to read
int *X = malloc((m + 1) * sizeof(int));
int *Y = malloc((n + 1) * sizeof(int));
Hope this helps!
Series of issues. First, as templatetypedef says, you're under-allocated.
Then, as paddy says, you're not freeing up your malloc'd memory. If you need the Y=X line, you'll need to store the original malloc'd space addresses in another set of variables so you can call free on them.
...mallocs...
int * original_y = Y;
int * original_x = X;
...body of code...
free(original_y);
free(original_x);
return X[0];
But this doesn't address your new question, which is why doesn't the code actually work?
I admit I can't follow your code (without a lot more study), but I can propose an algorithm that will work and be far more understandable. This may be somewhat pseudocode and not particularly efficient, but getting it correct is the first step. I've listed some optimizations later.
int lcs(char * A, char * B)
{
int length_a = strlen(A);
int length_b = strlen(B);
// these hold the position in A of the longest common substring
int longest_found_length = 0;
// go through each substring of one of the strings (doesn't matter which, you could pick the shorter one if you want)
char * candidate_substring = malloc(sizeof(char) * length_a + 1);
for (int start_position = 0; start_position < length_a; start_position++) {
for (int end_position = start_position; end_position < length_a; end_position++) {
int substring_length = end_position - start_position + 1;
// make a null-terminated copy of the substring to look for in the other string
strncpy(candidate_substring, &(A[start_position]), substring_length);
if (strstr(B, candidate_substring) != NULL) {
longest_found_length = substring_length;
}
}
}
free(candidate_substring);
return longest_found_length;
}
Some different optimizations you could do:
// if this can't be longer, then don't bother checking it. You can play games with the for loop to not have this happen, but it's more complicated.
if (substring_length <= longest_found_index) {
continue;
}
and
// there are more optimizations you could do to this, but don't check
// the substring if it's longer than b, since b can't contain it.
if (substring_length > length_b) {
continue;
}
and
if (strstr(B, candidate_substring) != NULL) {
longest_found_length = end_position - start_position + 1;
} else {
// if nothing contains the shorter string, then nothing can contain the longer one, so skip checking longer strings with the same starting character
break; // skip out of inner loop to next iteration of start_position
}
Instead of copying each candidate substring to a new string, you could do a character swap with the end_position + 1 and a NUL character. Then, after looking for that substring in b, swap the original character at end_position+1 back in. This would be much faster, but complicates the implementation a little.
NOTE: I don't normally write two answers and if you feel that it is tacky, feel free to comment on this one and note vote it up. This answer is a more optimized solution, but I wanted to give the most straightforward one I could think of first and then put this in another answer to not confuse the two. Basically they are for different audiences.
The key to solving this problem efficiently is to not throw away information you have about shorter common substrings when looking for longer ones. Naively, you check each substring against the other one, but if you know that "AB" matches in "ABC", and your next character is C, don't check to see if "ABC" is in "ABC", just check that the spot after "AB" is a "C".
For each character in A, you have to check up to all the letters in B, but because we stop looking through B once a longer substring is no longer possible, it greatly limits the number of checks. Each time you get a longer match up front, you eliminate checks on the back-end, because it will no longer be a longer substring.
For example, if A and B are both long, but contain no common letters, each letter in A will be compared against each letter in B for a runtime of A*B.
For a sequence where there are a lot of matches, but the match length isn't a large fraction of the length of the shorter string, you have A * B combinations to check against the shorter of the two strings (A or B) leading to either A*B*A or A*B*B, which is basically O(n^3) time for similar length strings. I really thought the optimizations in this solution would be better than n^3 even though there are triple-nested for loops, but it appears to not be as best as I can tell.
I'm thinking about this some more, though. Either the substrings being found are NOT a significant fraction of the length of the strings, in which case the optimizations don't do much, but the comparisons for each combination of A*B don't scale with A or B and drop out to be constants -- OR -- they are a significant fraction of A and B and it directly divides against the A*B combinations that have to be compared.
I just may ask this in a question.
int lcs(char * A, char * B)
{
int length_a = strlen(A);
int length_b = strlen(B);
// these hold the position in A of the longest common substring
int longest_length_found = 0;
// for each character in one string (doesn't matter which), look for incrementally larger strings in the other
for (int a_index = 0; a_index < length_a - longest_length_found; a_index++) {
for (int b_index = 0; b_index < length_b - longest_length_found; b_index++) {
// offset into each string until end of string or non-matching character is found
for (int offset = 0; A[a_index+offset] != '\0' && B[b_index+offset] != '\0' && A[a_index+offset] == B[b_index+offset]; offset++) {
longest_length_found = longest_length_found > offset ? longest_length_found : offset;
}
}
}
return longest_found_length;
}
In addition to what templatetypedef said, some things to think about:
Why aren't X and Y the same size?
Why are you doing Y = X? That's an assignment of pointers. Did you perhaps mean memcpy(Y, X, (n+1)*sizeof(int))?

Resources