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"
Related
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;
}
This function is basically just supposed to compare 2 strings and return their ASCII difference if they are different. It works perfectly fine when I compile it with the GCC compiler, but when I run it through the online compiler that is used to upload our classes homework, I get this error message:
Error near line 98: Reading an uninitialized value from address 10290
Line 98 is marked in the below code. I am not quite sure what the problem is and how I'm supposed to fix it. Does anyone have an idea?
int stringCompare(char * pStr1, char * pStr2) {
int n = 100;
int difference;
for (int i = 0; i < n; i++) {
difference = pStr1[i] - pStr2[i]; // line 98
if (difference != 0) {
return difference;
}
}
return difference;
}
Your code can skip over EOLN, if string equals, and try to compare memory after end of lines. To fix this, you need instantly return, if both string equals, and you see EOLN char '\0' in both strings at position i. Try my fix:
int stringCompare(char * pStr1, char * pStr2) {
int n = 100;
int difference;
for (int i = 0; i < n; i++) {
difference = pStr1[i] - pStr2[i];
if (difference != 0 || pStr1[i] == '\0') {
return difference;
}
}
return difference;
}
The problem in your code is that you fail to check the real length of the strings before indexing them. You are iterating with i from 0 to 99, but you do not check for the NUL terminator (\0) that marks the end of a string and therefore your for loop goes beyond the end of the string resulting in undefined behavior accessing memory that is not supposed to (which is what the error is telling you).
The correct way to iterate over a string, is not to loop a fixed amount of cycles: you should start from index 0 and check each character of the string in the loop condition. When you find \0, you stop. See also How to iterate over a string in C?.
Here's a correct version of your code:
int stringCompare(char *pStr1, char *pStr2) {
size_t i;
for (i = 0; pStr1[i] != '\0' && pStr2[i] != '\0'; i++) {
if (pStr1[i] != pStr2[i])
break;
}
return pStr1[i] - pStr2[i];
}
You could even write this more concisely with a simple while loop:
int stringCompare(char *pStr1, char *pStr2) {
while (*pStr1 && *pStr1 == *pStr2) {
pStr1++;
pStr2++;
}
return *pStr1 - *pStr2;
}
Of course, both the above functions expect two valid pointers to be passed as arguments. If you also want to allow invalid pointers you should check them before starting the loop (though it does not seem like you want to do that from your code).
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';
}
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))?
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');