Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon.

Pages: 1-4041-

Shitty program

Name: Anonymous 2010-05-24 23:11

Hi /prog/. I'm learning C, and wrote a program that takes a bunch of numbers and sorts them using a binary tree. I know it's not an efficient way to sort, I'm just learning binary trees. Could you look over it real quick and just give me some comments, like if there was something you would have done differently or something that's just stupid. Thanks a lot.

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

struct node_ {
        int value;
        struct node_ *left, *right;
};

typedef struct node_ node;

node *insert(node *parent, int newval)
{
        if (!parent) {
                node *n = malloc(sizeof(node));
                n->value = newval;
                return n;
        }
        else if (newval == parent->value)
                return parent;
        else if (newval < parent->value) {
                parent->left = insert(parent->left, newval);
                return parent;
        }
        else {
                parent->right = insert(parent->right, newval);
                return parent;
        }
}

void print(node *top)
{
        if (!top)
                return;

        print(top->left);
        printf("%d ", top->value);
        print(top->right);
}

int main(int argc, char *argv[])
{
        puts("enter values, 0 to stop");
       
        int next;
        scanf("%d", &next);
       
        node *root = 0;
       
        while (next) {
                root = insert(root, next);
                scanf("%d", &next);
        }

        print(root);
        puts("");

        return 0;
}

Name: Anonymous 2010-05-24 23:49

Your binary tree is dropping values. It's turning your collection into a set, which is probably incorrect.
Also, you should be using four spaces per indentation level, not eight.

Name: Anonymous 2010-05-25 0:13

Your binary tree is dropping values. It's turning your collection into a set, which is probably incorrect.
That was on purpose, not really sure why I did that.
Also, you should be using four spaces per indentation level, not eight.
I used tabs in vim, apparently copying and pasting from an xterm converts to spaces. I haven't set my tab size to 4, which I guess I should do though.

Thanks for the comments, so the way I did this wasn't completely stupid or anything like that, right?

Name: PHP > C 2010-05-25 0:22

Hey fuckwad, think about what "return" does before you skip off into an "else". You make me sick.

Name: Anonymous 2010-05-25 0:24

Looks pretty much like how I would have written it, except that I would have folded the struct definition and typedef into one thing:

typedef struct node {
    int value;
    struct node *left, *right;
} node;


It's hard to go far wrong with C.

Name: 5 2010-05-25 0:27

Oh, and:

        }
        else


should be

    } else

every time, of course.
>>4's point is a stylistic choice, because either way will almost certainly generate the same assembly, but K&R indentation must be taken seriously.

Name: Anonymous 2010-05-25 1:17

>>5
I would have folded the struct definition and typedef into one thing
It took me 10 years to learn why that's a bad thing to do. OP doesn't have it much better though.

Name: Anonymous 2010-05-25 1:19

>>7
And why is that?

Name: Anonymous 2010-05-25 1:35

node *insert(node *parent, int newval)
{ if(parent)
  { if(newval < parent->value) parent->left = insert(parent->left, newval);
    else if(newval > parent->value) parent->right = insert(parent->right, newval);
    return parent; }
  else return memcpy(malloc(sizeof(node)), &(node){ .value = newval; }, sizeof(node)); }

Name: Anonymous 2010-05-25 1:39

Why not this for your insert function?:


node *insert(node *parent, int newval)
{
    if (!parent)
    {
        node *n = malloc(sizeof(node));
        n->value = newval;
    }
    else if (newval < parent->value)
        parent->left = insert(parent->left, newval);
    else if (newval > parent->value)
        parent->right = insert(parent->right, newval);
    return parent;
}

Name: Anonymous 2010-05-25 2:16

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

struct node { int value; struct node *left, *right; } node

node *insert(node *parent, int newval)
{ if(!parent) parent = memcpy(malloc(sizeof(node)), &(node) { .value = newval; }, sizeof(node));
  else if(newval < parent->value) parent->left = insert(parent->left, newval);
  else if(newval > parent->value) parent->right = insert(parent->right, newval);
  return parent ; }

void print(node *top)
{ if(top)
  { print(top->left);
    printf("%d ", top->value);
    print(top->right); }}

int main(int argc, char *argv[argc])
{ int next;
  node *root = 0;
  puts("enter value, 0 to stop");
  for(scanf("%d", &next); next; scanf("%d", &next)) insert(root, next);
  print(root);
  putchar('\n');
  return 0; }


making insert tail-recursive and then doing tail call optimization on it is left as an exercise for the reader.

Name: Anonymous 2010-05-25 6:04

>>8
Something about destroying readability and making it hard to write linked lists or enforce scope.

Name: Anonymous 2010-05-25 7:38

>>2
Also, you should be using four spaces per indentation level, not eight.
Why is that?

Name: Anonymous 2010-05-25 7:49

>>13
Because that's the way Xarn does it.

Name: Anonymous 2010-05-25 10:26

>>5
Okay, I wasn't too sure about exactly how typedefs and structs work together.

>>10
Right, that looks a lot better. Thanks.

>>7,12
So how would you write that?

Name: Anonymous 2010-05-25 12:02

>>15
struct foo;
/* ^ if intended for use with an API, hiding the internals */

struct foo {
    int bar;
};
/* ^ if you want to be able to access members from outside the module */

typedef struct foo Bar; /* not always necessary */

Name: Anonymous 2010-05-25 12:41

>>5
I'm sure that wouldn't work, because it's referencing an incomplete type from within itself.

Name: Anonymous 2010-05-25 12:45

>>17
It wouldn't work if the inside declaration was just node, because of the way typedefs work, but it works just fine with struct node.

Name: Anonymous 2010-05-25 15:09

>>17
How did you get this bad at C?

Name: Anonymous 2010-05-25 15:18

>>19
He doesn't have a compiler, obviously. Otherwise he would have tested his hypothesis before posting.

Name: Anonymous 2010-05-25 15:25

>>19
I roll my own beets, of course.

Name: Anonymous 2010-05-25 15:39

>>15
>>10's insert returns NULL when parent is NULL. you should do what >>11 does instead.

Name: Anonymous 2010-05-25 19:33

Alright, thanks guys. So besides cleaning up the code a little (>>4,5,10) there's nothing really wrong with it?

Name: Anonymous 2010-05-25 19:39

>>23
>>4,5,10
Did you mean >>3,4,10?

Name: Anonymous 2010-05-25 19:44

>>20
>>his hypothesis
>>Could you look over it real quick and just give me some comments

Name: Anonymous 2010-05-25 19:58

computer science is as much about mathematics, as astronomy is about telescopes

Name: Anonymous 2010-05-25 20:12

>>23
when parent is NULL, your insert returns a pointer to a node with left and right containing unspecified values (they can be anything). you probably want them to be set to NULL instead, like >>11 does.

Name: Anonymous 2010-05-25 20:35

>>23
Logic errors aside: no reason to typedef the struct, use sizeof expr instead of sizeof (type) when possible, check malloc (at least print something + exit), scanf is usually bad for untrusted input

Name: Anonymous 2010-05-25 20:51

>>28
the typedef makes the code shorter, and is good if the tree is intended to be an opaque type only used through an api, which is the right way to do something like this. sizeof(expr) just gets turned into sizeof(typeof(expr)) anyway. scanf is used safely here (bad input can't cause anything worse than bad output). good point about checking malloc, tho.

Name: Anonymous 2010-05-25 21:34

>>29
An opaque API can be done with struct node; in the header. I find typedefs like this to be less clear than just using the normal type name. I also find using expressions for sizeof (when possible) nicer than embedding the type name. When you use an expression, allocations usually look like p = malloc(n * sizeof *p);. No type name needed. There's no right answer for either of these issues though, both are a matter of style. Just thought I'd give some food for thought.

scanf is OK for example code, but it actually causes undefined behaviour if the input item for the %d specifier is out of int range (eg sscanf("9999999999999999", "%d", &n);). A combination of fgets and the strto* functions works well.

Name: Anonymous 2010-05-25 22:05

A combination of fgets and the strto* functions works well.
or you could use mpz_inp_str and not worry about range at all...

Name: Anonymous 2010-05-25 22:16

fgets
fagets
lol

Name: Anonymous 2010-05-25 22:24

>>30
When you use an expression, allocations usually look like p = malloc(n * sizeof *p);.
Are you trolling?

Okay, okay, assuming you're not: I really hate that. When do you ever not know the type you're allocating? OTOH, if anyone else was reading your code they might not know (perhaps you're the type to write functions as long as your arm), and the call to malloc wouldn't help clear that up. There's no point in removing information like that.

I take the opposite approach: p = malloc(sizeof(Tp[n]));. I greatly prefer this because the syntax used parallels the stack allocation syntax, and the semantics are obvious when adding constants, factors, etc. void.h needs #define new(x) malloc(sizeof(x)).

Name: Anonymous 2010-05-25 22:47

>>25
>>17 isn't OP.

>>27
Alright, I wasn't sure about that. So when you allocate memory it's not guaranteed to be zeroed, right?

Name: Anonymous 2010-05-25 22:50

I'm not going to read the rest of this thread before I post, but

>>2,3

gtfo my prague. Shitchan converts tabs inside of [code] and (I think) [m] tags into 8 spaces. You should know this.

Name: Anonymous 2010-05-25 22:54

>>34
In principle it isn't. In practice you'll probably be fine in this particular case, but >>27 is right and it's a really to set things explicitly to NULL.

>>35
Still doesn't change the fact he shouldn't be using literal tabs at all, but four spaces.

Name: Anonymous 2010-05-25 23:01

Here's version 2, did I fix everything mentioned? I did change the behavior to keep duplicate values. Also, I added some basic malloc checking, but I'm not worried about scanf for this.

Also thanks, you have all been really helpful.

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

typedef struct node {
    int value;
    struct node *left, *right;
} node;

node *insert(node *parent, int newval)
{
    if (!parent) {
        node *n = malloc(sizeof(node));

        if (!n) {
            puts("malloc failed");
            return 0;  
        }      

        n->value = newval;
        n->left = n->right = 0;
        return n;
    }  

    if (newval <= parent->value)
        parent->left = insert(parent->left, newval);
    else
        parent->right = insert(parent->right, newval);

    return parent;
}

void print(node *top)
{
    if (!top)
        return;

    print(top->left);
    printf("%d ", top->value);
    print(top->right);
}

int main(int argc, char *argv[])
{
    puts("enter values, 0 to stop");

    int next;
    scanf("%d", &next);

    node *root = 0;

    while (next) {
        root = insert(root, next);
        scanf("%d", &next);
    }  

    print(root);
    puts("");

    return 0;
}

Name: Anonymous 2010-05-25 23:04

Alright, I wasn't sure about that. So when you allocate memory it's not guaranteed to be zeroed, right?
right. if you do (node){ .value = newval }, the rest of the struct members are guaranteed to be zeroed.

but i'm pretty sure this should work without a memcpy:
node *n = malloc(sizeof(n));
*n = (node){ .value = newval };

Name: Anonymous 2010-05-25 23:14

>>37
one return is a lot better than two:
node *insert(node *parent, int newval)
{
    if(!parent)
    {
        parent = malloc(sizeof(node));
        if(!parent)
        {
             fputs("malloc failed", stderr); /* errors go to stderr */
             exit(1); /* no point continuing if we're out of memory */
        }
        *parent = (node){ .value = newval; };
    }
    else if(newval <= parent->value)
        parent->left = insert(parent->left, newval);
    else
        parent->right = insert(parent->right, newval);
    return parent;
}


and there's print, which it almost looks like you're doing wrong on purpose:
void print(node *top)
{
    if(top)
    {
         print(top->left);
         printf("%d ", top->value);
         print(top->right);
    }
}

Name: Anonymous 2010-05-25 23:52

>>33
Not trolling. I always know the type, but I don't see a point in adding information like that.

Name: Anonymous 2010-05-26 0:25

>>40
I already made my counter argument to that. There's also the niggling issue with void and converted pointers. Niggling because it's rarely practical, but the fact is your form does fail to cover valid possibilities, not all of which are bad form.

Besides, the information is there, only indirectly. The problem is that by using the typed variable instead of the type itself hides that information from the person reading the code. It should be obvious this is bad: the cost of being explicit here is virtually nothing. Will no one else ever read your code? And to take the extreme: it's not like allocating to the stack using a typed variable in place of the type itself is a good idea, but you can totally do it.

Name: Anonymous 2010-05-26 3:08

>>22
I just copied the functionality he had but wrote it with cleaner if/else statements and with the returns.

Name: Anonymous 2010-05-26 14:13

>>42
No, they are different when parent is null.

Name: Anonymous 2010-05-27 14:20

>>43
Well I'll be damned. I didn't even realize that

Name: Anonymous 2010-11-27 7:44

Don't change these.
Name: Email:
Entire Thread Thread List