0

I am a new C-Language learner and faced 2 problems. (Written in the same page because I think it's the same root cause for both)

int mrr [2][3];

for (int r=0;r<2;r++)
{
    for (int c=0; c<3; c++)
        printf("[%d],[%d]:%d\n",r,c,mrr[r][c]);
}

I read that in case we don't provide a value to a newly created array it gets a default value of 0 inside all of its cells. But when I print the result it showed:

[0],[0]:0
[0],[1]:4201072
[0],[2]:6422224
[1],[0]:6422280
[1],[1]:6422476
[1],[2]:1977208000

Second, the following code returns a totally unexpected value (The Average) of 431374336.000000

double getAverage(int arr[], int size) {

   int i;
   double avg;
   double sum = 0;

   for (i = 0; i < size; ++i) {
      sum += arr[i];
   }
   avg = sum / size;

   return avg;
}

double balance[5] = {1000, 2, 7, 17, 50};
double avg = getAverage(balance, 5 );
printf( "Average value is: %f ", avg );
Defender
  • 17
  • 4
  • 1
    Only static variables get initialized to zero. Since your code is not complete, it's impossible to tell, but your `mrr` variable might be "auto", in which case it is not initialized. – Lee Daniel Crocker Jun 25 '19 at 19:37
  • Possible duplicate of [What is the behavior of integer division?](https://stackoverflow.com/questions/3602827/what-is-the-behavior-of-integer-division) – PM 77-1 Jun 25 '19 at 19:37
  • Find where you read that arrays are initialized to zero. See if it includes conditions on the claim. If the claim is indeed a blanket statement that arrays are always initialized to zero, then note the author and ignore everything they say about C. (Or, at least be extremely wary of any information they provide.) – William Pursell Jun 25 '19 at 19:45
  • 1
    @PM77-1: Where do you see an integer division? – Eric Postpischil Jun 25 '19 at 19:59
  • *Second, the following code returns a totally unexpected value (The Average) of 431374336.000000* Please post **all** of your code required to duplicate that behavior. It's not apparent what could cause that from the code you posted. (Also - don't ask two questions in one.) – Andrew Henle Jun 25 '19 at 20:04

4 Answers4

1

Objects defined inside functions without static or _Thread_local are not automatically initialized and have indeterminate values. To ensure the array mrr is initialized, define it with int mrr[2][3] = { 0 };, which will initialize it to all zeros.

The code in the second question is incomplete, but, presumably, the array is defined in the calling function and is not initialized, so its values are indeterminate.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
0

By default, [the values of] an array defined inside a function (i.e. a local array) is indeterminate. Global (i.e. static) variables, on the other hand, are initialized to 0 by default. See this related post: initial value of int array in C

To address your issue, simply initialize your array manually, with something like

for (int r=0;r<2;r++)
{
    for (int c=0; c<3; c++) {
        mrr[r][c] = 0;
        printf("[%d],[%d]:%d\n",r,c,mrr[r][c]);
    }
}
b-frid
  • 116
  • 8
  • Objects defined (not merely declared) inside functions that are not static or thread local have *indeterminate* values; they are not *undefined*. These are technical terms defined by the C standard and should be used correctly. – Eric Postpischil Jun 25 '19 at 19:47
  • When an object has an indeterminate value, that does not mean it has whatever value was last written to the memory it happens to occupy. When a value is *indeterminate*, that does not just mean that its initial value is undetermined but that each use of it is independently indeterminate—a C implementation might use a value from memory at one moment and a value from a register at another. – Eric Postpischil Jun 25 '19 at 19:47
  • Every definition is a declaration, but not every declaration is a definition. As the page you link to notes, `extern int a;` declares but does not define `a`. – Eric Postpischil Jun 25 '19 at 20:10
  • (Comment Deleted/Reposted, as I botched the last edit and ran out of time). Duly noted, and answer edited. However, concerning your first point... in the C language a variable [as opposed to a function] 'declared' is the same in effect as a variable 'defined', at least where the 'extern' keyword is not used, according to this source: [link](https://www.geeksforgeeks.org/difference-between-definition-and-declaration/) – b-frid Jun 25 '19 at 20:16
  • @EricPostpischil have things changed in the standard since C99? I always thought trying to read an uninitialised variable before it is set was undefined. – teppic Jun 25 '19 at 20:29
  • @teppic: It is complicated. The value is indeterminate. So it could be a trap value, so reading it can result in undefined behavior. But some implementations do not have trap values for some types, so reading them cannot cause undefined behavior due to a trap. And character types are special. But there is also a special rule that reading the value of an uninitialized object that “could have been” declared `register` has undefined behavior. I do not recall which C standard that first appeared in. Arrays cannot be `register`, so it does not apply to them. – Eric Postpischil Jun 25 '19 at 21:20
  • @EricPostpischil Thanks for the response. After asking I had a look in the most reliable book I know on C (C Programming: A Modern Approach) and the author's unusually vague on it, just saying the results are unpredictable and may cause a crash. – teppic Jun 25 '19 at 21:23
  • @EricPostpischil I can't understand why this behaviour occurs in c language while it doesn't happen in c# or any other language? what's the difference if it was declared inside a function or not? – Defender Jun 25 '19 at 22:32
  • @Defender : "why this behaviour occurs in c language while it doesn't happen in c# or any other language" : because those are other languages; the designers of other languages made different choices about how such things should be interpreted by a compiler ... perhaps purposefully compensating for this aspect of C, since it can lead to this kind of pitfall for new developers – landru27 Jun 25 '19 at 23:27
  • @Defender: One of the goals of C was to be a lightweight language. Objects defined outside functions have static storage duration, a consequence of which is that they can be initialized largely as part of loading the program. In contrast, objects defined inside functions (that are not static or thread local) have automatic storage duration, a consequence of which is they would have to be initialized each time execution enters their block. That is a run-time burden that can be discarded by not automatically initializing them. – Eric Postpischil Jun 26 '19 at 02:33
  • @b-frid But my array is not defined inside a function, it's inside the main – Defender Jun 26 '19 at 17:58
  • @Defender main is also a function (albeit a special one, in the sense that it's the program's "launch point", so to speak). – b-frid Jun 26 '19 at 18:26
0

Your question about uninitialized variables is well-covered by the comments and other answers; this answer is for your second question, about the unexpected output from getAverage().

You define balance as an array of double, but getAverage() is expecting an array of int. When I change:

double getAverage(int arr[], int size) { ... }

to:

double getAverage(double arr[], int size) { ... }

I get the expected result: 215.2. The implicit type-conversion [1] in your original code is causing the unexpected behavior.

Also, I don't know what compiler you are using, but when I compiled your original code [2], gcc threw this warning:

warning: incompatible pointer types passing 'double [5]' to parameter of type 'int *'

... which is a dead give-away that trouble is brewing. If your compiler doesn't generate this warning, or if you have warnings suppressed, or if they are logged to a file or something that you aren't looking at, I recommend making them visible again; paying attention to compiler warnings has saved me untold hours of grief.

[1] "type conversion" isn't maybe the best term here; type conversion is generally benign, even though there are some gotchas; what's happening here is that the bytes that make up the double value are being read as an int, and the byte structure of those two data types is very different; that's why the output of your original code makes it seem like it's dealing with such crazy-large numbers : e.g., the bytes used to represent "17" as a double when read as an int become a very, very different number. Worse, stepping through the bytes that make up an array of double values as if it were an array of int values, it probably isn't even looking at the array elements at the proper element boundaries. In short, the type disparity is causing havoc.

[2] By "original code" I mean your code with enough overhead code to make it compile, like include pragmas and a main() body; I agree in general with the other comments about providing a complete, minimal example.

landru27
  • 1,654
  • 12
  • 20
  • Thanks, But still I have some questions left. If it's expecting an array of type int and i sent double why this would change the result in 360 degrees. I still didn't get an answer to that. BTW, I am using GCC with eclipse on windows, it just showed small-yellow alert nothing more. – Defender Jun 25 '19 at 23:42
  • @Defender : I'll be happy to clarify; I'm going to post a second answer so that I can show you some code that illustrates what's going on with the type disparity; I'll post that supplemental answer shortly – landru27 Jun 26 '19 at 00:24
  • @Defender : in addition to posting my supplemental answer just now, I encourage you to pay careful attention to those small yellow alerts -- they are warnings about something that is not prohibited by C syntax, but will likely lead to unexpected and/or unintended results – landru27 Jun 26 '19 at 00:57
0

This supplemental answer is in response to the OP's comment on my earlier answer. Defender asks:

"I have some questions left. If it's expecting an array of type int and i sent double why this would change the result in 360 degrees. I still didn't get an answer to that."

It'll be easier to illustrate this with a type clash between short int and int, but the same idea applies to int vs. double.

Take a look at this code:

#include <stdlib.h>
#include <stdio.h>

void functionA(short int[], int size);

int main(void) {

    printf("sizeof short int : %lu\n", sizeof(short int));
    printf("sizeof int       : %lu\n", sizeof(int));
    printf("\n\n");

    printf("====  sending short int to functionA() ====\n");
    short int shortdata[4] = {10, 20, 50, 100};
    functionA(shortdata, 4);

    printf("====  sending int to functionA() ====\n");
    int intdata[4] = {10, 20, 50, 100};
    functionA(intdata, 4);
}

void functionA(short int arr[], int size) {

   int i;

   char* ptr;

   for (i = 0; i < size; ++i) {
      ptr = &arr[i];
      printf("bytes of 'arr[%d]' : %x %x\n", i, *ptr, *(ptr+1));

      printf("value of 'arr[%d]' : %d\n", i, arr[i]);

      printf("\n");
   }
}

which produces this output:

sizeof short int : 2
sizeof int       : 4


====  sending short int to functionA() ====
bytes of 'arr[0]' : a 0
value of 'arr[0]' : 10

bytes of 'arr[1]' : 14 0
value of 'arr[1]' : 20

bytes of 'arr[2]' : 32 0
value of 'arr[2]' : 50

bytes of 'arr[3]' : 64 0
value of 'arr[3]' : 100

====  sending int to functionA() ====
bytes of 'arr[0]' : a 0
value of 'arr[0]' : 10

bytes of 'arr[1]' : 0 0
value of 'arr[1]' : 0

bytes of 'arr[2]' : 14 0
value of 'arr[2]' : 20

bytes of 'arr[3]' : 0 0
value of 'arr[3]' : 0

The first two lines of output show that on my machine, short int takes 2 bytes of memory and int takes 4 bytes.

functionA() is expecting a short int array, and when I send it a short int[], we see the expected output. The bytes that make up the first element of the array are 0x0a 0x00, which in decimal is "10"; the bytes that make up the second element of the array are 0x14 0x00, which in decimal is "20"; and so on.

But when I send functionA() an int[], I'm sending 4 bytes per element, so when it iterates through the array, it doesn't see the elements correctly. It does okay with the first element, but only because it's a small number; when it looks for the second element, it is actually looking at the last two bytes of the first element, so it sees 0x00 0x00, or "0"; when it looks for the third element it is looking at the first two bytes of the second element, and sees 0x14 0x00, or "20"; and so on.

Another way to show it is that the bytes of shortdata are this:

0a 00 14 00 32 00 64 00

and the bytes of intdata are this:

0a 00 00 00 14 00 00 00 32 00 00 00 64 00 00 00

Because functionA() is expecting a short int[], it treats intdata that way -- two bytes per element -- and sees as its elements:

arr[0] : 0a 00
arr[1] : 00 00
arr[2] : 14 00
arr[3] : 00 00

It's a similar story with your code. Your getAverage() is expecting a int[], so when you send it a double[], it is not seeing the bytes the way you intend.

(The clash between int and double is even more drastic than between short int and int; this is because in addition to being different sizes (on most modern machines, int is 4 bytes and double is 8 bytes), floating-point numbers are stored as an exponent and a mantissa -- so the byte values have an entirely different meaning.)

I hope this explanation helps, and I encourage you to experiment with the code above, and read about the internals of C memory as it pertains to allocating and handling variables. As a new C programmer, you will gain insight in to fundamentals that will help you for as long as you continue programming in C.

landru27
  • 1,654
  • 12
  • 20
  • Thanks for this perfect explanation, just a small question as some mentioned I need to give default values for my array manually but should I do the same for variables like int and char? I mean would they get default values inside of functions or not? – Defender Jun 27 '19 at 12:44
  • @Defender : it makes for explicit, readable code to initialize variables with a default value, regardless of a language's rules on the matter; explicit, readable code is easier to work with -- both for yourself and also on a team -- and eliminates the ambiguity; it's not ambiguous to a true expert in a given language, but until you are an expert it will help you, and you won't always be working with true experts; and, like me, you might end up working in several languages, where the rules about this vary; make a habit of initializing variables -- it mitigates many types of problems – landru27 Jun 27 '19 at 13:09
  • Sure I will take this advice, but this didn't answer my question will this behavior only affect int arrays or chars and any other variable deceleration inside a function as well? – Defender Jun 27 '19 at 13:32
  • @Defender : I answered the way I did because in my experience a language's rules on this are less important than creating readable code; but the direct answer is that there is no difference between arrays and non-arrays when it comes to C's automatic initialization rules; the advice from others about arrays applies to non-arrays, too; notice, e.g., that Eric Postpischil's answer is about "objects" -- not just array objects – landru27 Jun 27 '19 at 13:41