3

This lab is trying to show the use of coder-defined functions to execute the code, but I'm trying to do it alternatively so when we actually are tested on it I won't be freaking out that I just copied the source code.

#define NotFound -1
#define WordSize 20

int stringSearch(char * string, char array, int * letter);

int main(void)
{
    char * string = (char *) malloc(WordSize * sizeof(char));
    char tester = '\0';
    int index_tester = 0, i;

    // do
    // {
    //     printf("Enter a test string and character, enter q for the test string to exit.\n");

    //     printf("Test string: ");
    //     scanf("%s", string);
    //     while (getchar() != '\n') {}                                        
    //     if (strcmp(string, "q") == 0) {                                     
    //         break;
    //     }
    // } // ----> Is it possible to do a while or for look instead? loop here? 
    printf("What is the test string you wish to enter: ?");
    for (i = 0; i < sizeof(string); i++)
    {
        {
        scanf("%c", &string[i]);
        }
    }
    string[i] = '\0';
    puts(string);

    printf("Tester for the inputed string: ");
    scanf("%c", &tester);
    while (getchar() != '\n') {}

    int ResultofSearch = stringSearch(string, tester, &index_tester);

    if (ResultofSearch == NotFound)
    {
        printf("That letter is not foudn in the string, try again: ");
    }
    else {
            printf("Character found at index %d.\n\n", index_tester);
        }
    return 0;
}
int stringSearch(char * string, char array, int * letter)
{
    for (int i = 0; i < strlen(string); i++)
        {
            if (string[i] == array)
            {
                *letter = i;
                return (Found);
            }

        }
        return(NotFound);
}

When executing the code, I can put in the string, which I think is working fine, but it will automatically put in the search for some random letters immediately without prompting for the user input. I'm still a greenhorn to all this coding stuff so sorry in advance, any advice would be appreciated though

JayTC
  • 53
  • 5
  • 1
    Tip: You use the boolean values `Found`/`NotFound` to indicate success or not, but C already has a system of boolean: `0` and not `0`. It would be better to return `1` instead of `Found` and `0` instead of `NotFound`, allowing you to use `if (stringSearch(string, tester, &index_tester))` – ikegami Feb 22 '20 at 20:53
  • 1
    `sizeof(string)` doesn't do what yo think it does. `sizeof` works on the type of the parameter. Since you gave it a `char *` you will only get back `4` or `8` (or whatever pointer sizes are on your system). The only way to get the size of `malloc` memory is to track it yourself with a variable (`WordSize` in this case already has that size). – kaylum Feb 22 '20 at 20:53
  • @ikegami hehe I realized that too but its something we had to put into our code for this particular lab its pretty redundant but school :3 – JayTC Feb 22 '20 at 21:22
  • @kaylum Ah! That makes more sense, thank you I didn't realize that. So then from there I would be doing sizeof(Wordsize)? or would it be better to do the malloc function once again? – JayTC Feb 22 '20 at 21:24
  • 1
    Not `sizeof(Wordsize)`. Just `Wordsize`. The malloc creates an array of `char *` with `Wordsize` number of elements. So the `for` loop needs to iterate over the number of elements in the array which is `Wordsize`. Actually, you want `Wordsize-1` to ensure last element is left for the NUL terminator - otherwise will get buffer overflow. – kaylum Feb 22 '20 at 21:25
  • Also, with that for loop you would require the user to enter exactly 19 characters (no more, no less). Is that really what you want? – kaylum Feb 22 '20 at 21:28
  • @kaylum aaah that makes sense and no I want them to be able to input a string up to 19 characters, that's one thing I think caused an issue outside of just using sizeof. – JayTC Feb 22 '20 at 22:00
  • I like and see the answer provided , I'll use it and I'll experiment with what you said and try and do it the way I originally am trying to do as well and see if that works. – JayTC Feb 22 '20 at 22:02
  • @JayTC - I disagree somewhat with what @ikegami said about return values. Using macros and enums to describe what return values *really mean* instead of just 0 and 1 is a good thing that you should keep doing. Plain language is easier to read and understand than numbers that take an extra moment to parse and put in context. Renaming functions to make them declarative true or false statements can also help make `if` statements read like an English sentence (or whatever language you're working in), further aiding readability. – skrrgwasme Feb 22 '20 at 22:29
  • @skrrgwasme Appreciate the response, that was one thing my professor did talk about, as depending on the code, macros really allow for easier adaptation and interpretation from an outside source. I just think for how small this one is its meh but it does help me learn to incorporate macros further down the line – JayTC Feb 22 '20 at 22:51
  • @skrrgwasme, It makes things more error prone to use macros since we now have to learn this particular function's idiosyncrasies instead of relying on the standard. It also makes things *less* readable because it's more noisy, because we have to look up what macros this particular function uses instead of universal true/false, and because it's *unlike* English. In English, we don't say "if in string is yes"; we say "if in string" like I'm suggesting. – ikegami Feb 22 '20 at 23:00
  • 1
    Re "*Is it possible to do a while or for look instead? loop here?*", `do { ... } while (a);` is equivalent to `while (1) { ...; if (!a) break; }` – ikegami Feb 22 '20 at 23:16

2 Answers2

3

Apart from the issues pointed out in the comments there is some things you should improve:

  • char * string = (char *) malloc(WordSize * sizeof(char)); is the same as char * string = malloc(WordSize), but for a 20 word string we will need char * string = malloc(WordSize + 1)

  • This part of the code:

    for (i = 0; i < Wordsize; i++) // already corrected
    {
        {
        scanf("%c", &string[i]);
        }
    }
    string[i] = '\0';

This will obligate you to always have a 19 character string. The cycle will not end until you do (you replace the 20th character with the null-terminator).

You can replace the whole thing with:

fgets(string, WordSize + 1, stdin);

And for good measure, discard the extra characters when the input is too big to fit the string.

int c;
while((c = fgetc(stdin)) !='\n' && c =! EOF); //discard until newline, for completion check for EOF return

This will allow a 20 character max size string but also for smaller ones.

Working sample

halfer
  • 19,824
  • 17
  • 99
  • 186
anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • hehe thank you! This helps out a great deal, yeah I'm hashing it out in the comments to figure out the 19 character string part but this indeed is an answer! – JayTC Feb 22 '20 at 22:02
  • @JayTC just to clarify the 19 character string is because your cycle gets 20 characters but then you replace the 20th with `\0` null terminator, so you get 19 characters. – anastaciu Feb 22 '20 at 22:07
  • oooooh Yeah that makes sense, appreciate it. Couldn't I just have it automatically allocate the amount of memory by using the users input for the for the wordsize instead? – JayTC Feb 22 '20 at 22:14
  • @JayTC, I updated the code to include a mechanism to discard extra character if, let's say you input a 21 or more charater string. Regarding the memory allocation, you could do it but then you would need to ask the user for the size of the string beforehand, because you never now what the size will be, until the string is entered, you coulld also read the input char by char and reallocate memory as you do but that's not very efficient code. – anastaciu Feb 22 '20 at 22:21
  • @anastaciu: `fgets(string, WordSize + 1, stdin);` will read `WordSize` charcters and assign `string[WordSize]='\0';` which is out of `string` bounds that is from [0] to [WordSize-1]. Same `string[WordSize + 1] = '\0';` which is even more out of bounds. – tansy Feb 22 '20 at 22:36
  • @tansy, thanks for noticing, needed some tweaks, I think it's good now, – anastaciu Feb 22 '20 at 22:40
  • @anastaciu: `fgets(string, WordSize + 1, stdin);` is still wrong, as with [fgets()](https://linux.die.net/man/3/fgets) _a terminating null byte (aq\0aq) is stored after the last character in the buffer_ - which will put `'\0'` behind `string`. To accomodate it either use `fgets(string, WordSize, stdin);` or `char * string = (char *) malloc(WordSize+1, 1);` – tansy Feb 22 '20 at 22:47
  • @tansy, but I removed the null terminator adition since fgets puts a `\n` at the end of the string witch in this case is just as good. – anastaciu Feb 22 '20 at 22:51
  • And when do you think `for (int i = 0; i < strlen(string); i++)` in `int stringSearch()` will end without null termination of string `string`? – tansy Feb 22 '20 at 23:00
  • @tansy, as far as I can tell the size will be 20 at max https://onlinegdb.com/HydSYV148 – anastaciu Feb 22 '20 at 23:29
  • Also a weird thing that I did during my own testing, and this is mostly likely due to the fact that I can't code for my life, but I have to always input the value I wish to find twice. OUTSIDE OF THAT it works out great, just some clonkiness on my end – JayTC Feb 22 '20 at 23:32
  • `fgets(string, WordSize + 1, stdin);` will write out of string `string`bounds. It's typical buffer overflow. Let's go to chat, I'll show you. – tansy Feb 22 '20 at 23:41
  • @JayTC: [fgets(char *s, int size, FILE *stream);](https://linux.die.net/man/3/fgets) will not read more than size bytes, and will put '\0' al size'th character. That's your safety mechanism. – tansy Feb 23 '20 at 00:11
  • you will still need to do `while (getchar() != '\n') {}` to exhaust input buffer. ( [Why does my code keep printing twice?](https://stackoverflow.com/questions/59883621/why-does-my-code-keep-printing-twice-i-dont-understand-the-problem/59883679), [How do I read a string entered by the user in C?](https://stackoverflow.com/questions/4023895/how-do-i-read-a-string-entered-by-the-user-in-c#4023921) ) – tansy Feb 23 '20 at 00:19
  • @tansy `char *fgets(char *str, int n, FILE *stream)` where `n` "is the maximum number of characters to be read (including the final null-character). Usually, the length of the array passed as str is used". So you pass the size of the array and fgets is responsible to null terminate the last char. – anastaciu Feb 23 '20 at 00:31
  • @JayTC the code sample I linked is working well, no double inputs are needed. If there are details that you'd like to change work from there, normally these double inputs have to do with charater leftovers in the buffer. – anastaciu Feb 23 '20 at 00:54
  • 1
    @anastaciu Yeah probably hehe, its only for the lab and not the homework itself so I can just submit it and comment that its broken. I'll do some bug testing myself and see where the issue is. – JayTC Feb 23 '20 at 01:57
  • The correct idiom for cleaning the input stream and avoiding potential infinite loops is `int c; while ((c = getchar()) != '\n' && c != EOF) { continue; }`. The same holds if you want to use `fgetc()`. In principle you need to test for `EOF` because these functions may return that value. In practice, `EOF` may be returned when there is an input failure, or in some circumstances when input is redirected from a file. A user can also signal `EOF` from the keyboard, and there may be some circumstance in which this causes a problem. Failure to test for `EOF` leads to fragile code. – ad absurdum Feb 23 '20 at 02:06
  • @exnihilo indeed it can, I'll add it to my answer and sample code. – anastaciu Feb 23 '20 at 02:31
  • 1
    @anastaciu -- it's not much more work to use the right idiom here, and it's better not to propagate incomplete or poor solutions to learners; this is one case where they should get in the habit of coding it correctly every time ;) – ad absurdum Feb 23 '20 at 02:36
  • @exnihilo can't argue with that. – anastaciu Feb 23 '20 at 02:41
  • @anastaciu -- oops: you need to put parentheses around the assignment: `while((c = fgetc(stdin))!='\n' && c =! EOF)` – ad absurdum Feb 23 '20 at 02:43
  • @exnihilo, jesus, lol, it's not being easy to wrap this up. I think now we've got it. – anastaciu Feb 23 '20 at 02:47
1

You should add required headers, use fgets() rather than scanf() and set tester_index to -1 rather than 0 which means found at index 0.

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

#define Found 1
#define NotFound 0
#define WordSize 20

int stringSearch(char * string, char array, int * letter);

int main(void)
{
    char * string = (char *) malloc(WordSize * sizeof(char));
    //char tester = '\0';
    char tester[2] = {'\0', '\0'};
    int index_tester = -1; // 0 means found @ index 0

/* (...) */

    printf("What is the test string you wish to enter: ?\n");

    fgets(string, WordSize, stdin);
    if (string[WordSize-1]=='\n')
        string[WordSize-1]='\0';
    puts(string);

    printf("Tester for the inputed string: \n");
    while (getchar() != '\n') {}
    ///scanf("%c", &tester[0]);
    fgets(tester, 2, stdin);

    int ResultofSearch = stringSearch(string, tester[0], &index_tester);

    if (ResultofSearch == NotFound)
    {
        printf("That letter is not found in the string.\n");
    }
    else {
            printf("Character found at index %d.\n\n", index_tester);
        }

    return 0;
}

int stringSearch(char * string, char c, int * index)
{ ... }

It's definitely not perfect but works more less expected way.

tansy
  • 486
  • 3
  • 8
  • Thank ya! This is definitely another approach to it to that I like as well. As you probably have seen in the other comments, a lot of the notation I'm still not familiar with but I feel like fgets will be an important aspect of my code going forward. I just find a way to do it with the tools I know as well – JayTC Feb 22 '20 at 22:49
  • 1
    I actually had a question concerning what you did to "Tester" why'd you make an array with two nulls? Is that important? – JayTC Feb 22 '20 at 23:05
  • Actually, in this case it is not required (read edited comment in program source). I put it pro forma to not leave "uninitialized" string. [fgets()](https://linux.die.net/man/3/fgets) reads size-1 number of characters + includes the final null-character (string[size]). So here will read one (for example 'a') and will zero second making `tester`: ['a', '\0']. – tansy Feb 22 '20 at 23:26
  • @tansy so I tested your code and it doesn't seem to find the character. – anastaciu Feb 23 '20 at 00:37
  • It works. Did you put `int stringSearch(char * string, char c, int * index)` definition there? As you can see I put only unfilled {} brackets, you have to put definition from @JayTC code and will work. – tansy Feb 23 '20 at 01:39
  • @tansy it works, just needed to input a character twice, witch is not ideal but I can't say it won't work at all. https://onlinegdb.com/HkQLyPyNL – anastaciu Feb 23 '20 at 02:13
  • Ok, it does strange thing/s when string is shorter than `WordSize`. Will check it later, it's 3am. – tansy Feb 23 '20 at 02:31
  • @tansy I love this community! Its super cool how many different approaches everyone has to the problem, and I'm surprised by the level of notice here. Did what you were trying to test work out for you? – JayTC Feb 25 '20 at 02:47