1

I am a beginner in C programming, and this is a string matching code I wrote. It aims to find the positions where the substring t appears in the string s and print them. The use of pointers is required.This code scored 94 out of 100 in the OJ test.

#include <stdio.h>
#include <stdlib.h>

int main () {
    char *s = malloc(100005);
    char *t = malloc(100005);

    scanf ("%s%s", s, t);

    char *ptrS = s;
    char *ptrT = t;

    if (s == NULL) {
        free(t);
        return 1;
    }
    if (t == NULL) {
        free(s);
        return 1;
    }

    while ( *ptrS != '\0') {
        if (*ptrT == *ptrS) {
            ptrT++;
            ptrS++;
        } else if (*ptrS != *(ptrT = t)) {
            ptrS++;
        }

        if (*ptrT == '\0') {
            printf("%d ", (ptrS - s) - (ptrT - t));
            ptrS = ptrS - (ptrT - t) + 1;
            ptrT = t;
        }
    }

    free(t);
    free(s);

    return 0;
}

I have tried many test cases that I could think of, and it gives the correct results for all of them. I hope to find any bugs or any test cases that cause it to error.

12
  • Some things to think about: What does scanf do if malloc returns NULL? What is value of s or t if scanf returns value which is not 2? What is value of s or t if input string is 100005 characters or longer?
    – user694733
    Commented Nov 26, 2024 at 12:16
  • Try s = "ababac" and t = "abac".
    – interjay
    Commented Nov 26, 2024 at 12:35
  • @Elliott T You could use standard C function strstr. Commented Nov 26, 2024 at 12:53
  • GOT IT ! Thanks to @interjay. Logic error inelse if ( ) { }
    – Elliott T
    Commented Nov 26, 2024 at 12:55
  • @Elliott T It is unclear how many positions should be outputed for strings "aaaa" and "aa". Two or three? Commented Nov 26, 2024 at 13:04

2 Answers 2

0

There are several logical errors in your code.

The first one is that before using the function scanf you shall check that pointers s and t are not null pointers that is that memory for the character arrays was successfully allocated.

You could write for example

char *s = NULL;
char *t = NULL;

if ( ( s = malloc( 100005 ) ) != NULL && ( t = malloc( 100005 ) ) != NULL )
{
    // process the arrays
}

free( t );
free( s );

In the call of scanf you should specify the field width like

if ( scanf( "%100004s %100004s", s, t ) == 2 )
{
    // process the inputed strings
}

The inner if statement in the else part of this if statement

if (*ptrT == *ptrS) {
    ptrT++;
    ptrS++;
} else if (*ptrS != *(ptrT = t)) {
    ptrS++;
}

must be written for example like

if (*ptrT == *ptrS)
{
    ptrT++;
    ptrS++;
}
else if ( *ptrT != '\0' )
{
    ptrS = ptrS - ( ptrT - t ) + 1;
    ptrT = t;
}

if (*ptrT == '\0')
{
    printf( "%td ", ( ptrS - s ) - ( ptrT - t ) );

    ptrS = ptrS - ( ptrT - t ) + 1;
    ptrT = t;
}

That is the logic of the assignment of the pointer PtrS must be the same as inside this if statement

if (*ptrT == '\0') {
    printf("%d ", (ptrS - s) - (ptrT - t));
    ptrS = ptrS - (ptrT - t) + 1;
    ptrT = t;
}

Bear in mind that there is a duplicated code in your program.

Difference of two pointers has type ptrdiff_t. So you need to use length modifier t in the format string of the call of printf

printf("%td ", (ptrS - s) - (ptrT - t));
        ^^^

And after the while loop you should insert the following call

putchar( '\n' );

Using such expressions like for example that

(ptrS - s) - (ptrT - t)

makes your code difficult to read. Usually such codes contain bugs as a rule.

Instead of your while loop I would write something like the following

for ( const char *ptrS = s; *ptrS != '\0'; ++ptrS )
{
    if ( *ptrS == *t )
    {
        const char *ptrT = t;

        while ( *ptrT != '\0' && *ptrT == *( ptrS + ( ptrT - t ) ) )
        {
            ++ptrT;
        }

        if ( *ptrT == '\0' )
        {
            printf( "%td ", ptrS - s );
        }
    }
}

putchar( '\n' );

Within the loops there are used pointers to const char because the loops do not change the processed strings.

Pay attention to that instead of manually written loops you could use standard C function strstr together with strchr. And you could initially check that the length of the string stored in the array pointed to by the pointer s is not less than the length of the string stored in the array pointed to by the pointer t.

5
  • The expression *( ptrS = ( ptrS - ( ptrT - t ) + 1 ) ) != *( ptrT = t ) is undefined behavior due to unsequenced writing and reading of ptrT.
    – interjay
    Commented Nov 26, 2024 at 15:29
  • @interjay Oh, you are right. I will update teh answer, Commented Nov 26, 2024 at 15:35
  • it's very helpful. thanks a lot for your detailed explanation@VladfromMoscow
    – Elliott T
    Commented Nov 26, 2024 at 16:14
  • @ElliottT No at all. We, beginners, should help each other.:) Commented Nov 26, 2024 at 16:16
  • @interjay How about the updated code snippet? Commented Nov 26, 2024 at 16:18
0

A wrong cases offered in comments: s = ababac, t = abac.

According to the logic of the original code, the pointer s should move back by a certain position each time in the else{} statement, but the original code overlooks it.

I rewrite it like :

        if (*ptrT == *ptrS) {
            ptrT++;
        } else {
            ptrS -= (ptrT - t);
            ptrT = t;
        }
        ptrS++;

it works.

6
  • That shall not work. If *ptrT is not equal to *ptrS then you shall not change the pointer ptrS if *ptrT is equal to '\0'. Otherwise the output will be incorrect. See my answer. Pay attention to that you should not write such answers. It could be a comment. Commented Nov 26, 2024 at 16:33
  • @VladfromMoscow, sorry, I'm not so familiar of rules here. But I suppose the code actually works here. The case you mention that" if *ptrT is != *ptrS, and if *ptrT == '\0', the pointer ptrS should not move" will not exist. Because when it comes to the cause if(*ptrT == '0'), the last *ptrT must be equal to the last *ptr, they will not come to the cause 'else if'. In another word, when *ptrT != *ptrS, *ptrT cannot be '\0', because the work we do in the last run of the loop ensures it.
    – Elliott T
    Commented Nov 26, 2024 at 17:25
  • It's a little bit confusing here. But now that both of the codes in the two answers score 100 out of 100 in the Online Judgement test, they might be equivalent. I' ve tested them on my own computer too.
    – Elliott T
    Commented Nov 26, 2024 at 17:35
  • It seems you are right.. The problem is that your code difficult to read. In my opinion the approach I showed in my answer with the for loop is more readable and clear. Commented Nov 26, 2024 at 17:37
  • @VladfromMoscow, That's true, my original code was indeed hard to read. As in your answer, iterating through s to match t is a very natural approach. I can't quite explain why I came up with such kind of strange method in the original code; perhaps it's because I'm not yet very familiar with pointers. This code is an assignment from my first time learning pointers.
    – Elliott T
    Commented Nov 26, 2024 at 17:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.