views:

311

answers:

6
int main()
{
    int j=97;
    char arr[4]="Abc";
    printf(arr,j);
    getch();
    return 0;
}

this code gives me a stack overflow error why? But if instead of printf(arr,j) we use printf(arr) then it prints Abc. please tell me how printf works , means 1st argument is const char* type so how arr is treated by compiler. sorry! above code is right it doesn't give any error,I write this by mistake. but below code give stack overflow error.

#include <stdio.h>
int main()
    {
       int i, a[i];
       getch();
       return 0;
    }

since variable i take any garbage value so that will be the size of the array so why this code give this error when i use DEV C++ and if I use TURBO C++ 3.0 then error:constant expression required displayed. if size of array can't be variable then when we take size of array through user input.no error is displayed. but why in this case.

+3  A: 

please tell me how printf works

First of all, pass only non-user supplied or validated strings to the first argument of printf()!

printf() accepts a variable number of arguments after the required const char* argument (because printf() is what's called a variadic function). The first const char* argument is where you pass a format string so that printf() knows how to display the rest of your arguments.

If the arr character array contains user-inputted values, then it may cause a segfault if the string happens to contain those formatting placeholders, so the format string should always be a hard-coded constant (or validated) string. Your code sample is simple enough to see that it's really a constant, but it's still good practice to get used to printf("%s", arr) to display strings instead of passing them directly to the first argument (unless you absolutely have to of course).

That being said, you use the formatting placeholders (those that start with %) to format the output. If you want to display:

Abc 97

Then your call to printf() should be:

printf("%s %d", arr, j);

The %s tells printf() that the second argument should be interpreted as a pointer to a null-terminated string. The %d tells printf() that the third argument should be interpreted as a signed decimal.

this code gives me a stack overflow error why?

See AndreyT's answer.

In silico
There are use-cases where passing the format argument as non-constant is useful (e.g., when doing i18n and l10n) but it is never a good idea to pass a *user-supplied* string as the format argument. Indeed, that's been the source of many hacks of programs where the data and code are in different security contexts (e.g., because they are setuid or taking data from the web).
Donal Fellows
Firstly, I don't see any problems with the string being "non-constant" in this case. It is not user supplied anyway. Secondly, neither in the answer addresses the issue of the crash.
AndreyT
@Donal Fellows: Right. I've modified my answer to not be too restrictive. However, all external data should always be validated.
In silico
@AndreyT: The OP asked how `printf()` works, so I answered that part of the question.
In silico
+3  A: 

I see that now the OP changed the description of the behavior to something totally different, so my explanation no longer applies to his code. Nevertheless, the points I made about variadic functions still stand.

This code results in stack invalidation (or something similar) because you failed to declare function printf. printf is a so called variadic function, it takes variable number of arguments. In C language it has [almost] always been mandatory to declare variadic functions before calling them. The practical reason for this requirement is that variadic functions might (and often will) require some special approach for argument passing. It is often called a calling convention. If you forget to declare a variadic function before calling it, a pre-C99 compiler will assume that it is an ordinary non-variadic function and call it as an ordinary function. I.e. it will use a wrong calling convention, which in turn will lead to stack invalidation. This all depends on the implementation: some might even appear to "work" fine, some will crash. But in any case you absolutely have to declare variadic functions before calling them.

In this case you should include <stdio.h> before calling printf. Header file <stdio.h> is a standard header that contains the declaration of printf. You forgot to do it; hence the error (most likely). There's no way to be 100% sure, since it depends on the implementation.

Otherwise, your code is valid. The code is weird, since you are passing j to printf without supplying a format specifier for it, but it is not an error - printf simply ignores extra variadic arguments. Your code should print Abc in any case. Add #include <stdio.h> at the beginning of your code, and it should work fine, assuming it does what you wanted it to do.

Again, this code

#include <stdio.h>

int main()
{
    int j=97;
    char arr[4]="Abc";
    printf(arr,j);
    return 0;
}

is a strange, but perfectly valid C program with a perfectly defined output (adding \n at the end of the output would be a good idea though).

AndreyT
For the explanation of \n: http://stackoverflow.com/questions/2141745/what-to-replace-in-this-c-puzzle/2141969#2141969
Johannes Schaub - litb
You state "variadic functions might (and often will) require some special approach for argument passing" which I believe is incorrect for all versions of **C** that I know. (I'd be happy to learn of some if you have references.) Variadic functions require special _argument handling_ but that's unrelated to the compilation. Interestingly you later correctly state "printf simply ignores extra variadic arguments" which is true due to the lack of format specifiers (no '%').
Carter Galle
@Carter Galle: That's strange. Virtually *any* modern C compiler will use a completely different calling convention for variadic functions vs. ordinary functions. For ordinary functions most compilers today use various "fast" calling conventions: as many parameters as possible are passed through CPU/FPU registers (instead of stack) and the stack cleanup is performed by the *callee*. For variadic functions this is obviously impossible, so for variadic functions the "old-fashioned" passing approach is usually used: everything is passed on the stack and the cleanup is done by the caller.
AndreyT
So, variadic functions *do* require special argument handling and it *is* related to compilation. In fact, that is exactly the reason why ANSI C89/90 explicitly requires such functions to be declared before they are called. (There's no such requirement for ordinary functions in C89/90.) Your remark about "versions of C" is not exactly clear to me. This has nothing to do with nay "versions of C", but everything to do with the detail of various implementations. What I said above is absolutely true for such compilers as GCC and MSVC on x86 platform, for one example.
AndreyT
@AndreyT: considering the vast majority of real-world non-embedded systems are i386 (32-bit x86), I think your claim is grossly incorrect. The i386 ABI does not use any fancy pass-by-register tricks, and in fact due to register starvation they would be largely useless. As far as the C language goes, implementations are of course allowed to use different calling conventions and valid C code must use correct prototypes for variadic functions, but in the world of x86 it's irrelevant.
R..
Slight correction: gcc (and perhaps msvc?) *do* use pass-by-register tricks, but only for `static` functions whose addresses are never evaluated or whose addresses can never be obtained by external code, and which are thus exempt from any ABI requirements. They may also have *options* to enable pass-by-register for external functions, but this is completely nonstandard and enabling such options will preclude linking to any code which follows the standard ABI.
R..
@R..: That's great, but it is beside the point. Firstly, we don't know what implementation the OP is using, but there are implementations out there that will not force performance-damaging calling conventions (like `cdecl` imposed by ABI) without being explicitly asked to. Secondly, when the language standard says that the behavior is undefined, the behavior is undefined, regardless of what any other irrelevant standards might say. Again, with the information available I see no other explanation for the behavior observed by the OP.
AndreyT
@R..: Thirdly, whether the issue of calling conventions is standard or not is completely irrelevant. What's relevant, is that `printf` has to be declared before it is called. The requirement has been put there specifically because the compiler has to know at compile time that we're calling a variadic function.
AndreyT
For the same reasons a pointer of type `int (*)()` ("any parameters"), can't be legally used to call a variadic function (although it can be legally used to call any ordinary `int`-returning function). Again, the compiler always has to know at compile time that we are calling a variadic function. That's the rationale behind the special treatment of variadic functions in ANSI C. That's the point I'm making. The points Carter Galle made above became outdated somewhere in the middle of the 80's.
AndreyT
A mere suggestion that in 2010, when modern compilers are capable of performing such thing as full-program optimizations (for one example) they would still stick to such a performance-destroying calling convention as `cdecl` strikes me as laughable.
AndreyT
Standard x86 calling convention ("cdecl" as you call it) is not "performance-destroying" or even sub-optimal on x86. I challenge you to find a single real-world function where you can measure the difference between passing arguments on the stack versus in (the tiny number of) registers. You are correct from a C language perspective that declaring the variadic function is required to avoid UB, but mixing in religious tirades and factually incorrect information about the x86 implementation is not the way to teach people to follow the standard.
R..
@R..: Firstly, the information that I attributed as applying to x86 is absolutely precise and factually correct. Secondly, the performance impact of `cdecl` convention is noticeable in *every* computational application I ever worked with. No exceptions. Thirdly, the number of available registers in modern CPUs (x86 again, for example) is already sufficient to cover many (if not most) functions with a reasonable number of parameters. Which is why the performance benefit of fast calling conventions is so *huge* and so immediately and easily measurable.
AndreyT
Finally, calling such obvious practical considerations as properties of variadic functions (compared to ordinary functions) "religious tirades" is something that fits somewhere between total lack of expertise and plain trolling.
AndreyT
Are you confusing x86 and x86_64, perhaps?
R..
A: 

-edit- oh sorry. I left after reading the question and when i came back all i could focus on was the 'syntax' error which actually does compile. FYI op, on VS 2010 your code does not stackoverflow and appears to work correctly.

"Abc" is a char*, char* make char angry. Char arrays are especially angry when programmer ask the array (char arr[4]) to become (=) a char*. Char arrays prefer if you ask it to copy char* contents (use strcpy)

acidzombie24
Sorry, but this just doesn't make any sense in the context of the original question.
AndreyT
also you can write = char{'A','b'...} but i rather strcpy on the line beneath var declaration.
acidzombie24
@acidzombie24, the tone of this answer is great and could've been brilliant (had it been right). Don't take the downvotes to mean "get serious" but rather "make sure you're right"...
Yar
@Yar: ha. Yeah. I think i was more excited about answering this way then remember the question wasnt a syntax error (i guess i was thinking isnt that a syntax error when i left and was excited when i came back lol) -edit- I left the answer up because i laugh everytime i read it
acidzombie24
@Yar and everyone else. What have i done?!?! http://stackoverflow.com/questions/3847061/c-why-is-arrayptr-legal (see the comments... the comments!)
acidzombie24
A: 

I suspect the printf() issue is a red herring, since with a null-terminated "Abc" will ignore other arguments.

Have you debugged your program? If not can you be sure the fault isn't in getch()?
I cannot duplicate your issue but then I commented out the getch() for simplicity.

BTW, why did you not use fgetc() or getchar()? Are you intending to use curses in a larger program?

===== Added after your edit =====

Okay, not a red herring, just a mistake by the OP.

C++ does allow allocating an array with the size specified by a variable; you've essentially done this with random (garbage) size and overflowed the stack, as you deduced. When you compile with C++ you are typically no longer compiling C and the rules change (depending on the particular compiler).

That said, I don't understand your question - you need to be a lot more clear with "when we take size of array through user input" ...

Carter Galle
printf ignores the extra arguments, but the compiler does not. the stack will be in a state printf doesn't expect, and if the calling convention doesn't handle that gracefully, who knows what will happen.
TokenMacGuy
Actually "the compiler does not" isn't strictly true, since it depends on the compiler, although I don't know of any compilers that will actually generate different code. I'm pretty sure that those which handle it at all _at most_ will generate *compile time* warnings or errors.
Carter Galle
The latest version of C (C99) does allow variable-length arrays. You are correct about the cause of the stack overflow error, though (the indeterminate value of `i`).
Matthew Flaschen
A: 

You see strings in C language are treated as char* and printf function can print a string directly. For printing strings using this function you should use such code:

printf("%s", arr);

%s tells the function that the first variable will be char*.

If you want to print both arr and j you should define the format first:

printf("%s%d", arr, j);

%d tells the function that the second variable will be int

MOLi
+1  A: 

In your line int i, a[i]; in the corrected sample of broken code, a is a variable-length array of i elements, but i is uninitialized. Thus your program has undefined behavior.

R..