-1

I was wondering if there's a easy way to define a variable inside an if block and then use it out of that if statement (like you can in Python).

Like that:

    int a = 2;

    if (a <= 1) {
        char string[10] = "foo";
    }
    else {
        char string[10] = "bar";
    }

    printf("%s", string);

Output

bar
puncher
  • 1,570
  • 4
  • 15
  • 38
  • 1
    You can't. Either move your `print` into both branches, or define the variable outside and `strcpy` the value to it inside the `if`. Or use two different strings and let the `if` to select between them (by assigning to a pointer, for example). – Eugene Sh. Mar 23 '21 at 13:23
  • You need `char string[10]; int foo = 2; if (foo == 2) strcpy(string, "Jahr"); else strcpy(string, "Jahren"); printf("%s\n", string);` – pmg Mar 23 '21 at 13:23

4 Answers4

2

You can not declare a variable inside an if statement (or other compound block) and use it outside of it's scope.

If you need the value outside, then you have to declare an appropriate variable and assign it in the if statement.

In your example this would looke like this:

char string[10];

if (a <= 1)
    strcpy(string, "Jahr");
else
    strcpy(string, "Jahren");

printf("%s\n", string);
Zoe
  • 27,060
  • 21
  • 118
  • 148
Devolus
  • 21,661
  • 13
  • 66
  • 113
0

You can do that by declaring the value to use as static and taking out a pointer to that. Marking as static prevents the variables from vanishing on exiting from the scope.

    int a = 2;
    char* pString;

    if (a <= 1) {
        static char string[10] = "Jahr";
        pString = string;
    }
    else {
        static char string[10] = "Jahren";
        pString = string;
    }

    printf("%s", pString);

I think it is better to declare a variable outside the if statement and copying data into that inside the if statement.

    int a = 2;
    char string[10];

    if (a <= 1) {
        strcpy(string, "Jahr");
    }
    else {
        strcpy(string, "Jahren");
    }

    printf("%s", string);

Add #include <string.h> (if it doesn't already exists) at the top of your code to use strcpy().

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
0

The easy solution would be this:

int a = 2;
const char* str;

if (a <= 1) {
    str = "Jahr";
}
else {
    str = "Jahren";
}

puts(str);

Optionally, in case you need to write to the string later:

const char* STR_TABLE[] =
{
  "Jahr",
  "Jahren"
};

int a = 2;
size_t i;
if (a<=1)
{
  i = 0;
}
else
{
  i = 1;
}

size_t size = strlen(STR_TABLE[i]) + 1;

char rwstr [size]; // or just allocate it "large enough", couple of hundred bytes
memcpy(rwstr, STR_TABLE[i], size);

puts(rwstr);

The issue about buffer overruns doesn't exist since there is no user input.

The compiler will even be able to resolve all of this at compile-time. When I run this on gcc x64 with optimizations on, it just tosses some magic numbers corresponding to the ASCII into a buffer, then call puts.

Lundin
  • 195,001
  • 40
  • 254
  • 396
-2

If you declare something inside a scope, it only exists inside that scope and will effectively go away as soon as that block exits.

To fix this declare it one level up, outside that immediate scope:

int a = 2;
const int LENGTH = 10;       // Define the buffer length clearly
char string[LENGTH] = { 0 }; // Zero out buffer to start

if (a <= 1) {
  strncpy(string, "Jahr", LENGTH - 1);
}
else {
  strncpy(string, "Jahren", LENGTH - 1);
}

printf("%s", string);

Note the use of LENGTH here as a constant so you know how big that buffer is. sizeof(string) can work in some contexts, but if it decays to a pointer when making a function call that information is lost, so it will need to be passed in separately. It's just a good habit to get into. Changing that one value will change all places where it's used, versus having to hunt down to search and replace them all.

If you over-commit on this, it's still safe:

const int LENGTH = 10;
char string[LENGTH] = { 0 };

strncpy(string, "my spoon is too big", LENGTH - 1);

printf("%s", string);

Where here you get my spoon as output.

Tip: Use buffer-overflow resistant functions like strncpy() which will avoid creating a mess if you inadvertently overflow something. Using buffers that are super tiny like 10 bytes long is risky, always be generous with these unless you know specifically how they will be used.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • 2
    Oh, I wondered why in one of the questions yesterday in a hot discussion about `strncpy` someone said "ask tadman about it" :) – Eugene Sh. Mar 23 '21 at 13:31
  • @EugeneSh. I think it's about time we put `strcpy()` to bed. It's done enough damage. I've been fighting against SQL injections for years, so used to the angry downvotes. – tadman Mar 23 '21 at 13:31
  • 1
    I saw enough damage with `strncpy` too :) Like `strncpy(dst, src, strlen(src));` :) For my opinion neither of these are good or bad. It is the programmer's understanding of what they are doing. – Eugene Sh. Mar 23 '21 at 13:33
  • @EugeneSh. Any sufficiently powerful tool in the hands of someone sufficiently incompetent is dangerous. `strlen()` vs. `sizeof()` continues to confound beginners, but that's more easily explained than why your product just got popped by an RCE. Anyway, glad for the newfound "fame", I guess! – tadman Mar 23 '21 at 13:34
  • I think the argument against `strncpy` is that even experienced programmers constantly get it wrong and manage to drop the null terminator. Unlike `strcpy`, it is one of those plain dangerous functions that should always be avoided. – Lundin Mar 23 '21 at 14:01
  • I mean simply replace `"Jahren"` with `I LikeBugs` and there you go. Compiles cleanly. [Demo](https://godbolt.org/z/MKz61ahf1) So much for your so-called buffer overflow safety. – Lundin Mar 23 '21 at 14:03
  • @Lundin As much as `strncpy()` can be misused if you're not paying attention, if you pay attention it won't cause problems. `strcpy()` offers **absolutely no control**. I'm against using super tiny buffers like seen here, I'd even recommend `strdup()` if the POSIX library is present, or a `malloc() + strncpy()` combo otherwise. – tadman Mar 23 '21 at 14:05
  • 1
    This is a really old debate. First someone who meant well at Microsoft realized that a whole lot of exploits were caused by `strcpy`. Then they got the misguided idea that `strncpy` was somehow meant to be the safe version, which isn't true. It's a version meant to be used on an ancient, long obsolete string format in Unix which didn't use null terminators. And yet MS starts to brainwash everyone the `strncpy` is now the way to go, giving warnings for use of `strcpy` and other madness. The _correct_ solution however was always to sanitize the buffer before calling `strcpy`. – Lundin Mar 23 '21 at 14:09
  • @Lundin Would [`strncpy_s`](https://en.cppreference.com/w/c/string/byte/strncpy) be a better alternative? The problem is `strcpy()` is not on a leash, it can go and utterly trash memory. I would suggest that Microsoft, having suffered *badly* at the hands of `strcpy()` type bugs in their products, has reasons for being paranoid here. The security track-record of their products improved dramatically from *awful* to *exceptional* inside of a decade. – tadman Mar 23 '21 at 14:09
  • "It's like people losing their minds over being forced to wash their hands before preparing food." No, it's rather that someone got the idea to stick their hands in poop before preparing food because MS told them to. You wrote a bug in this answer plain and simple. It's a subtle bug, it's easy to write, everyone writes them, `strncpy` is to blame. Even more blatant example: https://godbolt.org/z/P9PTeY9xe. An exploit that would be impossible even with `strlen` + `strcpy`. This is caused by `strncpy`, period. – Lundin Mar 23 '21 at 14:11
  • @Lundin You can make `strncpy` fail safe, and you can use `strncpy_s`, but `strcpy` is intrinsically dangerous unless you test very carefully *before* using it. – tadman Mar 23 '21 at 14:13
  • And no, forget about `_s` functions. The correct solution is: if you got the string from user input, then sanitize that input as you go. `fgets` is a good option. Avoiding creating command line junk as if it's still the year 1990 is an even better option - or at least use argv argc if you must. If you did _not_ get the string from user input, then you don't have to worry and you could as well have used `memcpy(string, "Jahren", sizeof("Jahren"));` since that is the fastest version & perfectly safe. – Lundin Mar 23 '21 at 14:15
  • Anyway, had this debate too many times already. See https://stackoverflow.com/a/30884175/584518 for a summary with links for further study. – Lundin Mar 23 '21 at 14:16
  • @tadman I already told you, sanitize the buffer. If it's safe and null terminated, then just use strcpy. Or memcpy if you know the size too. – Lundin Mar 23 '21 at 14:18
  • @lundin If the buffer is zeroed out you can just `strncpy()` one byte shorter than the buffer so you are guaranteed to get a NUL at the end. `strcpy()` will gladly stomp over that and keep going. Worst case with `strncpy()` is you just get truncated. No big deal. – tadman Mar 23 '21 at 14:19
  • Yeah but you didn't call `strncpy` with `LENGTH - 1` in the original code, now did you? And then I don't believe you are a rookie programmer. You were convinced that it was some sort of safe version of `strcpy` where you just needed to pass along the buffer size. Like everyone else still using this blatantly dangerous function. – Lundin Mar 23 '21 at 14:38
  • @Lundin I'm iterating on this based on feedback, or would you prefer I don't? There's surely a better way to educate people here than to tell them to give up. – tadman Mar 23 '21 at 14:40
  • Still you persist on using the function. Oh well, guess I'll post an answer too. – Lundin Mar 23 '21 at 14:47
  • @Lundin That doesn't cover the use case of trying to fit something into a pre-existing buffer. – tadman Mar 23 '21 at 15:11
  • It all boils down to: don't copy stuff that you don't even know how large it is. If you know, then copy away. – Lundin Mar 23 '21 at 15:36
  • @Lundin Are you recommending a `strlen() < N` + `strcpy()` combination? – tadman Mar 23 '21 at 15:49
  • If you know that `strlen` gets passed a valid string then sure. – Lundin Mar 24 '21 at 07:07