222

I recently had a test in my class. One of the problems was the following:

Given a number n, write a function in C/C++ that returns the sum of the digits of the number squared. (The following is important). The range of n is [ -(10^7), 10^7 ]. Example: If n = 123, your function should return 14 (1^2 + 2^2 + 3^2 = 14).

This is the function that I wrote:

int sum_of_digits_squared(int n) 
{
    int s = 0, c;

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

Looked right to me. So now the test came back and I found that the teacher didn't give me all the points for a reason that I do not understand. According to him, for my function to be complete, I should've have added the following detail:

int sum_of_digits_squared(int n) 
 {
    int s = 0, c;

    if (n == 0) {      //
        return 0;      //
    }                  //
                       // THIS APPARENTLY SHOULD'VE 
    if (n < 0) {       // BEEN IN THE FUNCTION FOR IT
        n = n * (-1);  // TO BE CORRECT
    }                  //

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

The argument for this is that the number n is in the range [-(10^7), 10^7], so it can be a negative number. But I don't see where my own version of the function fails. If I understand correctly, the meaning of while(n) is while(n != 0), not while (n > 0), so in my version of the function the number n wouldn't fail to enter the loop. It would work just the same.

Then, I tried both versions of the function on my computer at home and I got exactly the same answers for all the examples that I tried. So, sum_of_digits_squared(-123) is equal to sum_of_digits_squared(123) (which again, is equal to 14) (even without the detail that I apparently should've added). Indeed, if I try to print on the screen the digits of the number (from least to greatest in importance), in the 123 case I get 3 2 1 and in the -123 case I get -3 -2 -1 (which is actually kind of interesting). But in this problem it wouldn't matter since we square the digits.

So, who's wrong?

EDIT: My bad, I forgot to specify and didn't know it was important. The version of C used in our class and tests has to be C99 or newer. So I guess (by reading the comments) that my version would get the correct answer in any way.

Lundin
  • 195,001
  • 40
  • 254
  • 396
user010517720
  • 2,127
  • 2
  • 7
  • 15
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/200366/discussion-on-question-by-bogdan-vlad-my-professor-says-my-digit-summing-code-is). – Brad Larson Oct 03 '19 at 22:26
  • 125
    `n = n * (-1)` is a ridiculous way to write `n = -n`; Only an academic would even think of it. Let alone add the redundant parentheses. – user207421 Oct 04 '19 at 07:55
  • 34
    Write a series of unit tests to check whether a given implementation fits the spec. If there's a (functional) problem with a piece of code, it should be possible to write a test that demonstrates the incorrect result given a particular input. – Carl Oct 04 '19 at 10:31
  • 4
    How you go about correcting the teacher is a question on its own. – Salman A Oct 04 '19 at 12:08
  • 95
    I find it interesting that "the sum of the digits of the number squared" can be interpreted in three (3) completely different ways. (If the number is 123, the possible interpretations yield 18, 14, and 36.) – Andreas Rejbrand Oct 04 '19 at 14:03
  • 2
    @carl with such a small range, he could write an exhaustive test. – Vaelus Oct 04 '19 at 14:12
  • 3
    @AndreasRejbrand, ...how do you get 18? – ilkkachu Oct 04 '19 at 17:25
  • 23
    @ilkkachu: "the sum of the digits of the number squared". Well, "the number squared" is clearly 123^2 = 15129, so "the sum of the digits of the number squared" is "the sum of the digits of 15129", which obviously is 1+5+1+2+9=18. – Andreas Rejbrand Oct 04 '19 at 17:27
  • 2
    @AndreasRejbrand, ah, yes. Of course. Thanks. – ilkkachu Oct 04 '19 at 17:28
  • 5
    @AndreasRejbrand Those other interpretations are squelched by the inclusion of a detailed example in the specification. – Kaz Oct 04 '19 at 21:20
  • 16
    `n = n * (-1)`? Wut??? What your prof is looking for is this: `n = -n'. The C language has a unary minus operator. – Kaz Oct 04 '19 at 21:22
  • 4
    @Kaz: I agree, of course. Still, it is possible to write "the sum of the squares of the digits of the number", which only admits the intended interpretation. – Andreas Rejbrand Oct 04 '19 at 21:42
  • 3
    Three things shout that the teacher wasn't thinking much about it here: the useless `if` with the exactly inverted condition of the loop, the `n = n * (-1)`, and the domain of [-10⁷, 10⁷] (maximum value over the full domain of `int32_t` is 730 (1 999 999 999), so there is no risk of overflow anywhere). Don't take the lecture too seriously. – Jan Hudec Oct 05 '19 at 22:04
  • 9
    `n = abs(n)` is the simplest way to write `if (n < 0) { n = -n; }` – smci Oct 06 '19 at 04:12
  • 3
    If the number input was all valid integers, then ` n = n * (-1);` has a major bug with overflow if you try to convert the lowest possible int – Ferrybig Oct 06 '19 at 10:01
  • @Ferrybig, so does `n = -n`. I think [one of the answers mentions that case](https://stackoverflow.com/a/58225423/6372809). – ilkkachu Oct 06 '19 at 13:00
  • 8
    Sorry, I'm with the teacher on treatment of negative numbers. Back before computers, -3 mod 10 was 7. I would call this a matter of genuine dispute. **You are relying on an idiom of that particular C implementation to handle this**, and while you get a gold star for understanding your compiler version's gory internals, **this should not be left to chance**. You should force `n=abs(n)` immediately to settle the question and assure the case is handled as you intend. – Harper - Reinstate Monica Oct 06 '19 at 20:08
  • 5
    Note that the OP has explicitly specified C99 or newer, @Harper. This means that modulo is explicitly required (by the C99 standard) to round towards zero, and isn't left to implementations to decide. – Justin Time - Reinstate Monica Oct 06 '19 at 20:52
  • 7
    @JustinTime yeah, that is *precisely* "understanding your compiler's gory internals"... Which I am advising against. It may be well-published gory internals, but will it also work on PHP, Java, Javascript, Perl, Lua, COBOL, SAP, Haskell, random assembler, etc.? **My argument is don't train yourself to rely on idioms, because that makes you create bugs in other languages. Don't just be a good C coder, be a good coder**. [Like Heinlein said](https://www.elise.com/quotes/heinlein_-_specialization_is_for_insects)... – Harper - Reinstate Monica Oct 06 '19 at 21:13
  • 2
    @JustinTime and just to be clear, i do get why C does that. I would cheerfully see things the other way if we were doing code golf, or if performance/space was absolutely critical, because in that case, skill *is* making the most out of such subtleties. – Harper - Reinstate Monica Oct 06 '19 at 21:28
  • 2
    **`n = abs(n)`** already. End of story. – smci Oct 07 '19 at 02:33
  • 2
    Your *code* doesn't need to explicitly handle zero, but if you have to deal with stupid teachers, you might as well put in a defensive comment line before the `while` `// This will fall-through for n == 0 case`. – smci Oct 07 '19 at 02:46
  • 1
    @smci: Or a comment at the beginning of the code: `// Notice that this implementation works as intended also for zero and negative values of n.` Actually, that might be considered good practice in this case, even if it weren't for "stupid teachers". – Andreas Rejbrand Oct 07 '19 at 08:21
  • 7
    That's a valid opinion, @Harper, although close to the opposite of my opinion. Personally, if writing code in a programming language, I aim to write for _that language_, and use that language's features. I also wouldn't call "knowing how the language you're using works" the same as "understanding your compiler's gory internals", nor would I consider language features to be idioms. – Justin Time - Reinstate Monica Oct 07 '19 at 16:24
  • 2
    @JustinTime Fair enough. I wouldn't say that about language features generally, just this particular one deviates from traditional math, leaving an ambiguity. I dislike ambiguities. – Harper - Reinstate Monica Oct 07 '19 at 17:22
  • 2
    `-3` is not a digit. – axiac Oct 09 '19 at 16:10
  • Simply challenge him to speed test. Do n++ loop to (+/-)1 milion and measure time. I bet on you :) – Laky Oct 16 '19 at 08:16
  • 4
    @user010517720 What did your teacher say? – klutt Oct 16 '19 at 10:27
  • 2
    The teacher may just be seeking *evidence* that you have *explicitly* reasoned about the cases of zero, positive, and negative `n`. The code as submitted *implicitly* relied on the reasoning, but did not *explicitly* state it. That could have been done with a few comments in the code like `/* Assuming C99 and later, this works for case n = 0 because .... and for negative n works because ..... */`. There is a key difference between describing your reasoning and *assuming* the reader will implicitly understand your reasoning. – Peter Oct 20 '19 at 02:52
  • @klutt Unfortunately, I did not bring it up. He is not the type of teacher you want to correct. But I knew I wouldn't do it, I posted this question mainly for myself, to see if I should've got those points. – user010517720 Oct 25 '19 at 23:27
  • 1
    @user010517720 Hehe, I understand. But noting prevents you from sending him a link when you have graduated. ;) – klutt Oct 26 '19 at 01:53

9 Answers9

253

Summarizing a discussion that's been percolating in the comments:

  • There is no good reason to test in advance for n == 0. The while(n) test will handle that case perfectly.
  • It's likely your teacher is still used to earlier times, when the result of % with negative operands was differently defined. On some old systems (including, notably, early Unix on a PDP-11, where Dennis Ritchie originally developed C), the result of a % b was always in the range [0 .. b-1], meaning that -123 % 10 was 7. On such a system, the test in advance for n < 0 would be necessary.

But the second bullet applies only to earlier times. In the current versions of both the C and C++ standards, integer division is defined to truncate towards 0, so it turns out that n % 10 is guaranteed to give you the (possibly negative) last digit of n even when n is negative.

So the answer to the question "What is the meaning of while(n)?" is "Exactly the same as while(n != 0)", and the answer to "Will this code work properly for negative as well as positive n?" is "Yes, under any modern, Standards-conforming compiler." The answer to the question "Then why did the instructor mark it down?" is probably that they're not aware of a significant language redefinition that happened to C in 1999 and to C++ in 2010 or so.

klutt
  • 30,332
  • 17
  • 55
  • 95
Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • 1
    I edited my question. Forgot to mention the version. It's C99 or a newer version of C. – user010517720 Oct 03 '19 at 19:04
  • 41
    _"There is no good reason to test in advance for n == 0"_ -- technically, that's correct. But given that we're talking about a professor in a teaching setting, they may appreciate clarity over brevity much more than we do. Adding the extra test for `n == 0` at least makes it immediately and utterly obvious for any reader what happens in that case. Without it, the reader has to satisfy themselves that the loop is indeed skipped, and the default value of `s` returned is the correct one. – ilkkachu Oct 04 '19 at 06:42
  • 23
    Also, the professor may want to know that the student is aware and has thought about why and how the function behaves with an input of zero (i.e. that it doesn't return the correct value _by accident_). They may have met students who don't realize what would happen in that case, that the loop can run zero times, etc. When you're dealing with a teaching setting, it's best to be extra clear about any assumptions and corner cases... – ilkkachu Oct 04 '19 at 06:51
  • 37
    @ilkkachu If that's the case, then the teacher should hand out a task that is requiring such a test to work correctly. – klutt Oct 04 '19 at 09:30
  • 1
    @klutt, yes, that would indeed be better. Of course, that mention was pure speculation on my part, and I don't mean the original solution was wrong as such. It's just that my experience is that a teaching setting may have different rules, or even be somewhat disconnected from the real world. – ilkkachu Oct 04 '19 at 11:05
  • 39
    @ilkkachu Well, I take your point in the general case, because I absolutely appreciate clarity over brevity -- and pretty much all the time, not necessarily just in a pedagogical setting. But with that said, sometimes brevity *is* clarity, and if you can arrange for the main code to handle both the general case *and* the edge case(s), without cluttering the code (and the coverage analysis) with special cases for the edge cases, that's a beautiful thing! I think it's something to be appreciated even at a beginner's level. – Steve Summit Oct 04 '19 at 12:33
  • 10
    How about the teacher requiring the student to hand out a unit-testing routine along with the code they write? there would be some `assert(sum_of_digits_squared(0) == 0)` and `assert(sum_of_digits_squared(-3) == 9)` inside, so that the teacher can see that the student has taken the cases into account, and the production code would be free to be as brief as professionally liked. The UT routine would fail on non-conforming implementations. And the students would take good habits, IMHO. – Laurent LA RIZZA Oct 04 '19 at 12:43
  • 3
    @SteveSummit I agree with you, mostly. Yes, OP's code is better than his professors as it is smaller, equally clear, lacks unnecessary checks, and even (as @klutt pointed out) handles a corner case correctly. However! In a teaching situation it is not enough to get the right answer, you must also show that you know why it is the right answer. The flipside of OP's code being so simple is that a less knowledgeable student could write the same thing, and not even consider `n=0` or `n<0`. Continue... – Frodyne Oct 04 '19 at 12:54
  • 6
    Thus, I think (hope) that OP would have gotten a lot more credit if he had added comments to his code, that made clear that he _had_ considered all the possible inputs and their consequences. For example: `while (n) { // This handles the n=0 case, as the loop will be skipped and s already has been set to 0`. Comments like that may sound silly, but they show the student's thoughts to the teacher, and allow points to be given even for deep thoughts that lead to reduced code. – Frodyne Oct 04 '19 at 12:54
  • 6
    @Frodyne Yes, you must show that you know why the code is correct. But the correct way of doing that is by unit tests or comments or something similar. NOT by writing code that makes other coders wondering if you don't even know that 0 is false and everything else is true. – klutt Oct 04 '19 at 13:04
  • @klutt I agree, and if it sounded like I wanted OP to write more code, then I need to formulate myself better. I _only_ advocate that he should comment his code to hell and back - anything (relevant) he thought about: Write it into a comment, so the teacher can see his thoughts. – Frodyne Oct 04 '19 at 13:10
  • In my university course, we were taught to use C89 for the first two coursework submissions. Perhaps that’s the case for OP. – Tim Oct 04 '19 at 14:45
  • 51
    @ilkkachu By that argument you surely should also add tests for n = 1 and so on. There's nothing special about `n=0`. Introducing unnecessary branches and complications doesn't make the code any easier, it makes it harder since now you not only have to show that the general algorithm is correct you also have to think about all the special cases separately. – Voo Oct 04 '19 at 19:26
  • Regarding change of meaning of `%`, even the oldest standard required that ` (a/b)*b + a%b == a` and while it did leave the rounding, and therefore the sign, implementation defined for negative values, all widely used computers already rounded towards zero as needed here (and it was already specified behaviour for FORTRAN). – Jan Hudec Oct 05 '19 at 21:22
  • @JanHudec: Performance of many programs could be improved by offering a convenient means of performing division that would expressly allow compilers to round negative values up or down in Unspecified fashion, but I'm unaware of any proposals to add such a thing. – supercat Oct 05 '19 at 23:08
  • @Frodyne and then, also add 2*10^7 comments to declare for each possible value that the code does handle it? The fundamental problem of this reasoning is the underlying false assumption that zero was a special case. There is nothing special about zero. The code works correctly for the entire specified range without the need to treat it specially, so why should the author of the code add a comment telling that the code will handle this value in the middle of the specified legal input range? – Holger Nov 01 '19 at 09:16
  • @Holger `n == 0` is the **special** case where the `while(n)` loop stops (or, even more importantly, gets skipped if `n == 0` at the start). Now that we have established that `0` is indeed a special value for this code, let us remember that the code we are talking about here is a homework assignment. That means that writing code that works is secondary, while showing that you know _why_ the code works is the primary. So, my argument that the code needed more "look teacher, I thought about this too" comments still stands. – Frodyne Nov 01 '19 at 10:58
  • @Frodyne so you consider every loop condition a special case and hence, prepend every loop with an `if` statement regarding the loop’s terminal condition? Really? Especially when talking about homework assignment, we should not teach nonsensical coding style. And regarding “I thought about this too”, [this answer](https://stackoverflow.com/a/58225423/2711488) nailed it, the teacher’s code introduced potential undefined behavior where the OP’s code is fine. – Holger Nov 01 '19 at 11:52
  • @Holger No. Please quote the exact comment where I advocated that OP added an `if` statement. I have only ever said to add _comments_ that explicitly show the teacher that the student knows how his code works. And I never said that the teachers code is better. – Frodyne Nov 01 '19 at 11:57
  • @Frodyne it doesn’t matter whether you advocate an `if` statement or an extra comment. Both is not necessary to prove that the author did think about that condition. That’s already indicated by the fact that they put the condition into the loop. – Holger Nov 01 '19 at 12:35
  • @Holger How do you know that the student actually considered negative numbers, or that they could actually get a zero input? They could have failed to consider all that, and just randomly happen to have their code function for all cases. – Frodyne Nov 01 '19 at 14:13
  • 1
    @Frodyne We can never know such a thing, of course, so this is all rather pointless speculation. We can't know what the professor was thinking, either. (We do know, however, that the correction suggested by the professor involved adding code, not comments, suggesting that his concern abut the student's answer was different from yours.) – Steve Summit Nov 01 '19 at 14:16
  • @Frodyne you never know whether a student’s answer is correct, just because of pure luck. But a correct answer is a correct answer. Point. If a teacher wants to test for a particular knowledge, they have to ask the right questions¹, not to insist on redundant declarations of the same information. After all, your suggested clarifying comment could also be the product of pure luck, like the head hitting the keyboard when falling asleep. (¹ like assigning a task where negative numbers truly need special treatment). – Holger Nov 01 '19 at 14:19
  • @SteveSummit If the student had added a `// if n is 0, then the loop is skipped, and s=0 is returned` (plus a `// modulo also works for negative numbers, and the squaring makes everything identical`) then we (and more importantly the teacher) _would_ know that - and would be able to give full credits for it. That is my point. – Frodyne Nov 01 '19 at 14:20
  • @Frodyne I get your point (and I suspect Holger does, too) -- I just don't completely agree with it. (You're not wrong that the comments might "help", and you're not wrong that there might be a professor out there somewhere that explicitly expects them. Personally, though, I'm nowhere near as insistent about any of this. IMO, this particular code is fine without those comments, even in a pedagogical situation -- but we can agree to disagree about this.) – Steve Summit Nov 01 '19 at 14:27
111

Your code is perfectly fine

You are absolutely correct and your teacher is wrong. There is absolutely no reason at all to add that extra complexity, since it does not affect the result at all. It even introduces a bug. (See below)

First, the separate check if n is zero is obviously completely unnecessary and this is very easy to realize. To be honest, I actually question your teachers competence if he has objections about this. But I guess everybody can have a brain fart from time to time. However, I DO think that while(n) should be changed to while(n != 0) because it adds a little bit extra clarity without even costing an extra line. It's a minor thing though.

The second one is a bit more understandable, but he is still wrong.

This is what the C11 standard 6.5.5.p6 says:

If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a; otherwise, the behavior of both a/b and a%b is undefined.

The footnote says this:

This is often called "truncation toward zero".

Truncation toward zero means that the absolute value for a/b is equal to the absolute value for (-a)/b for all a and b, which in turn means that your code is perfectly fine.

Modulo is easy math, but may be counterintuitive

However, your teacher does have a point that you should be careful, because the fact that you're squaring the result is actually crucial here. Calculating a%b according to above definition is easy math, but it might go against your intuition. For multiplication and division, the result is positive if the operands have equal sign. But when it comes to modulo, the result has the same sign as the first operand. The second operand does not affect the sign at all. For instance, 7%3==1 but (-7)%(-3)==(-1).

Here is a snippet demonstrating it:

$ cat > main.c 
#include <stdio.h>

void f(int a, int b) 
{
    printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
           a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}

int main(void)
{
    int a=7, b=3;
    f(a,b);
    f(-a,b);
    f(a,-b);
    f(-a,-b);
}

$ gcc main.c -Wall -Wextra -pedantic -std=c99

$ ./a.out
a:  7 b:  3 a/b:  2 a%b:  1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a: -7 b:  3 a/b: -2 a%b: -1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a:  7 b: -3 a/b: -2 a%b:  1 (a%b)^2:  1 (a/b)*b+a%b==a:  true
a: -7 b: -3 a/b:  2 a%b: -1 (a%b)^2:  1 (a/b)*b+a%b==a:  true

So, ironically, your teacher proved his point by being wrong.

Your teacher's code is flawed

Yes, it actually is. If the input is INT_MIN AND the architecture is two's complement AND the bit pattern where the sign bit is 1 and all value bits are 0 is NOT a trap value (using two's complement without trap values is very common) then your teacher's code will yield undefined behavior on the line n = n * (-1). Your code is - if ever so slightly - better than his. And considering introducing a small bug by making the code unnecessary complex and gaining absolutely zero value, I'd say that your code is MUCH better.

In other words, in compilations where INT_MIN = -32768 (even though the resulting function cannot receive an input that is < -32768 or > 32767), the valid input of -32768 causes undefined behavior, because the result of -(-32768i16) cannot be expressed as a 16-bit integer. (Actually, -32768 probably would not cause an incorrect result, because -(-32768i16) usually evaluates to -32768i16, and your program handles negative numbers correctly.) (SHRT_MIN could be -32768 or -32767, depending on the compiler.)

But your teacher explicitly stated that n can be in the range [-10^7; 10^7]. A 16-bit integer is too small; you would have to use [at least] a 32-bit integer. Using int might seem to make his code safe, except that int is not necessarily a 32-bit integer. If you compile for a 16-bit architecture, both of your code snippets are flawed. But your code is still much better because this scenario reintroduces the bug with INT_MIN mentioned above with his version. To avoid this, you can write long instead of int, which is a 32-bit integer on either architecture. A long is guaranteed to be able to hold any value in the range [-2147483647; 2147483647]. C11 Standard 5.2.4.2.1 LONG_MIN is often -2147483648 but the maximum (yes, maximum, it's a negative number) allowed value for LONG_MIN is -2147483647.

What changes would I make to your code?

Your code is fine as it is, so these are not really complaints. It's more like that if I really, really need to say anything about your code, there are some small things that could make it just a tiny bit clearer.

  • The names of the variables could be a little bit better, but it is a short function that is easy to understand, so it's not a big deal.
  • You could change the condition from n to n!=0. Semantically, it's 100% equivalent, but it makes it a little bit clearer.
  • Move declaration of c (which I renamed to digit) to inside the while loop since it's only used there.
  • Change argument type to long to ensure it can handle the whole input set.
int sum_of_digits_squared(long n) 
{
    long sum = 0;

    while (n != 0) {
        int digit = n % 10;
        sum += (digit * digit);
        n /= 10;
    }

    return sum;
}

Actually, this can be a little bit misleading because - as mentioned above - the variable digit can get a negative value, but a digit is in itself never either positive or negative. There are a few ways around this, but this is REALLY nitpicking, and I would not care for such small details. Especially the separate function for last digit is taking it too far. Ironically, this is one of the things that your teachers code actually solves.

  • Change sum += (digit * digit) to sum += ((n%10)*(n%10)) and skip the variable digit completely.
  • Change the sign of digit if negative. But I would strongly advice against making the code more complex just to make a variable name make sense. That's a VERY strong code smell.
  • Create a separate function that extracts the last digit. int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; } This is useful if you want to use that function somewhere else.
  • Just name it c as you originally do. That variable name does not give any useful information, but on the other hand, it's not misleading either.

But to be honest, at this point you should move on to more important work. :)

claymation
  • 2,475
  • 4
  • 27
  • 36
klutt
  • 30,332
  • 17
  • 55
  • 95
  • 19
    *If the input is `INT_MIN` and the architecture is using two's complement (which is very common) then your teachers code will yield undefined behavior.* Ouch. That will leave a mark. ;-) – Andrew Henle Oct 03 '19 at 22:03
  • 2
    @AndrewHenle Yeah, I got really happy when that train of thought came through my brain. I'm so hoping OP will show that to his teacher. XD – klutt Oct 03 '19 at 22:05
  • 2
    That's some really nice bug spotting, too. I'm just hoping the teacher isn't one of THOSE that punish students for proving them wrong. – Andrew Henle Oct 03 '19 at 22:06
  • 3
    @AndrewHenle Let's hope so. And this example really shows how tricky it can be to forsee everything when dealing with lowlevel languages. I would not blame anyone for missing that bug. – klutt Oct 03 '19 at 22:19
  • @AndrewHenle There was another condition that I missed first which makes it even trickier. Look at my updated answer. – klutt Oct 03 '19 at 22:22
  • 2
    Yeah, but "two's complement without trap values" is what? 99.9999999% of all CPUs out there today? ([And it might actually be that high, if not higher](https://superuser.com/questions/1137182/is-there-any-existing-cpu-implementation-which-uses-ones-complement)...) – Andrew Henle Oct 03 '19 at 22:30
  • 3
    @AndrewHenle I agree. That's a theoretical matter. But look one more time. I spotted that the teachers (and OP:s) used a data type that is not guaranteed to be able to represent all the values in the input set. :D – klutt Oct 03 '19 at 22:32
  • 2
    Another good catch. I think you could make a good living selling your services doing code reviews. ;-) But pedantically, `int32_t` is an optional type - `int_least32_t` and `int_fast32_t` are required types. – Andrew Henle Oct 03 '19 at 23:52
  • 1
    @AndrewHenle Somehow I knew my luck should end somewhere. So in (pun intended) return: Good catch! – klutt Oct 03 '19 at 23:55
  • 5
    It should be mentioned that in addition to `(a/b)*b + a%b ≡ a`, the OP's code also hinges on the fact that `/` rounds towards zero, and that `(-c)*(-c) ≡ c*c`. It _could_ be argued that the extra checks are justified despite standard guaranteeing all that, because it's sufficiently non-obvious. (Of course it could equally well be argued that there should rather be a comment linking the relevant standard sections, but style guidelines vary.) – leftaroundabout Oct 04 '19 at 03:13
  • "*I cannot imagine that you're using an older C-version than that.*" Some compiler optimization switches might perform division operations more quickly but give wrong results for the modulo operator when using negative numbers. – Martin Rosenau Oct 04 '19 at 05:12
  • 8
    @MartinRosenau You say "might". Are you sure this is actually happening or that it is allowed by the standard or something or are you just speculating? – klutt Oct 04 '19 at 08:36
  • 6
    @MartinRosenau: Ok, but using those switches would make it not C anymore. GCC / clang don't have any switches that break integer division on any ISA I'm aware of. Even though ignoring the sign bit could maybe give a speedup using the normal multiplicative inverse for constant divisors. (But all the ISAs I'm familiar that have a hardware division instruction implement it the C99 way, truncating toward zero, so C `%` and `/` operators can compile to just an `idiv` on x86, or `sdiv` on ARM or whatever. Still, that's unrelated to the much faster code-gen for compile-time-constant divisors) – Peter Cordes Oct 04 '19 at 09:47
  • The other option for doing `abs` safely is to produce an `unsigned` (or `uint_least32_t`) result. I'm not sure how easy that is without signed-overflow UB *and* without assuming a 2's complement implementation, though. Or does `0U - x` work even on sign/magnitude or 1's complement? I think so, but it might compile to more work. (`int`<->`unsigned` is just a type-pun / reinterpret on a 2's complement machine, but others may need to adjust the bit-pattern to implement the modulo-reduction). As a bonus, using unsigned integers for the division loop makes it faster. – Peter Cordes Oct 04 '19 at 09:55
  • 1
    On a two's complement machine, INT_MIN * -1 is just INT_MIN. So the code still works (albeit more by luck than judgment). – TonyK Oct 04 '19 at 11:20
  • 6
    @TonyK AFIK, that's how it's usually solved, but according to the standard it's UB. – klutt Oct 04 '19 at 12:11
  • The behaviour of the OPs code is implentation-defined in C89 " If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator." – plugwash Oct 04 '19 at 14:09
  • The sign change will not yield any undefined behavior if the input is confined to the range that is specified in the problem description. If n is outside of that range, then the caller is abusing the function; any UB related to that is the caller's fault. – Kaz Oct 04 '19 at 21:24
  • @plugwash Yes, but since it's squared afterwards that's enough. – klutt Oct 05 '19 at 06:31
  • @Kaz Yep, that is true. But why make the code more complex with zero gain just because the input set is restricted? – klutt Oct 05 '19 at 06:31
  • 1
    @klutt "since it's squared afterwards that's enough" -- No! In one mode `n = -123` leads to `n / 10 == -12` and `n % 10 == -3` whereas the other leads to `n / 10 == -13` and `n % 10 == 7`. And of course `(-3)*(-3)` is not the same as `7*7`. – Hagen von Eitzen Oct 05 '19 at 09:33
  • I should note a potential issue here: As I interpret 6.5.5.p6, the behavior of a/b and a%b are undefined if the quotient a/b is a non-integer and an integer data type is involved (because such quotient is not _representable_ in any integer data type). For example, as I interpret that section, the behavior of `(int)100/(int)3` and `(int)100%(int)3` is undefined because 100/3 is not representable in `int`. – Peter O. Oct 13 '19 at 11:28
  • 1
    @PeterO. That interpretation is completely wrong. Integer division is well defined and `100/3` will always yield the result `30`. – klutt Oct 13 '19 at 23:46
  • @klutt: As it seems, there are [two interpretations](http://mathworld.wolfram.com/Quotient.html) of the word "quotient", and the C/C++ standard uses quotient to mean the integer part of the ratio of two numbers. – Peter O. Nov 01 '19 at 07:15
  • "You are absolutely correct and your teacher is wrong" - such comments give out a false sense of superiority. Code readability is extremenly important in large projects with over a million lines of code. The teachers code is more readable. Some C programmers love to show off how clever they are by cramming loads of instructions into a single line. 12 months later when they come back to make a change they have no idea what the code was supposed to do. – Paul McCarthy Jan 06 '21 at 10:46
  • 1
    @PaulMcCarthy There's something in between showing off by cramming lots of things into a single line and writing code that gives the reader the impression that you don't even know the basics of the language. And more lines does not automatically imply more readability. – klutt Jan 06 '21 at 22:24
  • @klutt If you can write code that never needs it then it is not the bacics, it's extra useful stuff that saves time. I'm not a big fan of the unary negation operator because an accidental press of the delete key while scrolling through the code could negate (pun intended) the meaning of the code. Nowadays with modern source code control it's not likely to be missed. Trying to teach coding is a tough job because there's such variation in the abilities of the students. – Paul McCarthy Jan 08 '21 at 14:15
  • 2
    @PaulMcCarthy I also avoid certain constructs for safety and readability. I just don't see how that is applicable in this case. You were talking about "cramming loads of instructions into a single line" but that's not the case for OP:s code. The code is clean and to the point. The teachers code not only adds extra lines, but also extra instructions that are already covered. In a large project, such things means extra place for bugs to hide in. If you have an algorithm that does not need to handle corner cases, why handle corner cases? – klutt Jan 08 '21 at 18:52
  • @klutt The teacher's code clearly illustrates that he has considered the edge case. Did the student initially consider it? Or did he only claim to have considered it after he saw the teacher's code. Only he/she can tell you that. There's a whole section of CS dedicated to mathematically proving code works, I never liked studying it. The idea is to know the code will work without having to run it. In theory it's for extreme scenarios like nuclear launch software. – Paul McCarthy Jan 12 '21 at 11:25
  • 2
    @PaulMcCarthy We only have OP:s description of it. I judge it from that. Sure it MAY be requirements from the teacher that we don't know about, but I see very little point in speculating about that. I judge it as production code and from how this question is asked. Speaking about proving mathematically that the code works, I have worked with [spec#](https://www.microsoft.com/en-us/research/project/spec/) for doing precisely that, but that's clearly out of scope for this question. – klutt Jan 12 '21 at 16:09
23

I don't completely like either your version or your teacher's. Your teacher's version does the extra tests that you correctly point out are unnecessary. C's mod operator is not a proper mathematical mod: a negative number mod 10 will produce a negative result (proper mathematical modulus is always non-negative). But since you're squaring it anyway, no difference.

But this is far from obvious, so I would add to your code not the checks of your teacher, but a big comment that explains why it works. E.g.:

/* NOTE: This works for negative values, because the modulus gets squared */

Lee Daniel Crocker
  • 12,927
  • 3
  • 29
  • 55
  • 9
    C's `%` is best called a *remainder*, because it is that, even for signed types. – Peter Cordes Oct 04 '19 at 09:48
  • 15
    The squaring is important, but I think that's the obvious part. What should be pointed out is that (e.g.) `-7 % 10` *will actually be `-7`* rather than 3. – Jacob Raihle Oct 04 '19 at 14:33
  • 7
    “proper mathematical modulus” does not mean anything. The correct term is “Euclidean modulo” (mind the suffix!) and that is indeed what C's `%` operator is not. – Jan Hudec Oct 05 '19 at 21:35
  • I like this answer because it settles the question of multiple ways to interpret modulo. Never leave a thing like that to chance/interpretation. This isn't code golf. – Harper - Reinstate Monica Oct 06 '19 at 21:22
  • 1
    *"proper mathematical modulus is always non-negative"* - Not really. The result of a modulo operation is an *equivalence class*, but it's common to treat the result as the smallest non-negative number belonging to that class. – klutt Oct 24 '19 at 11:02
10

NOTE: AS I was writing this answer, you did clarify that you are using C. The majority of my answer is about C++. However, since your title still has C++ and the question is still tagged C++, I have chosen to answer anyway in case this is still useful to other people, especially since most of the answers I've seen till now are mostly unsatisfactory.

In modern C++ (Note: I don't really know where C stands on this), your professor seems to be wrong on both counts.

First is this part right here:

if (n == 0) {
        return 0;
}

In C++, this is basically the same thing as:

if (!n) {
        return 0;
}

That means your while is equivalent to something like this:

while(n != 0) {
    // some implementation
}

That means since you are merely exiting in your if when the while wouldn't execute anyway, there really isn't a reason to put this if here, since what you are doing after the loop and in the if are equivalent anyway. Although I should say that is for some reason these were different, you'd need to have this if.

So really, this if statement isn't particularly useful unless I'm mistaken.

The second part is where things get hairy:

if (n < 0) {
    n = n * (-1);
}  

The heart of the issue is is what the output of the modulus of a negative number outputs.

In modern C++, this seems to be mostly well defined:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. For integral operands the / operator yields the algebraic quotient with any fractional part discarded; if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a.

And later:

If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.

As the poster of the quoted answer correctly points out, the important part of this equation right here:

(a/b)*b + a%b

Taking an example of your case, you'd get something like this:

-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3 

The only catch is that last line:

If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined.

That means that in a case like this, only the sign seems to be implementation-defined. That shouldn't be a problem in your case because, because you are squaring this value anyway.

That said, keep in mind that this doesn't necessarily apply to earlier versions of C++, or C99. If that is what your professor is using, that could be why.


EDIT: Nope, I'm wrong. This seems to be the case for C99 or later as well:

C99 requires that when a/b is representable:

(a/b) * b + a%b shall equal a

And another place:

When integers are divided and the division is inexact, if both operands are positive the result of the / operator is the largest integer less than the algebraic quotient and the result of the % operator is positive. If either operand is negative, whether the result of the / operator is the largest integer less than the algebraic quotient or the smallest integer greater than the algebraic quotient is implementation-defined, as is the sign of the result of the % operator. If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.

Does either ANSI C or ISO C specify what -5 % 10 should be?

So, yeah. Even in C99, this doesn't seem to affect you. The equation is the same.

  • 1
    The parts you've quoted don't support this answer. "the sign of the remainder is implementation defined" doesn't mean `(-1)%10` could produce `-1` or `1`; it means it could produce `-1` or `9`, and in the latter case `(-1)/10` will produce `-1` and OP's code will never terminate. – stewbasic Oct 04 '19 at 04:06
  • Could you point to a source for this? I have a very hard time believing (-1)/10 is -1. That should be 0. Also, (-1)%10 = 9 seems to violate the governing equation. –  Oct 04 '19 at 05:42
  • 1
    @Chipster, start with `(a/b)*b + a%b == a`, then let `a=-1; b=10`, giving `(-1/10)*10 + (-1)%10 == -1`. Now, if `-1/10` indeed gets rounded down (towards -inf), then we have `(-1/10)*10 == -10`, and you need to have `(-1)%10 == 9` for the first equation to match. Like the [other answers](https://stackoverflow.com/a/58225192/6372809) state, this isn't how it works within the current standard(s), but it's how it used to work. It's not really about the sign of the remainder as such, but how the division rounds and what the remainder then _has_ to be to satisfy the equation. – ilkkachu Oct 04 '19 at 06:16
  • 1
    @Chipster The source is the snippets you've quoted. Note that `(-1)*10+9=-1`, so the choice `(-1)/10=-1` and `(-1)%10=9` does not violate the governing equation. On the other hand the choice `(-1)%10=1` cannot satisfy the governing equation no matter how `(-1)/10` is chosen; there is no integer `q` such that `q*10+1=-1`. – stewbasic Oct 04 '19 at 19:00
9

As others have pointed out, the special treatment for n==0 is nonsense, since for every serious C programmer it is obvious that "while(n)" does the job.

The behaviour for n<0 is not that obvious, that's why I would prefer to see those 2 lines of code:

if (n < 0) 
    n = -n;

or at least a comment:

// don't worry, works for n < 0 as well

Honestly, at what time did you start considering that n might be negative? When writing the code or when reading your teacher's remarks?

C.B.
  • 208
  • 2
  • 7
  • 1
    Regardless of whether N is negative, N squared will be positive. So, why remove the sign in the first place? -3 * -3 = 9; 3 * 3 = 9. Or has math changed in the 30-odd years since I learned this? – Merovex Oct 18 '19 at 08:31
  • 2
    @C.B. To be fair, I didn't even notice that n could be negative while I was writing the test, but when it came back I just had a feeling that the while loop would not be skipped, even if the number is negative. I did some tests on my computer and it confirmed my skepticism. After that, I posted this question. So no, I wasn't thinking so deeply while writing the code. – user010517720 Oct 18 '19 at 20:57
6

This reminds me of an assignment that I failed

Way back in the 90's. The lecturer had been sprouting on about loops and, long story short, our assignment was to write a function that would return the number of digits for any given integer > 0.

So, for example, the number of digits in 321 would be 3.

Although the assignment simply said to write a function that returned the number of digits, the expectation was that we would use a loop that divides by 10 until... you get it, as covered by the lecture.

But using loops was not explicitly stated so I: took the log, stripped away the decimals, added 1 and was subsequently lambasted in front of the whole class.

Point is, the purpose of the assignment was to test our understanding of what we had learned during lectures. From the lecture I received I learned the computer teacher was a bit of a jerk (but perhaps a jerk with a plan?)


In your situation:

write a function in C/C++ that returns the sum of the digits of the number squared

I would definitely have provided two answers:

  • the correct answer (squaring the number first), and
  • the incorrect answer in keeping with the example, just to keep him happy ;-)
Community
  • 1
  • 1
SlowLearner
  • 3,086
  • 24
  • 54
  • 5
    And also a third one squaring the sum of the digits ? – kriss Oct 16 '19 at 10:55
  • @kriss - yeah, I'm not that smart :-( – SlowLearner Oct 17 '19 at 10:41
  • 1
    I also had my share of too vague assigments in my student time. A teacher wanted a bit manipulation library but he was surprised by my code and said it wasn't working. I had to point out to him that he never defined endianness in his assignment and he picked lower bit as bit 0 while I made the other choice. The only annoying part is that he should have been able to figure out where the difference was without me telling him. – kriss Oct 17 '19 at 14:25
1

Generally in assignments not all the marks are given just because the code works. You also get marks for making a solution easy to read, efficient and elegant. These things are not always mutually exclusive.

One I can't strees enough is "use meaningful variable names".

In your example it does not make much difference, but if you're working on a project with a milion lines of code readability becomes very important.

Another thing I tend to see with C code is people trying to look clever. Rather than using while(n != 0) I'll show everyone how clever I am by writing while(n) because it means the same thing. Well it does in the compiler you have but as you suggested you teacher's older version has not implemented it the same way.

A common example is referencing an index in an array while incrementing it at the same time ; Numbers[i++] = iPrime;

Now, the next programmer who works on the code has to know if i gets incremented before or after the assignment, just so someone could show off.

A megabyte of disk space is cheaper that a roll of toilet paper, go for clarity rather than trying to save space, your fellow programmers will be happier.

Paul McCarthy
  • 818
  • 9
  • 24
  • 2
    I've programmed in C only a handful of times and I know that `++i` increments before evaluation and `i++` increments afterwards. `while(n)` is also a common language feature. Based on logic like this I've seen plenty of code like `if (foo == TRUE)`. I agree re: variable names, though. – alan ocallaghan Oct 17 '19 at 09:21
  • It is good that you know but if the code were written on 2 lines you would not need to know. In big projects you need to know a lot about how the whole solution fits together so neat language features that save a line of code at the expense of readability are not really useful. What if your junior programmers don't know, they have to look it up. What if your boss doesn't know and now sees you as a threat. – Paul McCarthy Oct 17 '19 at 10:33
  • 4
    Generally that's not bad advice, but avoiding basic language features (that people will inevitably come across anyway) for the purpose of defensive programming is excessive. Short, clear code is often more readable anyway. We're not talking about crazy perl or bash one-liners here, just really basic language features. – alan ocallaghan Oct 17 '19 at 10:48
  • Using it can lead to errors so avoiding its use is good programming practice. The general principle is that a line of code should do one thing. The line below is certainly confusing and it is silly to use it in your code when it gives no benefit. array[i++] = array[i++] * 2 * array[++i]; – Paul McCarthy Oct 17 '19 at 16:03
  • 1
    Not sure, what the downvotes on this answer were given for. To me everything stated here is true and important and a good advice for every developer. Especially the smart-ass-programming part, even though `while(n)` is not the worst example for that (I "like" the `if(strcmp(one, two))` more) – Kai Huppmann Oct 18 '19 at 09:07
  • `for(i=0; s[i]; strncmp(&s[i], split, strlen(split)) ? *s++ : i++);` is abusing the language. A simple `array[i++] = value;` is not. Using `array[i++] = array[i++] * 2 * array[++i];` is not only bad practice. It's plainly wrong. You cannot compare things that causes UB with something that may or may not impact readability. I agree on the general principals you are writing about, but your examples are taken to the extreme. It reminds me of the quote "Blindly following best practices is not best practice" – klutt Oct 20 '19 at 15:45
  • 1
    And besides, you really should not let anyone who does not know the difference between `i++` and `++i` modify C code that should be used in production. – klutt Oct 20 '19 at 16:03
  • @KaiHuppmann The downvotes (at least mine) are for blindly following best practice rules without critical thinking. Python coders are often talking about the "pythonic" way of doing things. Well, among C coders, there is the "cic" (haha, maybe just invented a new word) way of doing things. – klutt Oct 20 '19 at 16:03
  • @klutt There is a big list of things people should know before modifying production code that they don't know. They modify it anyway, because they have to. Having less things on the list and more clearer code makes for less errors. I recommend reading Joel's article https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/ – Paul McCarthy Oct 21 '19 at 10:00
  • 1
    @PaulMcCarthy Yes, but the difference between ++i and i++ is something you typically learn the first lesson in a C course. As I said, I don't question the basic principals you're stating. I'm questioning on which level you should enforce them. To me, this does not sound far away from comments like `// The main function` or `// Declare an int variable` – klutt Oct 21 '19 at 11:31
  • 1
    @PaulMcCarthy However, I do agree that in most cases `while(n!=0)` is preferable above `while(n)`. It makes it a little bit clearer without adding an extra line, but I also think that it is a minor detail not worth spending time on. – klutt Oct 21 '19 at 11:35
  • @klutt Yes, agreed, we have drifted away from the original question. Each organization sets and enforces its standards according to what's best for it. I'm all for code readability because I've seen situations where all the staff who wrote the original code have left the company and it takes ages ages to work out what some of their code was supposed to do. – Paul McCarthy Oct 21 '19 at 15:54
  • 2
    @PaulMcCarthy We are both for code readability. But we disagree on what that means. Also, it's not objective. What's easy to read for one person can be hard for another. Personality and background knowledge strongly affects this. My main point is that you do not get maximum readability by blindly following some rules. – klutt Oct 22 '19 at 11:31
  • @klutt I think we both agree. I have never suggested to blindly follow rules. An organization with standards is much more likely to have readable code than one with no standards. My main point is just because it works does not make it good code. – Paul McCarthy Oct 22 '19 at 16:02
  • 1
    *A megabyte of disk space is cheaper that a roll of toilet paper*... but you can hardly use one when you need the other :) – chqrlie Dec 08 '19 at 11:38
  • A year later, but I have to comment on your comparison with `array[i++] = array[i++] * 2 * array[++i];`. That line is not only hard to read. It does also invoke undefined behavior, so the comparison is not good. – klutt Dec 26 '20 at 01:52
1

The problem statement is confusing, but the numerical example clarifies the meaning of the sum of the digits of the number squared. Here is an improved version:

Write a function in the common subset of C and C++ that takes an integer n in the range [-107, 107] and returns the sum of the squares of the digits of its representation in base 10. Example: if n is 123, your function should return 14 (12 + 22 + 32 = 14).

The function that you wrote is fine except for 2 details:

  • The argument should have type long to accommodate for all values in the specified range as type long is guaranteed by the C Standard to have at least 31 value bits, hence a range sufficient to represent all values in [-107, 107]. (Note that type int is sufficient for the return type, whose maximum value is 568.)
  • The behavior of % for negative operands is non-intuitive and its specification varied between the C99 Standard and previous editions. You should document why your approach is valid even for negative inputs.

Here is a modified version:

int sum_of_digits_squared(long n) {
    int s = 0;

    while (n != 0) {
        /* Since integer division is defined to truncate toward 0
           in C99 and C++98 and later, the remainder of this division
           is positive for positive `n` and negative for negative
           `n`, and its absolute value is the last digit of the
           representation of `n` in base 10.
           Squaring this value yields the expected result for both
           positive and negative `c`. Dividing `n` by 10 effectively
           drops the last digit in both cases.
           The loop will not be entered for `n == 0`, producing the
           correct result `s = 0`.
         */
        int c = n % 10;
        s += c * c;
        n /= 10;
    }
    return s;
}

The teacher's answer has multiple flaws:

  • type int may have an insufficient range of values.
  • there is no need to special case the value 0.
  • negating negative values is unnecessary and may have undefined behavior for n = INT_MIN.

Given the extra constraints in the problem statement (C99 and range of values for n), only the first flaw is an issue. The extra code still produces the correct answers.

You should get a good mark in this test, but the explanation is required in a written test to show your understanding of the issues for negative n, otherwise the teacher may assume that you were unaware and just got lucky. In an oral exam, you would have gotten a question and your answer would have nailed it.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
-2

I wouldn't argue about whether the original or the modern definition of '%' is better but anyone who writes two return statement into such a short function shouldn't teach C programming at all. Extra return is a goto statement and we don't use goto in C. Furthermore the code without the zero check would have the same result, extra return made it harder to read.

Peter Krassoi
  • 571
  • 3
  • 11
  • 5
    *"Extra return is a goto statement and we don't use goto in C."* -- that's a combination of a very broad generalization and a very far stretch. – S.S. Anne Oct 16 '19 at 11:30
  • @JL2210: Why? If return is the last statement it is a push operation otherwise it's a push and a jump (i.e. goto). Imagine this code in a try-finally block and you can see the goto statement. – Peter Krassoi Oct 17 '19 at 09:36
  • 1
    "We" definitely use goto in C. There's nothing wrong with that. – klutt Oct 20 '19 at 15:29
  • 2
    Also, there's nothing wrong with a function like `int findChar(char *str, char c) { if(!str) return -1; int i=0; while(str[i]) { if(str[i] == c) return i; i++; } return -1; }` – klutt Oct 20 '19 at 16:24
  • 1
    @PeterKrassoi I'm not advocating hard-to-read code, but I have seen tons of examples where the code looks like a complete mess just to avoid a simple goto or an extra return statement. Here is some code with appropriate use of goto. I challenge you to remove the goto statements while at the same time making the code easier to read and mantain: https://pastebin.com/aNaGb66Q – klutt Oct 22 '19 at 10:10
  • 1
    @PeterKrassoi Please also show your version of this: https://pastebin.com/qBmR78KA – klutt Oct 22 '19 at 10:22
  • @PeterKrassoi And just to be clear. It's supposed to be some work after the triple nested loop. I forgot to add that. – klutt Oct 22 '19 at 11:37
  • @klutt: Are you kidding? while(str[i] && str[i] != c) i++; return (str[i] ? i : -1 ); will do what you want. – Peter Krassoi Nov 04 '19 at 12:58
  • @klutt: I saw your malloc example. This is a perfect example why we use exceptions (and no goto). If malloc fails, surely the whole program fails. So do nothing more, log the error and quit. – Peter Krassoi Nov 04 '19 at 13:09
  • @PeterKrassoi I'm perfectly aware that I could have written that function in a better way. That was not the point. My point is that it's nothing wrong with having a few checks that will return immediately on failure in the beginning of a function. – klutt Nov 04 '19 at 13:11
  • 1
    Exceptions? There are no exceptions in C. And it's not necessary for a program to fail just because a malloc fails. Furthermore, this technique is not limited to memory allocation. It could be used for other resources too, like files. – klutt Nov 04 '19 at 13:15
  • Also, I'm a bit unsure what you mean with your improved version of `findChar`. Is that the complete body? Because if that's the case, it does not return -1 when `str` is a NULL pointer. – klutt Nov 04 '19 at 13:20
  • One example of where I have recovered after a failed malloc was when it was possible to gain a massive performance boost by using an algorithm that required huge amounts of memory. If the allocation failed, the program instead used a slower version that required less memory. And you never said how you would jump out of a triple nested loop. – klutt Nov 04 '19 at 14:34
  • @klutt: "My point is that it's nothing wrong with having a few checks that will return immediately" It is wrong design, no more words about it. If the function is short, it's unnecessary, if it's long, it is confusing. – Peter Krassoi Nov 13 '19 at 14:27
  • @PeterKrassoi And yet you failed to rewrite it in an equivalent way. Also, you're ignoring a vast majority of my questions and counterarguments. When I search around I find very little support for your claim that *"it is wrong design, no more words about it"'*. Quite the opposite in fact. The general consensus seems just to use multiple return statement with care, but not forbid them and that they are perfectly ok in guard code. Here is a locked question about it: https://stackoverflow.com/q/36707/6699433 – klutt Nov 13 '19 at 16:29
  • Actually, I would argue that it's worse to have multiple return statements in longer functions. In shorter functions it is much easier to understand the whole picture and then it does not matter so much. This is also something Martin Fowler points out in his book "Refactoring". – klutt Nov 13 '19 at 16:30
  • *anyone who writes two return statement into such a short function shouldn't teach C programming at all* Waay late here, but that is **asinine**. What is the point of force-cramming a square peg of some algorithm into the round hole of "only one return statement"? Deliberately adding extraneous levels of indentation that only confuses things? If the algorithm is simpler to implement with multiple return statements, **implement it with multiple return statements**. `s/anyone who writes two return statement into such a short function/close-minded pedants/g` – Andrew Henle Jan 17 '23 at 23:23