C: defining multidimensional arrays in a switch - c

I'm translating code from Maple to C in order to optimize performance. In order to save time, I've hard coded a 2-dimensional array for the 3 cases that I need to run asap. Later I'll add functions that generate this array so that I can run any case.
Here's how I tried to define the array schur: (here N and dim are pre-determined ints, and numPar is an int as well).
// load Schur functions
switch (N) {
case 3:
numPar = 3;
int schur[numPar][dim] = {
{1,0,0,0},
{0,1,1,0},
{0,0,0,1},
};
break;
case 4:
numPar = 5;
int schur[numPar][dim] = {
{1,0,0,0,0,0,0,0},
{0,1,1,0,1,0,0,0},
{0,0,1,0,0,1,0,0},
{0,0,0,1,0,1,1,0},
{0,0,0,0,0,0,0,1},
};
break;
case 5:
numPar = 7;
int schur[numPar][dim] = {
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0},
{0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0},
{0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0},
{0,0,0,0,0,1,1,0,0,0,1,1,0,1,0,0},
{0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
};
break;
default:
}
Clearly this will not work. However, I'm at a loss as to how to rewrite it so that it does work. One idea is to flatten the array, but that will obfuscate my code rather badly later on. Suggestions are greatly appreciated.

You can allocate the multidimensional array to be as large as the largest case. Based on the switch case you can only fill it to the size you need, and then only access it to the size you filled.
So for example for the 3 by 4 array:
int staticArray[3][4] = {
{1,0,0,0},
{0,1,1,0},
{0,0,0,1},
};
for (int i = 0; i<3; ++i) {
for (int j = 0; j<4; ++j) {
schur[i][j] = staticArray[i][j];
}
}

Since you're concerned about space, and since your larger arrays appear to be mostly zeros with relatively few ones, you might want to consider a "sparse array" solution. Access speed would be much slower, but the amount of memory used might be much less.
Websearching on that phrase will find implementations; which one would be best depends on how you intend to use these arrays.

switch (N) {
case 3:
numPar = 3;
int tmp1[3][dim] = {
{1,0,0,0},
{0,1,1,0},
{0,0,0,1},
};//then copy this rray to thry you want
break;
case 4:
numPar = 5;
int tmp2[5][dim] = {
{1,0,0,0,0,0,0,0},
{0,1,1,0,1,0,0,0},
{0,0,1,0,0,1,0,0},
{0,0,0,1,0,1,1,0},
{0,0,0,0,0,0,0,1},
};//then copy this rray to thry you want
break;
case 5:
numPar = 7;
int tmp3[7][dim] = {
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0},
{0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0},
{0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0},
{0,0,0,0,0,1,1,0,0,0,1,1,0,1,0,0},
{0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
};//then copy this rray to thry you want
break;
default:
}

First, note the hopefully obvious problem that you can't use variables when declaring an array in C, only constants. For example, your first declaration could work like this:
int schur[][] = {
{1,0,0,0},
{0,1,1,0},
{0,0,0,1}
};
and the compiler will happily figure out just how much space to allocate ... if, of course, you also weren't trying to declare the same variable multiple times in your switch statement. :-)
The second thing to keep in mind is that the construct:
int myArray[][] = { {1, 0, ... }, { 0, 1, ... }, ... };
declares an array of pointers to arrays of integers. In that example, schur is an array of 3 pointers, each of which points to an array of 4 integers.
This being C, there is of course a number of different ways to accomplish what you're trying to do. (Steve's Law of Computing: "If there exists one way to do something, there exists an infinite number of ways to do the same thing.")
What comes to mind first for the three cases you show above is to declare the 3 arrays you need, then just return the appropriate one from the switch statement:
int schur3[][] = {
{1,0,0,0},
{0,1,1,0},
{0,0,0,1}
};
int schur4[][] = {
{1,0,0,0,0,0,0,0},
{0,1,1,0,1,0,0,0},
{0,0,1,0,0,1,0,0},
{0,0,0,1,0,1,1,0},
{0,0,0,0,0,0,0,1}
};
int schur5[][] = {
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0},
{0,0,1,0,1,1,0,0,0,1,1,0,0,0,0,0},
{0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0},
{0,0,0,0,0,1,1,0,0,0,1,1,0,1,0,0},
{0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}
};
/* Note that what you get is a pointer to an array of pointers! */
int * * getSchurArray(int N)
{
switch (N)
{
case 3:
return (schur3);
case 4:
return (schur4);
case 5:
return (schur5);
}
}
(Caveat: No, I didn't run that through a compiler yet, so I won't guarantee there are no typos!)
Now, if you want to make this dynamic, and you really want to stick with C, you're going to have to use malloc(), which is how you do dynamic arrays in C. In your case, you need to do something along the lines of:
int * * createSchurArray(int numPar, int dim)
{
/* malloc() requires number of bytes, which is number of entries */
/* times the size of each entry. */
int * * answer = malloc(numPar * sizeof(int *));
for (int rowIndex = 0; rowIndex < numPar; rowIndex++)
{
answer[rowIndex] = malloc(dim * sizeof(int));
for (int colIndex = 0; colIndex < dim; colIndex++)
{
answer[rowIndex][colIndex] = schurValue(numPar, dim, rowIndex, colIndex);
}
}
}
where implementation of:
int schurValue(int numPar, int dim, int rowIndex, int colIndex)
is left as an exercise for someone who understands what you're trying to do with Schur functions. :-)
(Oh, wait - did I break an "only one smiley per answer" rule?)

Related

I have a question about sparse matrix multiplication code in C

I'm learning sparse matrix in "Fundamentals of Data Structures in C" by Horowitz.
And my problem is about sparse matrix multiplication! I do know how it works and algorithm, but I can't understand the code.
Below is the code about "mmult"
It is the part about so called "boundary condition" that makes me confused with this code. I don't understand why this condition is needed. Isn't it just fine without these terms?? I need some help understand this part...
The book says "...these dummy terms serve as sentinels that enable us to obtain an elegant algorithm.."
typedef struct {
int row;
int col;
int value;
} SM; // type SM is "Sparse Matrix"
void mmult(SM* A, SM* B, SM*C) {
int i, j;
int rowsA, colsB, totalA, totalB, totalC;
int rowbegin, A_Row, B_Col, sum;
SM* newB;
rowsA = A[0].row, colsB = B[0].col;
totalA = A[0].value, totalB = B[0].value;
totalC = 0;
if (A[0].col != B[0].row) {
fprintf(stderr, "can't multiply\n");
}
transpose(B, newB) // newB is a transposed matrix from B
/* set boundary condition */
A[totalA+1].row = rowsA;
newB[totalB+1].row = colsB;
newB[totalB+1].col = -1;
rowbegin = 1;
for (i = 1, A_Row = A[1].row, sum = 0; i <= totalA;) {
B_Col = newB[0].row;
for (j = 1; j <= totalB + 1) { // don't know why it should be iterated by totalB+1
/* current multiplying row != A[i].row */
if (A[i].row != A_Row) {
storesum(C, A_Row, B_Col, &totalC, &sum);
for(;newB[j].row == B_Col;j++);
i = rowbegin; // reset i to rowbegin, which is the first row term of current multiplying row;
}
/* current multiplying column != newB[j].col */
else if (newB[j].row != B_Col) {
storesum(C, A_Row, B_Col, &totalC, &sum);
B_Col = newB[j].row;
i = rowbegin;
}
/* Otherwise, during multiplication.. */
else {
switch(compare(A[i].col, newB[j].row)) {
case -1 :
i++;
break;
case 0 :
sum += (A[i].value * newB[j].value);
i++, j++;
break;
case 1 : j++;
}
}
}
for(;A[i].row == A_Row;) i++;
A_Row = row[i].row;
rowbegin = i;
}
}
void storesum(SM* C, int row, int col, int* totalC, int* sum) {
/* storesum is to store to C and set sum to 0 when multiplying current row or column is over */
if(*sum) {
(*totalC)++;
C[totalC].row = row;
C[totalC].col = col;
C[totalC].value = *sum;
*sum = 0;
}
}
It is the part about so called "boundary condition" that makes me
confused with this code. I don't understand why this condition is
needed. Isn't it just fine without these terms??
The matrix multiplication could be computed without including the extra entry, yes, but the function given would not do it correctly, no.
The book says "...these dummy terms serve as sentinels that enable us to obtain an elegant algorithm.."
That's a little vague, I agree. Consider how the algorithm works: for each row of matrix A, it must scan each column of matrix B (== row of matrix newB) to compute one element of the product matrix. But the chosen sparse-matrix representation does not record how many elements there are in each row or column, so the only way to know when you've processed the last element for a given column is to look at the next one in linear element order, and see that it belongs to a new column.
The given code integrates the check for end of column and the storage of the resulting element into the processing for the next element, but that leaves a problem: what do you do about the last element in the matrix's element list? It has no following element with to trigger recording of an element of the result matrix -- at least, not a natural one. That could be solved with some special-case logic, but it is tidier to just add a synthetic extra element that definitely belongs to a different column, so that the end of the matrix no longer constitutes a special case.
I'm not sure I agree that the term "sentinel" is a good fit for this. It's just the opposite of a sentinel in many ways, as a matter of fact. The term normally means a special value that cannot be a part of ordinary data, and therefore can be recognized as an end-of-data marker. String terminators are an example. This "sentinel", on the other hand, works by mimicing real data. It is, nevertheless, an extra, artificial element at the end of the list, and in this sense it's not crazy to call it a sentinel.

How to create array of random strings in C?

I tried creating an array of ten random strings that would print directions randomly. Such as first time "up down right ... rot_x" and second time "forward rot_y up ... down" etc. I tried using a char* pc and allocating memory for it with memset but that didn't work so I tried the following code but I'm getting weird output. How can I fix this?
int main()
{
int r_num;
char r_arr[10][10];
for (int i = 0; i < 10; i++)
{
r_num = rand() % 10;
switch(r_num)
{
case 0: strcpy(r_arr[0], "up");
break;
case 1: strcpy(r_arr[1], "down");
break;
case 2: strcpy(r_arr[2], "left");
break;
case 3: strcpy(r_arr[3], "right");
break;
case 4: strcpy(r_arr[4], "rot_x");
break;
case 5: strcpy(r_arr[5], "rot_y");
break;
case 6: strcpy(r_arr[6], "rot_z");
break;
case 7: strcpy(r_arr[7], "forward");
break;
case 8: strcpy(r_arr[8], "back");
break;
case 9: strcpy(r_arr[9], "reset");
break;
default:
fprintf(stderr, "Cannot process input/n");
}
}
for (int i = 0; i < 10; i++)
{
printf("%s ", r_arr[i]);
}
return 0;
}
here's my output:
up ?V? left right rot_x ?V? forward back reset
A few problems with your code are:
You aren't seeding rand(), so every run of your program will generate identical output. You need to use srand() first with a seed. Traditionally one uses time().
Secondly despite the randomness you are unrandomly (is that a word?) filling r_arr. "up" will always be first, "down" will always be second etc.... Instead you should do something like
for (int = 0; i< 10; i++) {
r_num = rand() % 10;
strcpy(r_arr[i], getDirection(r_num));
}
where getDirection() will return a string based on an integer input (e.g.: via a case statement that associates 0 with "up").
Your r_arr needs to be initialized. There is no guarantee in your current code that each entry in the array will be populated with chars before being accessed. If you implement suggestion 2 then you wont have a problem. Today however your code is accessing potentially uninitialized memory.
As noted by others above, your issue is that you're not indexing your array with the iteration number of your loop. You had:
case 0: strcpy(r_arr[0], "up");
Whereas you should have had:
case 0: strcpy(r_arr[i], "up");
The additional thing that I wanted to point out is that rand() uses a linear equation (at least on many systems) so it will be impossible for you to ever get two even numbers in a row or two odd numbers in a row, which is not very random. Hence I suggest something like:
r_num = (rand() >> 8) % 10;
As the commenters pointed out, you are randomizing not what value you put in each position but which positions get filled with their preset value. Also, your use of a switch statement here is just odd. Try something like:
char value_arr[10][10]={"up", "down", "left", "right", "rot_x", "rot_y", "rot_z", "forward", "back", "reset"}
for (int i = 0; i < 10; i++)
{
r_num = rand() % 10;
strcpy(r_arr[i], value_arr[r_num]);
}
Print the strings inside the switch instead of the for-loop at the end.
Maybe you'll also need something like:
srand (time(NULL));
here is a code that fits exactly to your need :
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// we use this instruction to get a random result each time we run the program
srand(time(NULL));
int r_num;
char r_arr[10][10];
// the dictionary array will be used to take from it the possible strings
char dictionary[10][10]={"up","down","left","right","rot_x","rot_x","rot_x","forward","back","reset"};
int i=0;
for(i=0; i<10; i++)
{
// r_num will be assigned randomly an index from dictionary tab
// the general equation is (rand()%(max-min+1))+min to get a random value
// between max and min inclusive
r_num=(rand()%(9-0+1))+0;
// we will put the random string chosen in the array r_num each time
strcpy(r_arr[i],dictionary[r_num]);
}
// this loop will print the result
for(i=0; i<10; i++)
{
printf("r_arr[%d]=%s \n",i,r_arr[i]);
}
return 0;
}
by looking at your output i noticed some strange values like ?v?,
the problem is that not all numbers between 0 and 9 will be generated by
the rand() function which mean that the corresponding array element(to those numbers) will not be initialized and therefor it contain garbage values from what ever was stored in that memory address.
i hope that explain why you are getting those strange values.

Optimal method for creating a 2D Array without any repetitions

I'm trying to create some code to fish out records from a list of about 200k to 1million records. Obviously, I would want this process to be as fast as possible. The basic idea is as follows, the records in the large list are a combination of numbers which are to be kept together. For Example:
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400076,400097,800076,800097
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200032,200078,500032,500078
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300043,300083,600043,600083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600026,600077,900026,900077
0,0,0,0,0,0,0,0,0,0,0,0,0,0,100008,100028,400028,400056,600008,600056
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400042,400098,500042,500098
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,86,500015,500086
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400013,400076,800013,800076
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,700024,700083,900024,900083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100003,100047,800003,800047
The maximum length of the record is 20 which is why the additional zeroes. Let's not worry about these for a moment. So, I want to "fish" out some records such that no repetitions are observed. If one repetition is there, I can discard that record and no longer look at it further. Thus, I must compile a list which looks like this:
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400076,400097,800076,800097
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200032,200078,500032,500078
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300043,300083,600043,600083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600026,600077,900026,900077
0,0,0,0,0,0,0,0,0,0,0,0,0,0,100008,100028,400028,400056,600008,600056
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400042,400098,500042,500098
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,86,500015,500086
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,700024,700083,900024,900083
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100003,100047,800003,800047
Note how in the above list, record no. 8 is missing because the number 400076 already exists in a previous record.
The code I am using to do this is as follows:
void Make_List(ConfigList *pathgroups, ConfigList *configlist)
{
int i,j,k,l,flag,pg_num=0,len,p_num=0;
for(i = 0;i<configlist->num_total;i++)
{
flag = 0;
for(j = configlist->configsize-1;j>=0;j--)
{
if(configlist->pathid[i][j])
{
for(k = 0;k<pg_num;k++)
{
for(l = pathgroups->configsize-1;l>=0;l--)
{
if(pathgroups->pathid[k][l])
{
if(configlist->pathid[i][j]==pathgroups->pathid[k][l])
{
flag++;
break;
}
}
else
{
break;
}
}
if(flag)
{
break;
}
}
}
else
{
break;
}
if(flag)
{
break;
}
}
if(!flag)
{
len = 0;
for(j = configlist->configsize-1;j>=0;j--)
{
pathgroups->pathid[pg_num][j]=configlist->pathid[i][j];
if(configlist->pathid[i][j])
{
len++;
}
}
pg_num++;
p_num+=len;
if(p_num>=totpaths)
{
break;
}
}
}
Print_ConfigList(stderr,pathgroups);
}
The structure ConfigList basically stores the 2D array along with other things used in different parts of the program.
num_total tells us the number of rows in the array whereas configsize tells us the number of columns in the array.
totpaths is a breakpoint which terminates the loop early in case assignment is completely finished.
Checking if each element is repeated for each new element analyzed has a computational cost of O(N^2) which, given your large input set, is far too much.
Basically, what you need is a fast access data-structure where you can keep a count of how many times your record has appeared or at least a boolean flag.
The easiest way to do this is to have an array where the position represent each possible value and the array value the count of times the position value has appeared (or its boolean value of existence). However, if your data range is too large you can do this because the memory used to store the array is proportional to the range size.
The alternative to avoid that is to use Hash tables or sets.
As you has established in your comments above, your integer range is [0,99999999] so if you wanted to use a vector to keep track of the presence or not of each single value you would need approximately 96 MB to store it in memory.
This is an example using byte arrays:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_IN_RANGE 99999999
int main()
{
char * isInInput = (char*)malloc(MAX_IN_RANGE+1);
memset(isInInput,0,MAX_IN_RANGE+1);
size_t i;
int inputExample[] = {1,3,5,2,1,5};
for(i = 0; i < 6; i++)
{
int value = inputExample[i];
printf("%d\n",value);
if(!isInInput[value])
{
printf("Add value %d to your collection\n", value);
isInInput[value] = 1;
}
else
{
printf("%d is repeated\n", value);
}
}
free(isInInput);
}
To use hash tables instead you can rely on libraries such as Judy in order to avoid implementing your own hash table.

Passing 2d arrays and then not getting a value

I am having an issue with some code that I am writing.
I use this site often as I have found many people who have already asked the same questions I am wondering. With that I want to thank the community on here for all of the previous insight into my programming conundrums.
(And before we get too far, no this is not a 'school project' or 'school homework', I am simply trying to solve the 'Travelling Salesman Problem' and better my C skills.
This is the portion of code I have been stuck on:
void printAndFlip(int arrayone[][20], int citytotal, int arrayCities[])
{
////Finds cost:
int x, y, z;
int totalCost
int singleTrip;
int cheepestTrip;
int nCity = citytotal + 1; //nCity is the number of Cities //Adding one to accomadate going back to the first city
int gCounter;
int gCounterTrue = 1;
int cheepestTrip[20];
int totalCost = 0;
int lCounter;
int i;
int n = citytotal;
////Sets up for a default case to set cheepestTrip:
for(gCounter = 1; gCounter <= nCity; gCounter++)
{
while(gCounterTrue == 1)
{
if(gCounter == arrayCities[gCounter])
{
gCounterTrue = 1;
}
else
{
gCounterTrue = 0;
gCounter = 50; //Stopping the larger for loop with 50 (the nCity can't be larger than 20) so that it will hopefully be faster
}
if(gCounter == nCity)
{
if(arrayCities[1] == arrayCities[nCity])
{
!!!!! cheepestTrip = totalCost;
}
}
}
}
for(x = 1; x < nCity; x++)
{
y = arrayCities[x];
z = arrayCities[x+1];
singleTrip = arrayone[y][z]; //finding individual cost of each trip...will be added to 'totalCost' below
totalCost = singleTrip + totalCost;
}
!!!!!!!! if(totalCost <= cheepestTrip)
{
for(lCounter = 1; lCounter <= nCity; lCounter++)
{
cheepestTrip[lCounter] = arrayCities[lCounter];
}
}
To make it easier to show where my compile errors are at I put exclamation points on the lines.
Please tell me if I am wrong, but I am passing an array of pointers with an array when I send 'arrayone' to printANDFlip right?
I know the compile errors are relating to the pointers but I am just uncertain of where they should be placed.
Any and all help will be appreciated.
Much thanks,
Alex
To make explicit what some of the other replies are saying: You have two variables with the same name but different types:
int cheepestTrip; /* This is an single integer... */
and
int cheepestTrip[20]; /* ...but this is an array of integers. */
This should be triggering a warning at compile time (probably something about redeclaring an existing variable).
Here you are comparing an array pointer with a int value
if(totalCost <= cheepestTrip)
For example you should compare it to an element of that array
if(totalCost <= cheepestTrip[0])
cheepestTrip is the name of the array, which is equivalent to a pointer to the first element. totalCost is an int. Just remove the [20] from your declaration at the top part of the code.
you are comparing a pointer to an int, which your particular compiler might not be allowing (though I though with C you could). but cheapestTrip is essentially a pointer to the first element in your array of ints, while totalcost is simply an int primative

Iterate through int array of unknown length

I am trying to iterate through an array that will contain up to a maximum of 4 elements - no other knowledge of the array-length exists.
Pseudo Code
void insert_vals(uint8_t num, uint8_t *match_num, uint8_t *value)
{
uint8_t i;
while(data_exists) // how do I determine if data exists in 'value'?
{
switch(num)
{
case 0:
{
switch(match_num[i])
{
case 0:
hw0reg0 = value[i];
case 1:
hw0reg1 = value[i];
case 2:
hw0reg2 = value[i];
case 3:
hw0reg3 = value[i];
}
}
case 1:
{
switch(match_num[i])
{
case 0:
hw1reg0 = value[i];
case 1:
hw1reg1 = value[i];
case 2:
hw1reg2 = value[i];
case 3:
hw1reg3 = value[i];
}
}
// etc. 2 other cases
}
i++;
}
}
Calling Example (Pseudo Code)
/*
* num: hardware device select from 1 - 4
* match_num: 4 possible matches for each hardware device
* value: 32-bit values to be assigned to 4 possible matches
* NOTE: This function assumes hardware devices are selected
* in a consecutive order; I will change this later.
*/
// example calling code - we could have configured 4 hardware devices
insert_vals(0, [0, 1], [0x00000001, 0x000000FF]); // arg2 and arg3 equal in length
How can I accomplish this?
In a character array, C will automatically add '\0' to the end of the array, but this does not seem to be the case for an integer array. If I was somehow able to determine the length of match_num and value (see if statement) at runtime originally, then that would allow me to create a for loop.
Edit
Since I know that there will be a maximum of 4 elements, couldn't I do something similar to the following?
void insert_vals(uint8_t num, uint8_t *match_num, uint32_t *value)
{
int i;
for(i = 0; i < 4; i++)
{
if(value[i] == -1)
break;
else
{
// Assign data
}
}
}
You can't get the length of an array pointed to given only the pointer. Either you have to pass the length, or it must be constant (always 4) with some sentinel value in the unused elements -- a value that is somehow invalid for your computations (like NUL is for strings).
Is there a value you can guarantee it's not in the "usable" data? (e.g. 0 is no valid character for character strings, therefore Mr. Kernighan and Mr. Ritchie decided to pick it as a "end of array" marker. You could do the same with any value.
Say you know your integer values are between 0 to 512, so you could initialize the whole array e.g. to 1024, then fill it and iterate through it until a number >512 occurs (which has to be your end of array marker).
Another possibility is to pass the number of elements in the array along with the array.

Resources