Necroteuch
Necroteuch

Reputation: 243

C - Return value to main

Haven't found anything answering my question, so here goes:

I want to return an integer from a function to my main for further use, specifically it's a an add() function that creates a list from entries in a text file. The integer I want to return is called error, is declared as 0 at the beginning of the function and changes to 1 when a condition is met (in my case, if any character other than a-z, A-Z, - and ' ' is found). If this condition is met, I want to return error (as 1) to the main function, otherwise i return it as 0. In my main I then have an if statement that checks if error is 1 to then print an error message and return 1. Is that possible? I've tried a few things but it doesn't work, if I initialize the error variable as 0 in my main, shouldn't the add function change it to one in case of my error condition? I don't want to use global variables and only want to implement the add function into main as a last resort.

Sorry if this is complete nonsense, I am very much a beginner.

int add(char line[])
{
    struct node *newnode = (struct node *) malloc(sizeof(struct node));
    struct node *walker = newnode;

    strcpy(newnode->name, line);                

    int check = 0;
    int error = 0;

    while(line[check] != '\n')
    {
        if(line[check] == '!')
        {
            error = 1;
            return error;       
        }
        check++;
    }
    .    //Here is just the creation of the list elements
    .    //If the functions reaches end, it returns error with 0 as value
    .
    }

And the main:

int main()
{
    FILE *fp;
    char line[33];
    int error = 0;

    fp = fopen("test.txt", "r");

    if(!fp)
    {
        printf("Error.");
        return 0;
    }

    while(fgets(line, 33, fp) != NULL)
    {
        add(line);
    }
    fclose(fp);

    if(error == 1)
    {
        printf("error");
        return 1;
    }

    // ...
}

EDIT: Ok, I've made it work, and since a few people basically had the right solution for me, I wanted to say thanks here! Don't know if I can choose multiple answers as correct.

Upvotes: 2

Views: 3738

Answers (6)

Runium
Runium

Reputation: 1085

A side answer to comments. Became a bit long for a new comment.

(This is regarding while(line[check]) != '\n') {)

fgets is OK. But fgets quit reading once it reaches a newline or have read 32 bytes.

It does not append a newline to the end of data.

That \n at the end is only there if it was inside the bounds of current location in file. What it do every time is append a null, as in 0x00 as last byte - within length of read data. AKA C-string termination.

That is:

 buf[4];
 line 'a\n'    => buf = {'a', '\n', 0x00}
 line 'ab\n'   => buf = {'a', 'b' , '\n', 0x00}
 line 'abc\n'  => buf = {'a', 'b' , 'c' , 0x00}

Put another way. That newline in buf is not something fgets append, - but is part of the read data that also triggers a stop in read. Thus you have:

fgets stop reading if:

     IF lenght - 1 bytes read
  OR IF read \n
  OR IF end of file

Also; if a line is more then 32 bytes next read will continue where it left off, not on next line.

That gives:

data:

abc
defghijklmno
pq
rstuv

Sample code:

#include <stdio.h>

int main(int argc, char *argv[])
{

    FILE *fp;
    char *fn = "sample.txt";
    char buf[4];
    int i = 0;

    if (argc > 1)
        fn = argv[1];

    if ((fp = fopen(fn, "r")) == NULL) {
        fprintf(stderr, "Unable to open '%s` for reading.\n", fn);
        return 1;
    }

    while(fgets(buf, 4, fp) != NULL) {
        fprintf(stdout, "%2d: @%s@\n", i++, buf);
    }

    fclose(fp);

    return 0;
}

Result:

 0: @abc@
 1: @
@
 2: @def@
 3: @ghi@
 4: @jkl@
 5: @mno@
 6: @
@
 7: @pq
@
 8: @rst@
 9: @uv
@

What happens in your code, the while loop, is that if there are no \n or ! in line it keeps advancing beyond the memory area where line resides. Either it result in a segfault or it finds one of the two and aborts - giving you false positives if say:

  char line[4]; read 'abcd'

  MEMORY ADDRESS   REF        VALUE
  0xf00            line[0]    a
  0xf01            line[1]    b
  0xf02            line[2]    c
  0xf03            line[3]    0x00
  0xf04            <unknown>  z
  0xf05            <unknown>  x
  0xf06            <unknown>  0x08
  0xf07            <unknown>  A
  0xf08            <unknown>  f
  0xf09            <unknown>  0xfd
  0xf0a            <unknown>  !
  0xf0b            <unknown>  g

By this your function would return error because it kept reading until address 0xf0a where it found ! – but this is not part of line.


By this also rename line to buf or the like as it is confusing in the code (perhaps).

And, only as a suggestion, consider returning 0 on success and != 0 on error as that is the convention by many functions in same format.

A function named is_ok(var), would be logical to return 1 for true and 0 for false, but a function add(var) is more logical to return != 0 on error – as in error code.


EDIT to comment:

Yes. A normal way could be something like:

int i = 0;

while (buf[i] && buf[i] != '\n') {
    if (!isalpha(buf[i++]))
        return 1;
}

Or the more clearer, but IMHO not needed as if one are normalized to C it one intuitively read it as this:

while (buf[i] != 0x00 && buf[i] != '\n') {
    ...

isalpha() is part of ctype.h, but if you know it is always going to be ASCII it is easy enough to write yourself. Also read this perhaps.

Upvotes: 1

Abhineet
Abhineet

Reputation: 5389

The variables you have in add() and main() are local variables. This means that the values they contain cannot be used outside the respective functions. So when add() returns a value, you need to have a variable in main() to collect that. So,

while(fgets(line, 33, fp) != NULL)
{
      /* Here when the add returns 1 or 0, the value is collected 
       * by the "error" variable of main function */
      error = add(line);
      if( error == 1 ) break ; /* Its Simple :-) */
      /* Once add() returns, you will break from the while loop
         will continue with the main() instructions ahead */
}

Upvotes: 1

Milos
Milos

Reputation: 548

In the function add(), change the line

if(line[check] == '!')

to

if(!ok(line[check]))

and write the function ok so that it checks if the provided argument isn't a letter of English alphabet or a space. Now you just just check if your line contains !. Maybe that's a mistake.

You can write ok like this:

int ok(char ch){
    return (ch>='A' && ch<='Z') || (ch>='a' && ch<='z') || ch==' ';
}

In the main, you should have something like this:

while(fgets(line, 33, fp) != NULL)
{
    error=add(line);
    if(error==1)  ... handle error ...

Upvotes: 1

Suvarna Pattayil
Suvarna Pattayil

Reputation: 5239

int main()
{
FILE *fp;
char line[33];
int error = 0;

This error variable in main is different from the one defined in add

int check = 0;
int error = 0;

When you call error in main, that variable local to main is called which is still unaltered. The change to error in add is local to that function. Changes in that variable is not reflected in error in main.

You have to something like int error = add(line); if(error) in main

Upvotes: 0

isioutis
isioutis

Reputation: 324

I think you are in the right direction just inside the while you don't use the value that add() function returns.

You have to assign that function to the error like error = add(line); and then you can check if that is 1 so you return.
Hope this helps of course the checking has to be don inside the while as every time the value changes and thus you might loose the value 1. With error = add(line); inside the while , variable error takes new value in every loop.

Upvotes: 0

Edward Clements
Edward Clements

Reputation: 5132

you seem to have two variables called error, one in add() and another in main(), they are different variables! you need at least to change

add(line);

to

error = add(line);
if (error != 0)
    break;

Upvotes: 2

Related Questions