4

Why does int a[5] = {1,2,3,4,5,6} give a warning while int a[5] = {1,2,3,4,5}; a[5] = 6; does not?

Is it a good practice to do this when I initially declared the array size to be 5?

What if I don't know the size of my array? Can I declare it like this int a[]?

djechlin
  • 59,258
  • 35
  • 162
  • 290
mushroom
  • 1,909
  • 3
  • 16
  • 33
  • 1
    Note you "meant" `a[5] = 6;`. – djechlin Apr 26 '13 at 17:00
  • @djechlin thanks, yep i did mean `a[5] = 6;`. Updated. – mushroom Apr 26 '13 at 17:09
  • Can anyone give this a more precise title? – djechlin Apr 26 '13 at 17:10
  • Changed to long but correct and specific title. – djechlin Apr 26 '13 at 17:27
  • @jon - I removed your added question, because that makes this question overly broad and invalidates all existing answers, while inviting new answers to your new question. Try to research that separately first and post it as a new question if you can't find out the answer (it is okay to link to the two question). – djechlin Apr 26 '13 at 17:30
  • @djechlin Thanks for helping me to clean up the question. I'd gladly like to search around, but I really don't know how to translate that to searchable terms. Would you be kind enough to point me somewhere? (I hear all about getting a book and stuff, yes I will, but meanwhile just hoping I could understand this aspect of what I'm learning on my own. Thanks guys for being so helpful! :D ) – mushroom Apr 26 '13 at 17:35
  • (The other question I had was whether `int a[]={1,2,3}` and `a[3]={1,2,3}` are equivalent.) – mushroom Apr 26 '13 at 17:45
  • Searchable terms are "C array initialization." – djechlin Apr 26 '13 at 17:48
  • tomatoes tomatoes. some like the freedom of C, some find it scary. – AndersK Jun 26 '13 at 23:16

4 Answers4

8

Why does int a[5] = {1,2,3,4,5,6} give a warning while int a[5] = {1,2,3,4,5}; a[5] = 6; does not?

The assignment gives you a warning because you know the size of the variable in the initialization statement, and it is obviously violating the size of your declaration. You don't have the size of the array from a in the line a[6] = 6, so for the compiler it seems ok. Of course the level of warnings change from compiler to compiler, and for some compilers you can specify extra warnings.

For example, using gcc, you could use the flags -Wextra and -Wall to get a lot of warnings. Receiving warnings is a good thing, because the compiler helps you to find possible caveats without needing to debug your code. Of course, they are only good if you fix them :-)

Is it a good practice to do this when I initially declared the array size to be 5?

It is never a good practice to assign an integer to a place in memory that you didn't declare - you can't be sure where this value is being written, and it can be overwriting another variable, or worse, partially overwriting some other variable or the stack. Since this kind of stuff is different from compiler to compiler, as @PascalCuoq pointed out, it is called undefined behavior, and is something you want to avoid at all costs. Of course, since it is undefined, it can happen that your program will execute just fine after this declaration, yet it is a very poor practice.

However, there is nothing wrong with initializing an array with fixed size, if it will not change. You should avoid magic numbers and use constants instead, like MAX_NUMBER_OF_PERMUTATIONS or CURRENCIES_SIZE, for instance.

Can I declare it like this: int a[]?

Declaring it as int a[] is a shorthand when you are initializing a fixed array and the compiler can specify the number of elements. For example:

int a[] = {1,2,3}; //this is good
int b[3] = {1,2,3}; //same from above

In the past it was usual declare int a[]; however it don't works in every compiler, so should be avoided. (Thanks @PascalCuoq for pointing this out)

What if I don't know the size of my array?

If you don't know the size of your array, you should declare it as a pointer, like int * a and manage the memory yourself using malloc, realloc, calloc and similar system calls. Please do a good job and learn about free too - the world will thank you later. You should read about pointers instead of arrays if you are looking for dynamic memory allocation.

fotanus
  • 19,618
  • 13
  • 77
  • 111
  • You say “you should declare as you wrote” in answer to “Can I declare it like this int a[]?” but using `malloc()`, … requires a pointer and `int a[];` does **not** declare a pointer. – Pascal Cuoq Apr 26 '13 at 18:46
  • Excellent answer overall. But I think "undefined behavior" (even if technically true) and "poor practice" are putting it mildly. Given `int a[5];` then consciously doing `a[5]` isn't just poor practice. It's **bad practice** and a serious bug. – Nik Bougalis Jun 26 '13 at 23:05
6

Why does int a[5] = {1,2,3,4,5,6} give a warning while int a[5] = {1,2,3,4,5}; a[6] = 6; does not?

Warnings are only the compiler trying to help you. The compiler does not have to warn everytime you do something wrong. There are too many ways to write incorrect programs, and compilers cannot warn for all of them.

Is it a good practice to do this when I initially declared the array size to be 5?

No. When a is an array of size 5, accessing a[6] or a[5] invokes undefined behavior (translation: it is very bad). The traditional saying about undefined behavior is that it allows the compiler to make daemons fly out of your nose:

During a discussion on that group in early 1992, a regular remarked “When the compiler encounters [a given undefined construct] it is legal for it to make demons fly out of your nose” (the implication is that the compiler may choose any arbitrarily bizarre way to interpret the code without violating the ANSI C standard).

So, in short, always avoid undefined behavior in your C programs.

What if I don't know the size of my array? Can I declare it like this int a[]?

No, you can't. You have to know the size of your array when you declare it. int a[]; would declare an incomplete array, which is not what you want (here is a link about incomplete types, but if you are asking about accessing the sixth element of a five-element array, you just don't want to hear about incomplete types yet). If you do not know the size you will eventually need, learn about malloc() and realloc().

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • Thanks for your clear explanation. However, I'm still a bit confused about the `int a[]` part as it kind of contradicts with Neil's answer: "You can declare as array as `int a[]`, but all you are declaring there is a pointer to an array". – mushroom Apr 26 '13 at 17:01
  • 3
    @jon This is exactly why you should get yourself a good book instead of learning C piecewise from answers by strangers on the internet. A late edition of this one should be okay. If you have already read it, I can look for a good second book for you: http://en.wikipedia.org/wiki/The_C_Programming_Language – Pascal Cuoq Apr 26 '13 at 17:04
  • "No. It invokes undefined behavior (translation: it is very bad)." Can you elaborate or give me a link for it? I didn't knew that this is a bad thing. – fotanus Apr 26 '13 at 17:05
  • @fotanus Explanation about undefined behavior and joke reference to nasal daemons added. – Pascal Cuoq Apr 26 '13 at 17:11
  • @PascalCuoq downvoter was me, I can't find anywhere a reference that int a[5] is undefined behavior. For example, [this answer](http://stackoverflow.com/questions/201101/how-to-initialize-an-array-in-c) cites it as a very good thing. If you can give a reference for int a[5] as undefined behavior (or remove it form your answer) I'll upvote you back – fotanus Apr 26 '13 at 17:21
  • @fotanus If you use Linux, type “apt-get install frama-c” (or similar) and you should be able to reproduce the example here: http://pastebin.com/GJMjJYAy . This means the Frama-C analyzer considers `a[5]` as undefined behavior. In this guide, it is under “Dereferences of Wild Pointers and Out of Bounds Array Accesses”: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html – Pascal Cuoq Apr 26 '13 at 17:28
  • @fotanus The undefined behavior is accessing `a[6]` or `a[5]` when `a` is declared of size `5`. I will clarify this in my answer. – Pascal Cuoq Apr 26 '13 at 17:29
  • @PascalCuoq right, I read the question wrong - I thought you was telling that declare an array of fixed size was undefined behavior :-) – fotanus Apr 26 '13 at 17:37
  • @Koushik I carefully tried to say only true things while at the same type avoiding the subject (“… when you declare it”) because like incomplete types, I do not think this is the kind of discussion the question calls for. – Pascal Cuoq Apr 26 '13 at 17:40
  • yes but that is very subtle. yeah you are right about not being appropriate for this discussion though. – Koushik Shetty Apr 26 '13 at 17:45
2

In your second example, the compiler is doing pointer math to figure out where to put the integer. In older C implementations, an array is not a first-class type, so there's no range checking available. Some newer ones can detect out-of-bounds errors.

If you need to dynamically allocate arrays, this is the correct pattern:

int *a;
// calloc( elements, size)
// create an array of 6 elements, 0..5
a = calloc(6, sizeof(int));
// use like a[5]=6, be sure to free(a) later. 

Check the calloc and free references.

fotanus
  • 19,618
  • 13
  • 77
  • 111
PaulProgrammer
  • 16,175
  • 4
  • 39
  • 56
2

The question: Why does

int a[5] = {1,2,3,4,5,6};

give a warning while

 int a[5] = {1,2,3,4,5};
 a[5] = 6;

does not?

That's actually a very good question, and I don't have a really good answer to it.

As for what the C language standard says, the initialization is a constraint violation, which means that a conforming compiler must issue a diagnostic, and may reject the program. (gcc does so with the -pedantic-errors option, which I recommend using).

In the second case, a[5] = 6; has undefined behavior. The language doesn't require a diagnostic, but it certainly permits one, and a sufficiently clever compiler could warn about it. When the compiler sees the assignment to a[5], it knows (or could know) that a has only 5 elements and you're trying to access the 6th element. gcc, at least doesn't do so, at least not with the options I tried.

Why does the standard require a diagnostic for the first case but not for the second? Because the first error can always be detected at compile time. A compiler has to know how big a is, because the declaration is creating it; it has to allocate memory for it. In a declaration like that, the size is always known at compile time. (There are variable-length arrays, but you can't use that kind of initializer for them.)

In a[5] = 6;, the compiler would have to perform more analysis to detect the error, analysis that's not always possible. You could as easily have written:

a[n] = 6;

which would be just as bad if n happens to be 5 -- but the compiler would have to determine at compile time the value that n is going to have at run time to diagnose it. Compilers can sometimes perform that kind of analysis, but there are cases where it's theoretically impossible.

So why doesn't gcc diagnose the particular case of a[5] = 6;? It's probably just a matter of where the developers of gcc chose to devote their time and other resources.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • I like your discussion very much, but I wouldn't go as far as saying it's “theoretically impossible” to warn for out-of-bounds accesses that may happen at run-time. I would only say that “for theoretical reasons, compilers makers must accept to have false positives, or false negatives, or both, even if they have the best will in diagnosing these”. And as you already point out `a[5]` is not one of the difficult cases. – Pascal Cuoq Apr 26 '13 at 17:46
  • @PascalCuoq: If the index value is entered by the user or read from a file, I'd say it's theoretically possible to issue a compile-time warning that it's out of bounds. You can always issue a warning that it *might* be out of bounds, but then you'll likely drown in spurious warnings. Some handwaving mention of the Halting Problem might be appropriate here. – Keith Thompson Apr 26 '13 at 19:05
  • 1
    I wasn't suggesting to cite the Halting problem. “Theoretical reasons” is vague enough to be right, whereas the Halting problem I feel is one amalgam too many. In fact, I have this discussion periodically, so I wrote it up to perhaps re-use in the future. It definitely would not have fit in this comment anyway: http://blog.frama-c.com/index.php?post/2013/04/26/Of-compiler-warnings-discussions – Pascal Cuoq Apr 26 '13 at 21:37
  • @PascalCuoq: Oops! In my previous comment, I meant to write that it's theoretically *impossible*. – Keith Thompson Apr 26 '13 at 22:16