I have the task of going through a binary search tree and collecting the keys in every node and storing them in an array. After this, I have to set the last element of this array as NULL. This is all done inside a function, which then returns this array of *char.
I believe the part about collecting the keys and storing them in the array is working well, as I've tested that enough. However, I'm not able to define the last element of the array as NULL correctly. I thought it should be done like this:
list_keys[tree_size(tree)] = NULL;
However, after some printf's, I realized that this was cleaning my tree. When I print tree_size(tree) before this line, it gives me the size of the tree correctly. However, when I do it after that line, it gives me 0. I don't believe that the problem is in the function tree_size() because when I attempt to access an element of the tree before that line, it works well, but after the line, I get a Segmentation fault (core dumped) error, probably due to trying access something that doesn't exist anymore.
I have no idea what's wrong, so any help is appreciated. Thanks in advance.
EDIT:
The tree is of the type tree_t and it's defined as:
struct tree_t {
struct entry_t *entry;
struct tree_t *right_child;
struct tree_t *left_child;
};
The tree_size() is defined as:
int tree_size(struct tree_t *tree) {
if (tree->entry != NULL) {
//if two children exist
if (tree->left_child != NULL && tree->right_child != NULL) {
return 1 + tree_size(tree->left_child) + tree_size(tree->right_child);
}
//there's a right child and there isnt a left child
if (tree->right_child != NULL && tree->left_child == NULL) {
return 1 + tree_size(tree->right_child);
}
//there's a left child and there isnt a right child
if (tree->left_child != NULL && tree->right_child == NULL) {
return 1 + tree_size(tree->left_child);
}
//there are no children
if (tree->left_child == NULL && tree->right_child == NULL) {
return 1;
}
}
//if the entry is empty
return 0;
}
Basically, what I'm doing right now is:
First, I define list_keys and allocate memory:
char **list_keys;
list_keys = (char *)malloc((tree_size(tree)+1)*sizeof(char));
Then, I call an auxiliary function tree_get_keys_aux(tree, list_keys, 0) that will do the initial part I mentioned. It is defined as:
void tree_get_keys_aux(struct tree_t *tree, char **list_keys, int index) {
//N
list_keys[index] = (char *)malloc((strlen(tree->entry->key))*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
index = index + 1;
//L
if (tree->left_child != NULL) {
tree_get_keys_aux(tree->left_child, list_keys, index);
}
//R
if (tree->right_child != NULL) {
tree_get_keys_aux(tree->right_child, list_keys, index);
}
return;
}
Then, I do the line that's bringing me problems,
list_keys[tree_size(tree)] = NULL;
And lastly,
return list_keys;
Firstly,
list_keys = (char *)malloc((tree_size(tree)+1)*sizeof(char));
is wrong. The elements are char*, so you have to allocate for that or you will cause out-of-range access.
It should be:
list_keys = malloc((tree_size(tree)+1)*sizeof(char*));
or
list_keys = malloc((tree_size(tree)+1)*sizeof(*list_keys));
See also: c - Do I cast the result of malloc? - Stack Overflow
Secondly,
list_keys[index] = (char *)malloc((strlen(tree->entry->key))*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
is wrong. You have to allocate one more element for terminating null-character.
It should be:
list_keys[index] = malloc((strlen(tree->entry->key) + 1)*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
Thirdly, tree_get_keys_aux is not recoginizing number of elements in left child and data from left child will be overwritten by data from right child.
To avoid this overwriting, you can use tree_size to determint the tree size and advance index according to that.
//L
if (tree->left_child != NULL) {
tree_get_keys_aux(tree->left_child, list_keys, index);
index += tree_size(tree->left_child); // add this
}
//R
if (tree->right_child != NULL) {
tree_get_keys_aux(tree->right_child, list_keys, index);
}
EDIT:
I realize that the code in my OP is long and hard to read. I've highlighted the problem with 4 lines of code.
char **t = {"Hello", "World"};
char **a = t;
++(a[0]);
printf("%c\n",**t);
I want to increment through the array of strings without losing the pointer to the first character. Therefore, I initialize a new pointer 'a' to point to the first character. After I increment the 'a' pointer, though, it seems to change what 't' points to! In the printf statement, I expect that t's pointer value remain unchanged, but it seemed to increment with 'a' and now points to the second character. Why is this happening?
SOLVED:
In the above example, a and t seem to be the same pointer so if I change one (by incrementing for example), the change is also reflected in the pther. However, if I dereference t into another variable, then I can change said variable without having that change reflected in t. In the above example, this looks like
char *a = t[0];
++a;
printf("a value: %c\n", *a);
printf("t value: %c\n", **t);
I think that I had originally been confused about dereferencing since t points to a pointer. Every response I've gotten is to use array indexing as opposed to pointers, and I can see why.
ORIGINAL POST:
Say I have:
array1 {"arp", "live", "strong"}, and
array2 {"lively", "alive", "harp", "sharp", "armstrong"}
I'm trying to find the strings in array1 that are substrings of any string in array2.
To do this, I wrote a helper function (compString) that takes in the string from array1, the entire array2, and the length of array2.
Essentially, what the function does is create local pointer values for both the string pointer and the array pointer. It then extracts the first string from array2 and begins to walk through it to find a match for the first letter of the input string. If no match is found, the function will move on to the next string, until a full match is found or until it walks through the entire array2. It then returns to its calling environment.
I ran into some unexpected behavior. When I call the function (with the same arguments), after having already called it, the array pointer seems to point to exactly where it left off in the previous call.
For example, if I call compString("arp", array2, 5) then the function will flag a match starting at the a in harp.
Then, if I call compString("live", array2, 5), the function begins at the a in harp and goes to the end of the array without flagging a match.
Finally, when I call compString("strong", array2, 5), array2 is now pointing to garbage since it has already been iterated through, and does not flag a match.
Since one of the first things the helper function does is "localize" the pointers being passed (that is, create a local pointer variable and assign to it the value of the pointer being passed to the funcion, then iterate that local variable), I would assume that subsequent calls to the function wouldn't "save" the previous value of the pointer. Any pointers?
Source attached:
#include <stdio.h>
#include <string.h>
int compString(char *, char **, int);
int main(void)
{
int sz1 = 3;
int sz2 = 5;
char *p, *p2;
char *array1[] = {"arp\0", "live\0", "strong\0"};
char *array2[] = {"lively\0", "alive\0", "harp\0", "sharp\0", "armstrong\0"};
compString("arp\0",array2,5);
compString("live\0",array2,5);
compString("strong\0",array2,5);
}
int compString(char *arr1, char **arr2, int sz2)
{
printf("\n\n\n");
printf("WORD: %s\n",arr1);
int i = 0;
char *a1 = arr1;
char **a2 = arr2;
char *p;
char *p2;
printf("BEGIN ITERATION %d\n",i);
printf("Checking against word: %s\n",a2[i]);
while (i < sz2)
{
printf("%c\n",*a2[i]);
if (*a1 == *a2[i])
{
char *p = a1;
char *p2 = a2[i];
while ((*p == *p2) && (*p != '\0'))
{
++p;
++p2;
}
if (*p == '\0')
{
return 1;
}
else
{
*++(a2[i]);
if (*(a2[i]) == '\0')
{
++i;
printf("BEGIN ITERATION %d\n",i);
printf("Checking against word: %s\n",a2[i]);
}
}
}
else
{
*++(a2[i]);
if (*(a2[i]) == '\0')
{
++i;
printf("BEGIN ITERATION %d\n",i);
printf("Checking against word: %s\n",a2[i]);
}
}
}
return 0;
}
Your loop is causing an off-by-one error. What you want to do is looping through your array of 5 strings, so from 0 to 4. We can see that when you run all three tests because they somehow depend on the result on eachother (I didn't look into the comparing logic too much, it seems rather obfuscated).
We can replicate the behavior with just one test:
compString("test", array2, 5);
So the 5 is supposed to tell it to loop from 0 to 4. In the comparison function, you have this:
int i = 0;
printf("BEGIN ITERATION %d\n", i);
printf("Checking against word: %s\n", a2[i]);
while (i < sz2)
So far, so good. The i < sz2 is correct, it supposedly loops from 0 to 4, assuming you increase i correctly.
Then, however, you do this somewhere at the end of the function:
++i;
printf("BEGIN ITERATION %d\n", i);
printf("Checking against word: %s\n", a2[i]);
So when i is 4, you increase it to 5, and at that point the function should stop looping through the array, but at that point you do that print that tries to access a2[5], which doesn't exist. That's where it crashes for me on MSVC.
My suggestion is that you rework your loop logic to something like this:
for (int i = 0; i < sz2, i++){
printf("BEGIN ITERATION %d\n", i);
printf("Checking against word: %s\n", a2[i]);
// do something with a2[i] and don't manually change the value of "i"
}
Also, I would tidy up that string logic, there might be a bug in it somewhere. You don't need all those suspicious dereferencing calls. When you want to access character x of string y in a2, then a2[y][x] does the trick. For instance, if you want to find some letter, simply do:
for (int n = 0; n < strlen(a2[y]), n++){
if (a2[y][n] == 'a')
printf("found letter 'a' at position %d\n", n);
}
Furthermore, you don't need to add \0 to string literals. Those are automatically added, so you're just adding a second one. Instead of this:
char *array1[] = {"arp\0", "live\0", "strong\0"};
Do this:
char *array1[] = {"arp", "live", "strong"};
Also, I don't know if you have to implement this function because it's a task you've been given, but if you just want to find substrings, then you don't need to reinvent the wheel as strstr already does that.
are you looking for something like this:
char *array1[] = {"arp", "live", "strong", NULL};
char *array2[] = {"lively", "alive", "harp", "sharp", "armstrong", NULL};
void findsrings(char **neadles, char **haystack)
{
while(*neadles)
{
char **hay = haystack;
size_t pos = 0;
printf("Searching for %s\n", *neadles);
while(*hay)
{
if(strstr(*hay, *neadles))
{
printf("Found!! Haystack word is: %s at index %zu in haystack\n", *hay, pos);
}
pos++;
hay++;
}
neadles++;
}
}
int main()
{
findsrings(array1, array2);
return 0;
}
you do not need the '\0' at the end of the string literals as they are added automatically by the C compiler. I have added NULL wihch terminates the array of the string pointers - so you do not need to provide the sizes of the arrays/.
As mentioned in the comments the side effect you've noticed, is due to this line *++(a2[i]); which is altering the contents of your second array. As time progresses you'll eventually end up with the second array having no actual words in it.
Generally your code is overly complicated and you're using while loops when for loops are better suited.
The outer loop for example would work better as:
for(i=0;i<sz2;i++)
{
printf("BEGIN ITERATION %d\n",i);
printf("Checking against word: %s\n",arr2[i]);
And then since you want to check each substring in arr2[i], you can use a for loop for that...
for(wordstart=arr2[i];*wordstart!='\0';wordstart2++)
{
Finally, you have an inner loop that compares each character of arr1 with the substring defined by the wordstart. You need to make sure that neither p1 or p2 goes beyond the end of their respective strings and that they point to the same character.
for(p1=arr1,p2=wordstart;(*p1!='\0')&&(*p2!='\0')&&(*p1==*p2);p1++,p2++);
Once any of those 3 conditions is no longer true, if you check that p1 has reached the end of the string, you know that it must have found a substring.
if(*p1=='\0')
{
printf("Matched %s\n",arr2[i]);
return 1;
}
The resulting function looks like:
int compString(char *arr1, char **arr2, int sz2)
{
printf("\n\n\n");
printf("WORD: %s\n",arr1);
int i = 0;
char *p1;
char *wordstart;
char *p2;
for(i=0;i<sz2;i++)
{
printf("BEGIN ITERATION %d\n",i);
printf("Checking against word: %s\n",arr2[i]);
for(wordstart=arr2[i];*wordstart!='\0';wordstart++)
{
for(p1=arr1,p2=wordstart;(*p1!='\0')&&(*p2!='\0')&&(*p1==*p2);p1++,p2++);
if(*p1=='\0')
{
printf("Matched %s\n",arr2[i]);
return 1;
}
}
}
return 0;
}
Other things to note is that you don't need to implicitly add the \0 to a string. The following is just fine.
char *array1[] = {"arp", "live", "strong"};
You could also add NULL as the last element in the list of strings so that you don't need to track how many strings there are.
char *array2[] = {"lively", "alive", "harp", "sharp", "armstrong"};
which means the outer loop could be simplified to
for(i=0;arr2[i];i++)
I'm trying various code to check Palindrome words, sure there are so many ways to code it, I somehow find something that triggers my curiosity but I couldn't find any answer somewhere although the code run good
That's found that there's a slight differences between two array of char wordReverse declaration below.
Could anyone give an explanation of these two declarations?
bool checkPallen(char word[]){
char wordReverse[25] = ""; //error if used char wordReverse[25];
int revCount = 0;
for(int i = strlen(word) - 1; i >= 0; i--){
wordReverse[revCount] = word[i]; //
revCount++;
}
if(strcmp(wordReverse, word) == 0) return true;
return false;
}
The difference is that uninitialized local variables have indeterminate values.
When you read from wordReverse,
strcmp(wordReverse, word)
strcmp takes two strings, i.e. it expects to find a NUL terminator somewhere.
Your loop that fills wordReverse doesn't terminate it, so you get undefined behavior here.
Fix:
wordReverse[revCount] = '\0';
after the loop.
The version that initializes wordReverse as
char wordReverse[25] = "";
works because it is equivalent to char wordReverse[25] = { '\0' }, which sets the first element to '\0' explicitly and all remaining elements to '\0' implicitly.
NB:
if (X) return true;
return false;
is equivalent to
return !!X; // Returns 1 if and only if X is not 0, and 0 otherwise
I've got an issue i can't handle so i've thought maybe you can help me. Basically i have a function that receives a char* as a paramater and does some stuff to it(i've checked those steps/functions and they work just fine).If the char* given in the function is ""(i guess NULL), i receive seg fault with assert.
Here is the code:
char *computeRNA(char *s)
{
if (s != NULL && s!= "")
{
Lista* l = to_list(s);
int i;
l = l->next;
for(i = 0; i<= lungime(l); ++i)
{
if(l->info == 'T')
l->info = 'U';
l = l->next;
}
char *rna = to_pointer(l);
return rna;
}
return NULL;
}
And here is the assert:
char *s;
s = computeRNA("");
ASSERT(!strcmp(s, ""), "computeRNA-01");
free(s);
This is a school homework so i can not change assert's code , only the function.Thanks in advance !
This ASSERT that you weren't supposed to be changing, tests that if an empty string is given to computeRNA it should also return an empty string:
s = computeRNA("");
ASSERT(!strcmp(s, ""), "computeRNA-01");
Your code returns null pointer. Also comparing strings needs to be done using strcmp, s == "" would only compare pointer values that wouldn't be of use here.
Thus I'd start the function with
// null pointer was given
if (!s) {
return calloc(1, 1);
}
// an empty string was given (the first character pointed to b s is the string terminator.)
// to compare explicitly, you need to do `strcmp(s, "")` == 0. `s == ""` is wrong.
if (!*s) {
return "";
}
You cannot pass a NULL pointer to strcmp() as argument. Before the ASSERT statement, just check for non-NULL value of s. Something like
if (s) {
ASSERT(!strcmp(s, ""), "computeRNA-01");
}
should do the job.
EDIT:
In case changing the function using the assertion is also not possible, you can modify the computeRNA() function to return an empty string, instead of NULL, like
char * empty_string = calloc(1,1);
return empty_string;
which can later be passed to free() in the caller.
Can't seem to find the answer on any question here.
Basically I have this line of code:
qsort(Code_Lines, number_of_lines, sizeof(char*), myCmp);
when Code_Lines is a char** - points to an array of char*s when each one contains a string OR pointing to NULL. What I want to do is to sort all of the strings alphabetically when the strings containing NULL will be at the END of the array.
Each string within the Code_Lines will be sorted by its 4 first characters (it's unequal in length of each string - and the first four characters are always different - mentioning a number from 0001 to 9999), if it's NULL it will just put it in the end of the array.
The variable number_of_lines is containing the number of lines (code lines) that are in the array, AKA - number of elements (strings, in this case) in the array.
myCmp is my compare function and I wrote it this way:
int myCmp(const void* element1, const void* element2)
{
int return_value;
if(!element1) //element1 of them is NULL
{
return_value = 1;
}
else if(!element2) //element 2 is NULL
{
return_value = -1;
}
else
{
return_value = strncmp(*((char**)element1), *((char**)element2), 4);
}
return return_value;
}
Any idea what the problem might be ? The program just crashes there.
The function works when the array isn't containing NULL but fails when it does...
In a qsort comparison function, the arguments are pointers to the elements of the array you provided, not the elements themselves. The elements being compared obviously exist, so these pointers can never by NULL by definition. What you want to check is whether a particular element is NULL, not the pointer to element:
int myCmp(const void* element1, const void* element2)
{
int return_value;
char * e1 = *(char **) element1;
char * e2 = *(char **) element2;
if(!e1) //element1 of them is NULL
{
return_value = 1;
}
else if(!e2) //element 2 is NULL
{
return_value = -1;
}
else
{
return_value = strncmp(e1, e2, 4);
}
return return_value;
}