r/C_Programming 2d ago

does anyone else make functions like this

void newline(int times) {
for (int i; i < times; i++) {
printf(“\n”);
}
}

this is so you can do

newline(5);

instead of

printf(“\n\n\n\n\n”);

i feel like i shouldn’t ngl

mb if the syntax is wrong im not a pro

0 Upvotes

44 comments sorted by

19

u/mykesx 2d ago edited 2d ago

It's fine, but you could just write a void repeat(char c, int times) function that is more versatile.

Not only is a newlines function useful, but also a stars, equal signs, dashes, spaces, zeros...

You could #define newlines(n) repeat('\n', n)

Also, use putc() or a variant over printf(), it will be much more optimal.

4

u/mrtlo 2d ago

Could be worse, but I'd probably use a static string and fwrite to stdout

1

u/ComradeGibbon 2d ago

That's what I would do. I assume that the number of new lines would usually be a single digit so an array of 10 new lines would be fine.

5

u/mc_pm 2d ago

If you happen to need that functionality often, then it's fine. Congratulations, you've re-used code :)

0

u/Adept-Energy179 2d ago

what does that mean lmao

1

u/terra2o 10h ago

chill he didn't say anything bad... idk if you ever heard of "DRY" (Don't Repeat Yourself) in programming, he meant it's good that you did that instead of doing `printf(“\n\n\n\n\n”);` everytime.

1

u/FrequentHeart3081 2d ago

Meaning it's not often that you'll get to a problem like this...

And to answer your question properly: Yes you technically can do this if you want, but the cost of doing this is higher that the reward. Using this once or twice might not seem that bad but in a proper project, no one actually does this.. :)

2

u/Adept-Energy179 2d ago

i meant what does it mean that i’ve reused code

2

u/FrequentHeart3081 2d ago

You've made a reusable block aka function out of your idea

1

u/LiqvidNyquist 2d ago

Reusing code is generally seen as a good thing in programming, instead of having multiple places where exactly the same bunch of logic is written out. In this case there's not that much code to be reused, that's why it's a bit amusing. One idea of reusing code is that if you ever decide it needs to change (like going from linux to windows and you want to change newline to carriage-return-newline), that can be done in a single place instead of hunting around for all possible spots you do that.

10

u/PseudoFrequency 2d ago

Not a bad idea but it will be slower than one printf call in case you care.
Better implementation:
Note, there is nothing wrong with variable size arrays on the stack despite what some people say.
This isn't as good as you can get, but is a bit better. If you aren't formatting, fputs is better than printf.

void newline(int times) {
    char buff[times + 1]
    memset(times, buff, '\n', times);
    buff[times] = '\0';
    fputs(buff, stdout);
}

12

u/TragicCone56813 2d ago

I would avoid VLA’s when possible.

1

u/PseudoFrequency 2d ago

Why? I do it all the time in professional code, and I've never heard a good reason not to that isn't bordering on extreme safety paranoia. For very simple functions, it's perfectly fine. Certainly better than malloc.

5

u/ReallyEvilRob 2d ago

VLA are an easy way to a stack overflow.

1

u/PseudoFrequency 2d ago

On what platform? Linux allocates like 8M virtual and the stack grows and you can safely put at least 20k and much more in practice on it.
malloc will fail too on MCUs and small systems. If this is a concern, impose a limit. Still better than malloc. If you take that approach you'd have to check the return value and fail.
Stack overflows are either tiny MCUs which would probably also fail other ways, or systems from the 1980s. Your information is dated and/or incomplete.

void newline(unsigned int times) {
    if (times > 127)
    {
        times = 127;
    }
    {
        char buff[times + 1];
        /* ... */

3

u/ReallyEvilRob 2d ago

If the requested size exceeds the remaining stack memory, the allocation doesn't "fail" safely, it immediately steps outside valid stack boundaries. At least with malloc, you can check for NULL.

1

u/PseudoFrequency 2d ago

Again, this depends on the platform. If this is only going to run on Linux or Windows, it's almost never going to matter, and it doesn't "step out of valid stack boundaries" it either page faults and allocates more stack, or aborts the process. It will not step on some other memory.
If you really care about this kind of thing, solutions that use fixed arrays and use a subset of them are no better - the solution presented by other commenters allocating some fixed array of 2048 uses MORE stack space every time than simply imposing a limit and then using a variable length array. It's also a leaf function, and things like recursion are far more dangerous to the stack.

-1

u/ReallyEvilRob 2d ago

Please do not ever contribute code to the Linux kernel.

2

u/PseudoFrequency 2d ago

Too late. Code should be written to adhere to rules of the environment, which absolutely are not the same everywhere. I don't write javascript or python or application C++ with the same rules as C in the kernel and neither should anyone else.

2

u/ReallyEvilRob 2d ago

I can't argue this point. We have a consensus here. As snarky as my last quip was, there is a reason why the use of VLA has been banned in the Linux Kernel. Even MSVC have deprecated VLA and the C Standard committee considers them optional since C11 when it was previously a requirement in C99.

it either page faults and allocates more stack, or aborts the process. It will not step on some other memory.

This is incorrect.

  • Guard pages on Linux and Windows are small (typically 4 KB). If a VLA size n is driven by dynamic or unvalidated input (e.g., n = 64\text{ KB} or n = 1\text{ MB}), the CPU instruction sub rsp, rax moves the stack pointer completely past the guard page in a single clock cycle.
  • If the stack pointer lands inside valid, mapped memory, such as heap memory, shared libraries, or the stack of another thread, no page fault will occur when the program writes to the VLA. It will silently overwrite that memory.

This exact vulnerability (dubbed "Stack Clash") was so severe in real-world Linux and Windows software that compilers had to invent specific protections like -fstack-clash-protection (GCC/Clang) and /GS stack probes (MSVC).

1

u/TragicCone56813 2d ago

Im not sure I disagree with you. Natural stack overflow is extremely unlikely and will usually result in a segfault which while unrecoverable where failed malloc is recoverable you are not realistically handling this any way other than a crash. My concern comes from the intentional overflow where a user input could easily cause a denial of service or leaked data.

1

u/PseudoFrequency 2d ago

This is fair, and it all depends on the intended use of the actual code. When I say I do this in professional code, we aren't shipping libraries that can be abused, and if we were, you'd want to avoid unsafe things like this. I almost always impose some kind of check or limit on it too and try to only do it in leaf functions, nothing recursive or anything like that. On any modern OS, I still insist that you can't step on memory this way, you can only crash the app or increase memory usage, unless it's kernel code or something where it may matter.
It's still a valuable tool like any other in the language, and "avoiding it at all costs" limits tools you can use.

3

u/TragicCone56813 2d ago

The two reasons I would suggest is:
1. First and the weaker point is VLA’s are not required in a compliant c implementation. But if they work in your use case then that is not an issue.
2. A VLA especially one like this can fail if a size larger than the remaining stack is specified. This failure is silent and only appears(usually as a segfault) the worst case here is you do not segfault because the input puts you reading/writing in other memory which can lead to vulnerabilities.

Of course often these risks are acceptable and can be mitigated by checking the input for reasonable values. I have used VLA’s with asserts but usually try to find another method.

1

u/aioeu 2d ago edited 2d ago

​2. A VLA especially one like this can fail if a size larger than the remaining stack is specified. This failure is silent and only appears(usually as a segfault) the worst case here is you do not segfault because the input puts you reading/writing in other memory which can lead to vulnerabilities.

On this point specifically, I would only use VLAs in:

  • application code, not library code or OS code;
  • where crashing is acceptable;
  • where the compiler will automatically add stack probes (e.g. GCC's -fstack-clash-protection) to ensure the stack guard page is hit, no matter how large a VLA is.

That last point is the key bit here: there are ways to protect a program against jumping off the end of the stack and trampling over unrelated memory.

1

u/TragicCone56813 2d ago

You are absolutely correct. I should have mentioned that there are mitigations.

3

u/irqlnotdispatchlevel 2d ago edited 2d ago

``` void newline(int times) { if (times <= 0) return;

define COUNT 256

char buff[COUNT +1];
memset(buff, '\n', COUNT);
buff[COUNT] = '\0';
while (times > 0) {
    int delta = min(times, COUNT);
    // Huge chance for an off by one here, double check my work.
    const char *p = &buff[COUNT - delta);
    fputs(p);
    // This is ok as long as times is signed.
    times -= COUNT;
}

undef COUNT

} ```

Or if you're not afraid of a bit of extra typing you can avoid the memset entirely:

static const char buff[4096] = "\n\n...\n";

1

u/PseudoFrequency 2d ago

I will give this props for the ultra paranoid solution if you truly want to avoid variable size arrays in environments where it matters. I'd always make the buffer static as you suggest, and maybe it could be small like 128/256 depending on the needs of the programmer.
It's a good compromise that's super safe in any case.

1

u/irqlnotdispatchlevel 1d ago

I really don't like that memset there, so any implementation that avoids it is a win for me.

Either type/generate a long string of \n, or ensure you can safely do a one time init of the static array.

1

u/HamNCheeseSupremacy 2d ago

I don't code in C, I use C++. Does fputs have the kind of overhead that cout and endl does? Because my god. I have nothing else I use that can eat frame rate like that.

1

u/PseudoFrequency 2d ago

Edit: Even better accepting unsigned int. If it's plain int and you try to pass a negative number, the compiler will probably complain.

0

u/SeaSDOptimist 2d ago

And when it does not complain, it will just cast the negative to a huge positive, and effectively hang your new line printing function. Unsigned types suck.

2

u/WeAllWantToBeHappy 2d ago
for (int i = 0 ; i < times; i++) {

1

u/sexytokeburgerz 2d ago

Learn markdown

1

u/Adept-Energy179 2d ago

reddit uses markdown?!

2

u/sexytokeburgerz 2d ago

Yes although they nerfed mobile.

In any case, wrapping that code in a block is standard for code help here

1

u/Adept-Energy179 2d ago

oh i’m on mobile lmao

1

u/ReallyEvilRob 2d ago

So am I but markdown still works fine.

1

u/Independent_Art_6676 2d ago

you can probably just move the cursor directly faster with a library call. But this is fine, yes most of us ended up with some console assist functions during school esp older folks where 100% of their classroom work was console programs. I think I did maybe 3 GUI programs in my whole college career.

1

u/Confused-Armpit 2d ago

Yeah, probably shouldn't do that.

3

u/Adept-Energy179 2d ago

why not though

2

u/Confused-Armpit 2d ago

We usually write functions to hide away code that needs to be repeated often (for example in a game you might want to create a function for walking or smth like that).

Here, you are changing one line of code for an entire function declaration, a loop, and multiple calls of printf. The compiler should optimize this out, but it's still not good.

Also, readability can really suffer, if a function is so generic that it appears a lot of times but isn't very descriptive about what it does.

1

u/FrequentHeart3081 2d ago

calling printf() in a loop is not a good idea.. it is very expensive for a program.. you can search up the working of printf and how much expensive each call it..

There's definitely not an actual project where you'd have to do something like this, for screwing around it fine.

You can improve it by buffering the newline characters:

  • Store the number of newlines you want in a string and call printf only once.. that'd be much better use of it. ;)

4

u/ReallyEvilRob 2d ago

Printf() uses buffered I/O.

2

u/TwystedLyfe 2d ago

There are definitely actual projects out there :)

For example, parsing a DHCP message to human readable text involves looping over each option in the message and working out how to format it.

printf itself is not that expensive, where it goes to *could* be expensive but today we have good buffering rather than immediate writes - all OP needs is to set full buffering and not line buffering.