You are perhaps better off not knowing, in many cases.
char s[][10]={"good", "morning"}; // at stack?
char *t[] = {"good", "morning"}; // at heap?
The important difference between the two examples is that the
former's strings will be modifiable, while the latter's will not.
This is indeed an important difference, but not necessarily
the only one. In particular, the code fragments above might
be incomplete, so that the above is not the entire translation
unit.
Consider the following (complete-program) translation unit:
#include <stdio.h>
char a[] = "hello";
char *b = "world";
char *e(int x) {
return x ? a : b;
}
char *f(int x) {
char c[] = "cello";
char *d = "whorl";
return x ? c : d;
}
int main(void) {
printf("%s %s\n", e(1), e(0));
printf("%s\n", f(0));
return 0;
}
The behavior and output of this program is well-defined, but if
one were to change the second printf() to call f(1), the program
would have undefined behavior and the output would be unpredictable.
On many implementations, the second line of output will contain
some sort of trash, as in the one I used to run it here:
% ./t
hello world
ÜzÓ
/<invalid character deleted>¬z`
The reason for the undefined behavior is that the string in
the array named "c" has automatic duration, lasting only as
long as the array object itself. The array is (at least in
principle) destroyed when f() returns, and on this implementation,
by the time printf() gets around to using the pointer, the
array is in fact destroyed (hence the u-umlaut and so on).
When a string literal is used to initialize an array object, the
characters in the literal simply initialize that object. The scope
and duration of the object are not affected by the string literal.
On the other hand, when a string literal appears in some other
context, its characters are used to initialize an anonymous array
that is -- at least in principle -- read-only, yet nonetheless has
type "array N of char" -- not "array N of const char", even though
"const" would make sense -- where N is the number of "char"s required
to contain the whole literal plus an additional terminating '\0'
byte. Hence, in the code above, "world" and "whorl" both initialize
anonymous objects of type "array 6 of char". The objects so
initialized have static duration, so they last as long as the
program runs (and possibly beyond then; the C Standards sayeth not)
and no scope (they are anonymous, so the concept of scope does not
really apply).
Thus, to go back to the original examples:
char s[][10]={"good", "morning"}; // at stack?
The Standards do not define "a" (much less "the") stack, although
automatic variables behave in a stack-like manner, and a single
stack, or sometimes two stacks -- one for data and one for control
-- are quite common. But if "s" is outside any function, the
variable s is not going to be on the data-stack in typical
implementations; if it is inside a function, it is. In
any case, the type of "s" will be "array 2 of array 10 of char"
and the memory contents can be described pictorially as:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-...-+
| g | o | o | d |\0 |\0 |\0 |\0 |\0 |\0 | m | o | r | n | ... |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-...-+
The scope and duration of "s" will be the same as that of
any other variable not declared with "static" or "extern",
i.e., depends on whether s is declared within a function.
char *t[] = {"good", "morning"}; // at heap?
In this case, the answer is more complex. Once again, the Standards
do not define the term "heap"; but if we take that word to mean
"the space that malloc() allocates, and free() destroys", the answer
is: no, none of these will be in that space.
Instead, the string "good" will initialize some anonymous array
of type "array 5 of char":
+---+---+---+---+---+
| g | o | o | d |\0 |
+---+---+---+---+---+
This array will live somewhere in memory, possibly in read-only
memory, and will be present and initialized by the time main() is
entered initially; the array will still exist up until the program
exits (and possibly afterward, or perhaps not; as far as the Standard
is concerned, the "program universe" vanishes once the program
exits, and no one cares anymore).
The string "morning" will initialize some other anonymous array,
perhaps somewhere near the one that contains "good\0", perhaps not:
+---+---+---+---+---+---+---+---+
| m | o | r | n | i | n | g |\0 |
+---+---+---+---+---+---+---+---+
This object has type "array 8 of char" and again may (or may not)
be read-only.
Finally, the ordinary variable "t" will be created, with type
"array 2 of pointer to char", initialized so that t[0] points
to the first anonymous object, and t[1] to the second:
+---+---+---+---+---+
_--> | g | o | o | d |\0 |
/ +---+---+---+---+---+
|
|
+-------+-------+
| * | * |
+-------+-------+
|
___________/
/
|
\ +---+---+---+---+---+---+---+---+
`-> | m | o | r | n | i | n | g |\0 |
+---+---+---+---+---+---+---+---+
The duration and scope of the object named "t" will be the same as
that of "s", assuming both are inside (or both outside) a function.
It is possible (but not necessarily the case) that the strings
"good\0" and "morning\0" will actually abut in memory.
If it were important that they did, one could write:
static char strpair[] = "good\0morning";
to initialize a named (non-anonymous) array, give it static duration,
make it read/write (or add "const" to make it read-only), and then
use &strpair[0] instead of "good" and &strpair[5] instead of
"morning". Or one could write, e.g.:
void f(void) {
char *t[2];
t[0] = "good\0morning";
t[1] = t[0] + 5;
... code ...
}
Note that embedded \0 sequences in string literals are copied
through to the objects they initialize. Hence a string literal is
not always the same as the first string within it (because in C,
a "string" is simply a \0-terminated data structure; if the string
literal contains \0 characters, it produces multiple adjacent
strings).