0

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

sample output

byrnesj1
  • 189
  • 1
  • 14
  • 2
    If you have not tried that so far, this may be a good time to get familiar with a debugger. Side note: `"something"` has a `\0` at its end already (otherwise the `printf`-s would need that too, but they do not) – tevemadar Dec 18 '18 at 09:51
  • 1
    Not sure what you're trying to do with `*++(a2[i]);` but it doesn't look right and dereferencing it does nothing. – Chris Turner Dec 18 '18 at 09:53
  • My thinking with *++(a2[i]) is that a2[i] returns a pointer to a string, ++a2[i] increments the pointer to point to the next char in said string, and *++(a2[i]) produces the value of that char. – byrnesj1 Dec 18 '18 at 10:02
  • That is all correct, but you don't store the value of that char anywhere so dereferencing is pointless and incrementing the pointer means you're altering the contents of `arr2` so the list of words you're checking against will become useless over time – Chris Turner Dec 18 '18 at 10:32
  • I see what you're saying about the dereferencing being pointless, but why does incrementing a2 alter arr2. My thinking was I could have a2 and arr2 point to the beginning of the string array and use a2 to parse through the array during a function call, with arr2 remaining unchanged so it can serve as reference point to the beginning of the array in subsequent calls. If incrementing a2 alters arr2 then this logic is completely wrong. – byrnesj1 Dec 18 '18 at 10:51
  • 1
    @byrnesj1 See the answer here: [Pointer expressions: *ptr++, *++ptr and ++*ptr](https://stackoverflow.com/questions/18481740/pointer-expressions-ptr-ptr-and-ptr). It should clarify quickly why it would degrade your literal array. To be clear, because you pass a `char **` type, you have read access to the array, but can still modify its elements. Unless the exercise is to use pointer arithmetic, it would be best to adopt 2d-array notation. – Sean Monroe Dec 18 '18 at 11:15

3 Answers3

1

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.

Blaze
  • 16,736
  • 2
  • 25
  • 44
0

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/.

0___________
  • 60,014
  • 4
  • 34
  • 74
0

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++)
Chris Turner
  • 8,082
  • 1
  • 14
  • 18