0
\$\begingroup\$

I am trying to print out the squares of n numbers, n which is a value retrieved from the user. I want to know if there is a better way to do this:

#include <stdio.h>

int main(void){
    int i, n;
    printf("This program prints a table of squares.\n");
    printf("Enter a number of entries in table: ");
    scanf("%d", &n);
    getchar();

    for(i = 1; i <= n; i++){
        printf("%10d%10d\n", i, i*i);
        if(i % 24 == 0){
            printf("Press Enter to continue...");
            if(getchar() == '\n'){
                continue;
            }
        }
    }
    return 0;
}
\$\endgroup\$

3 Answers 3

3
\$\begingroup\$

Input handling

scanf("%d", &n);

We can't safely use n unless scanf() successfully converted at least one value (and it can't convert more, because we only asked for one conversion). Therefore:

if (scanf("%d", &n) != 1) {
    fputs("Input should be numeric!\n", stderr);
    return EXIT_FAILURE;
}

Consider also checking that n is positive.

We could loop until we get a valid input. Personally, I would take the number to generate as a command-line argument:

#include <stdlib.h>

int main(int argc, char **argv)
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s NUMBER\n", argv[0]);
        return EXIT_FAILURE;
    }
    char *end;
    long n = strtol(argv[1], &end, 0);
    if (n < 1 || !*end) {
        fprintf(stderr, "Usage: %s NUMBER\n", argv[0]);
        return EXIT_FAILURE;
    }
    ⋮
}

Or we could just generate an "infinite" list, and let users filter with standard tools (head) to get the length required. When I say "infinite" list in quotes like that, I mean that we should stop it only when i * i would overflow its type. We could change i to unsigned long long (or even uintmax_t from <stdint.h>) if we will really need the extra range.


Paging

    if(i % 24 == 0){
        printf("Press Enter to continue...");
        if(getchar() == '\n'){
            continue;
        }
    }

It seems inconvenient to have output stop every 24 lines, even when I'm using a much larger (virtual) terminal, or writing output to a file. It would be better if we adapted better to the terminal size (e.g. using getenv("ROWS") if that's present). However, this is something that we have standard tools for, and interactive users will probably want to use their own choice of pager. So my recommendation is to not duplicate that functionality in one's own programs.

In any case, there's a bug. If the input character is not a newline, then the behaviour is no different to when it is - continue at the end of a loop skips nothing!


Minor bits

For very large integers, the format string %10d%10d will run the numbers into each other. It's a good idea to have at least one space so that when the minimum width overflows, they are still separated.

In main() (and only that function), we can omit return 0; at the end.


Simplified program

The result is greatly pared down:

#include <stdint.h>
#include <stdio.h>

int main(void)
{
    for (int i = 1;  i <= INT_MAX/i;  ++i) {
        printf("%10d %10d\n", i, i*i);
    }
}

We can use this in pipelines:

squares | head -n 20  >twenty_squares

And interactively:

squares | head -n 200 | more

Or interactively until we get bored:

squares | more
\$\endgroup\$
0
2
\$\begingroup\$

Overflow

i*i can overflow (given a pause every 24, it would take a lot of enters).

To prevent overflow, use a wider type for the multiplicaiton.

#include <stdint.h>

// printf("%10d%10d\n", i, i*i);
printf("%10d %10lld\n", i, 1LL*i*i);
// or better
printf("%10d %10jd\n", i, (intmax_t)i*i);
\$\endgroup\$
1
  • \$\begingroup\$ @TobySpeight Interesting. I pulled <inttypes.h> from C spec 7.8.1 7 example. Perhaps the C spec example needs improvement to also include <stdint.h> or maybe that gets pulled in by <inttypes.h>? \$\endgroup\$
    – chux
    Commented Aug 26, 2021 at 13:36
1
\$\begingroup\$

Intent here is to print square of consecutive integer number. As square of (N-1) is already computed in previous iteration, square of N can be computed by

N*N = (N-1)*(N-1) + 2*(N-1) + 1 = (N-1)*(N-1) + ((N-1) << 1) + 1

So essentially we need one shift and two add operation rather than multiplication. For smaller values of N, performance difference will be negligible but for large value of N it can be beneficial.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.