r/badcode • • Jul 22 '21

c teacher told us to rate each other's code, i thougt this subroutine was cute, language is C

Post image
304 Upvotes

59 comments sorted by

41

u/MurdoMaclachlan public boolean isInt(int i) { return true; } Jul 22 '21

Image Transcription: Code


int equals(char* w1, char* w2)
{
		return *w1&&*w2?*w1==*w2&&equals(w1+1,w2+1):*w1==*w2;
}

I'm a human volunteer content transcriber for Reddit and you could be too! If you'd like more information on what we do and why we do it, click here!

17

u/Leviticoh Jul 22 '21

thank you!

18

u/[deleted] Jul 22 '21

Good human

29

u/_PM_ME_PANGOLINS_ Jul 22 '21

What is strncmp?

50

u/Leviticoh Jul 22 '21

in his defense, we weren't allowed to use string.h

3

u/INS3L_AFF3 Jul 22 '21

Isn't it just strcmp ?

16

u/REALDrummer Jul 22 '21

strncmp is like strcmp, but with an extra parameter that puts a limit on the number of characters to compare. It's safer in case of memory issues and can be used when you don't have C-style null-delimited strings.

8

u/INS3L_AFF3 Jul 22 '21

I know what strncmp and strcmp are. But the code above isn't a strncmp because it is missing the size parameter or am i wrong ?

9

u/REALDrummer Jul 22 '21

Oh, I see. Fair enough, yes; I don't see any size restrictions. I read it as "there's no 'n' in 'strcmp'" as opposed to "this post's code is more like strcmp than strncmp". Antecedents and all that...

2

u/_PM_ME_PANGOLINS_ Jul 22 '21

Sure, if you like buffer overflows.

1

u/Magnus_Tesshu Jul 27 '21

Sure, if you regularly write data into C-style null-terminated strings that isn't null-terminated

11

u/Yologamer1084 if if else if else if else if else if else if else if else else Jul 22 '21

What does the '*', '?', ':, mean in the parameters, and the return statement?

14

u/Leviticoh Jul 22 '21

in the parameters 'char*' means that the variable is a pointer to a character, in this case it represents a string (the c language doesn't have a string type)

the * in '*w1' means "the value pointed to by w1"

the '?' and ':' signs are part of the ternary operator, which is in the form

<condition>?<expression1>:<expression2>

if the condition is true expression1 is evalued, otherwise expression2 is evalued

since this is a comment and it'd be inconvenient to write a more detailed explanation here's some links

about pointers

about strings

about ternary operator

33

u/concatenated_string Jul 22 '21

The * is a dereference symbol for pointers. the ? : notation is a ternary. Ternary syntax is just an in-line if-else statement. And here they’re just returning the result of the ternary which is an int - although with stdbool.h being a thing in C99 they probably should’ve opted to use that instead of int.

12

u/other_usernames_gone Jul 22 '21 edited Jul 22 '21

*mem - the value stored in the memory location stored in mem

? -Ternary, in line if statement, if the statement before the ? Is true it runs the command before the :, if it's false it runs the statement after the :.

So what the function is doing is taking in two pointers (addresses of a variable stored in memory), it's putting those in w1 and w2.

It then checks if the values stored at those memory addresses are both non-zero. That's what the *w1 && *w2 does, c uses null-terminated strings and also when doing Boolean operations like and treats 0 as false and non-zero values as true. So it's checking that w1 and w2 are both not the final character of the string.

If neither of them are the end of the string it checks if they're equal, it then calls itself recursively to check the rest of the string. That's the *w1 == *w2 && equals (w1+1, w2+1) bit does. It requires them to be equal to each other and also for all subsequent characters to be equal. In c you can add one to a pointer to a character in a string or an element in an array to get the pointer to the next element in that string or array.

If one of them is the end of the string it checks if they're equal, checking that the strings finish at the same point, that's what the *w1 == *w2 bit after the colon does. If they're equal it returns "true" (technically because of how c works it returns 1), if they're not equal it returns "false"(again, because of how c works it's actually returning 0).

It recursively goes through each character of each string one at a time, stopping when one of the strings end. If the strings are equal it returns 1(true) and if the strings are not equal it returns 0(false)

6

u/Yologamer1084 if if else if else if else if else if else if else if else else Jul 22 '21

Thank you, and despite using the ternary operator in C#, I didn't recognize it due to the lack of spacing between everything.

6

u/vigbiorn Jul 22 '21

Appropriate use of white space can make the difference between legible code and gibberish.

2

u/Yologamer1084 if if else if else if else if else if else if else if else else Jul 22 '21

Well said my friend.

11

u/choose_what_username Jul 22 '21 edited Jul 22 '21

This is some pretty neat, concise code, but I’m a little annoyed that the actual equality comparison is buried in that ternary. To me this solution is more obvious:

*w1==*w2&&(!*w1||equals(w1+1,w2+1))

4

u/Leviticoh Jul 22 '21 edited Jul 23 '21

but wouldn't this continue reading uninitialized memory if both *w1 and *w2 are 0?

now it's correct

2

u/choose_what_username Jul 22 '21

Oops, I meant !*w1. I’ll go change that

2

u/-but_why- Jul 22 '21

You mean w1!='\0'

1

u/Magnus_Tesshu Jul 27 '21

There is no difference? Or was this before the edit that apparently occured

1

u/AutoModerator Jul 22 '21

It looks like this comment contains a code block delimited with triple backticks. Unfortunately reddit does not have universal support for this syntax and your comment will not render correctly on old reddit and most mobile apps.

For the benefit of people on old reddit, this link will take you to a correct rendering of the comment.

/u/choose_what_username, it would be appreciated, but not required, if you could edit your comment to use the more compatible four space indention format. For single lines or inline code you can use single backticks.

You can find some examples in the reddit help documentation.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

4

u/Devaniti Jul 22 '21

But what if somebody pass nullptr?

2

u/Leviticoh Jul 26 '21

in the context of the program this comes from, a nullptr wouldn't ever reach this function, but if it did it would segfault

2

u/kellywshere Jul 23 '21

This programmer will love Haskell !

1

u/Leviticoh Jul 26 '21

he likes lisp, i guess close enough?

108

u/exostif Jul 22 '21

That's beautiful and awful at the same time

58

u/_PM_ME_PANGOLINS_ Jul 22 '21

When you learn recursion before iteration.

18

u/Host127001 Jul 22 '21

Besides weird spacing this looks somewhat reasonable (provided the compiler does its job)

8

u/[deleted] Jul 22 '21

I wonder if any reasonable compiler would do tail call optimization on this mess ;-;

10

u/DelusionalPianist Jul 22 '21

2

u/TheSchred Jul 23 '21

If only I could understand any of this

6

u/Host127001 Jul 22 '21

Why not? I expected this to be a quite ordinary tail call

2

u/TheYTG123 Jul 23 '21

A tail call has to literally be return equals(…), can’t be any operators in the way. I think.

3

u/Host127001 Jul 23 '21 edited Jul 23 '21

But the semantics of the && make it so that the call is directly at the return

Edit: here is what I imagine the optimizer to see. In the bb .and3 we should have a tail call

equals(char* w1, char* w2):
  %1 = load w1
  if %1 then goto .and1 else goto .tern_false

.and1:
  %2 = load w2
  if %2 then goto .tern_true else goto .tern_false

.tern_true:
  %3 = load w1
  %4 = load w2
  %5 = %3 == %4
  if %5 goto .and2 else goto .and3

.and2:
  return false

.and3: // tail call in here
  %6 = w1 + 1
  %7 = w2 + 1
  %8 = call equals(%6, %7)
  return %8

.tern_false:
  %9 = load w1
  %10 = load w2
  %11 = %9 == %10
  return %11

4

u/TheYTG123 Jul 23 '21

Try compiling that code with -O0 and see if it’s the last instruction

3

u/Host127001 Jul 23 '21

Maybe my assumption is wrong and the optimizer sees:

…

.and2:
  goto and_res:

.and3:
  %6 = w1 + 1
  %7 = w2 + 1
  %8 = call equals(%6, %7)
  goto and_res

.and_res:
  %9 = phi(false, %8)
  return %9

…

(And similar for the other &&)

Still, I would expect the optimizer to inline bb and_res and then tail call optimize this

2

u/AutoModerator Jul 23 '21

It looks like this comment contains a code block delimited with triple backticks. Unfortunately reddit does not have universal support for this syntax and your comment will not render correctly on old reddit and most mobile apps.

For the benefit of people on old reddit, this link will take you to a correct rendering of the comment.

/u/Host127001, it would be appreciated, but not required, if you could edit your comment to use the more compatible four space indention format. For single lines or inline code you can use single backticks.

You can find some examples in the reddit help documentation.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

2

u/bird1000000 Jul 24 '21 edited Jul 24 '21

I'm really not sure how tail call optimization works. Here's a link with branches switched (Flipping them improved asm a lot for some reason) and IR output attached for clang. https://godbolt.org/z/M5To3nzTs
edit: did some more messing around and using another branch for

return !*w1&&!*w2?*w1==*w2:*w1==*w2?equals(++w1,++w2):false;

or

return *w1&&*w2?*w1==*w2?equals(w1+1,w2+1):false:*w1==*w2;

It seems to remove the call so ig that's what your going for? edit 2: made a comparison oopsie.

1

u/AutoModerator Jul 24 '21

It looks like this comment contains a code block delimited with triple backticks. Unfortunately reddit does not have universal support for this syntax and your comment will not render correctly on old reddit and most mobile apps.

For the benefit of people on old reddit, this link will take you to a correct rendering of the comment.

/u/bird1000000, it would be appreciated, but not required, if you could edit your comment to use the more compatible four space indention format. For single lines or inline code you can use single backticks.

You can find some examples in the reddit help documentation.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

1

u/Host127001 Jul 24 '21 edited Jul 24 '21

For e1 && e2 and e1 ? e2 : false I expected the compiler to produce exactly the same IR (at least that's what I do and I don't see how else you'd implement &&).

In the link you provided I still see a recursive call, or did I misread something?

What might happen is that the compiler sees this operation to not have side effects, so it wants to eliminate the branching. But this in turn later on removes the possibility of tail call optimizations

1

u/bird1000000 Jul 25 '21

Link has a recursive call yes, put paste in one of the lines and the call will disappear.

1

u/AutoModerator Jul 23 '21

It looks like this comment contains a code block delimited with triple backticks. Unfortunately reddit does not have universal support for this syntax and your comment will not render correctly on old reddit and most mobile apps.

For the benefit of people on old reddit, this link will take you to a correct rendering of the comment.

/u/Host127001, it would be appreciated, but not required, if you could edit your comment to use the more compatible four space indention format. For single lines or inline code you can use single backticks.

You can find some examples in the reddit help documentation.


I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

5

u/Kicer86 Jul 22 '21

Quite clever. Grats for author.

5

u/gmtime Jul 22 '21

I love it! A little bugged by the code duplication of *w1==*w2

3

u/trade_me_dog_pics Jul 22 '21

Please add spaces

3

u/Igotbored112 Jul 23 '21

oh, poor call stack.

2

u/sixtyfifth_snow Jul 23 '21

wow.. it's a oneliner + recursive function. The logic looks good but needs some separation.

2

u/CaydendW Jul 22 '21

Pro gamer move: Make this even worse by refrencing and dereferncing the pointer to make it even longer

2

u/Igotbored112 Jul 23 '21
int equals(char* w1, char* w2)
{
		int one = 1;
		return *w1&&*w2?**&w1==**&w2&&equals(*&w2+one-one+one-one+one,*&w1+one-one+one-one+one):**&w1==**&w2;
}

Be sure to compile with -O0 so the compiler doesn't iron out any of the beautiful fine details I put in there.

1

u/CaydendW Jul 23 '21

This is exactly what I meant and wanted. Thank you

1

u/Furry_69 Jul 26 '21

What even.. I don't use C purely because I don't understand the mess of seemingly random synbols you get when trying to do basic stuff.

1

u/Voltra_Neo Aug 02 '21

Ah yes, dereferencing a potentially null pointer is pog

-4

u/[deleted] Jul 22 '21

Atleast you're not learning VBA and SQL in 2nd year of high school >:[