16

I'm trying to create a int and a float array without a size (it might be 0 or it might increment while the user use the program).

I was trying to do the follow:

int bills[];

float totalAmount[];

I can't assign a max size because I'm printing each array with a for loop (If I assign a size of 99 I'll print 99 lines, and I don't want that).

Ryan M
  • 18,333
  • 31
  • 67
  • 74
  • 1
    You need to dynamically allocate memory if you do not know how much storage you will need. Look into `malloc()` and `free()` documentation. – Tanveer Badar Apr 29 '18 at 05:45
  • 2
    You simply can't do that in C, there is however many alternative solution but I don't think we can help you more that a good book of C. As a beginner simple solution have a look to [`malloc()` and `realloc()`](http://man7.org/linux/man-pages/man3/malloc.3.html) – Stargateur Apr 29 '18 at 05:45
  • If the max size is known (and smallish) you could use `int bills[99];` and then keep track of the number of used items separately. You *will* have to keep track of the number of bills anyway. – Bo Persson Apr 29 '18 at 11:53

4 Answers4

29

C does not support arrays with a dynamic number of elements. The number of elements of an array must be determined either at compile time or since C99 can be evaluated at runtime at the point of creation. Once the array is created, its size is fixed and cannot be changed. There are a few cases where the size is not explicitly specified between the [], either in array definitions or in array declarations.

You can define an array without an explicit size for the leftmost dimension if you provide an initializer. The compiler will infer the size from the initializer:

int a[] = { 1, 2, 3 };              // equivalent to int a[3] = { 1, 2, 3 };
int m[][2] = {{ 1, 2 }, { 3, 4 }};  // equivalent to int m[2][2] = {{ 1, 2 }, { 3, 4 }};
char s[] = "Hello world\n";         // equivalent to char s[13] = "Hello world\n";

Note how the compiler adds the implicit null terminator in the string case.

You can declare an array without a size specifier for the leftmost dimension in multiples cases:

  • as a global variable with extern class storage (the array is defined elsewhere),
  • as a function parameter: int main(int argc, char *argv[]). In this case the size specified for the leftmost dimension is ignored anyway.
  • as the last member of a struct with more than one named member. This is a C99 extension called a flexible array.

The compiler has no information on the actual size of these arrays. The programmer will use some other information to determine the length, either from a separate variable or from the array contents.

In the case of a function argument, the array is passed as a pointer and even if the number of elements is specified, sizeof(argv) evaluates to the size of a pointer.

6equj5
  • 91
  • 1
  • 12
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Do you know if the cases when you can declare an array without a size specifier for the leftmost dimension are the same in C++ (obviously "`struct`" would probably need to be changed to "`struct` or `class`" )? I tried the function parameter case; it seems like the syntax is valid but the variable becomes a pointer (according to `typeid(the_variable).name`). – HelloGoodbye Oct 18 '20 at 20:11
  • Also, it could be mentioned that C99 flexible arrays are only allowed in `struct`s with at least two named members. – HelloGoodbye Oct 18 '20 at 20:34
  • @HelloGoodbye: good point about more than one named member... Answer amended. – chqrlie Jan 19 '21 at 01:49
  • @HelloGoodbye: arrays are always passed as a pointer to their first element. – chqrlie Jan 19 '21 at 01:50
  • "arrays are always passed as a pointer to their first element" – in C but not in C++ in that case, right? I believe an array with a size specifier in C++ is its own datatype. Or do you mean that the datatype is changed to a pointer when you use it as a function parameter? – HelloGoodbye Jan 20 '21 at 02:07
10

You don't declare an array without a size, instead you declare a pointer to a number of records.

so, if you wanted to do

int bills[];

The proper way to do this in C is

int* bills;

And you will have to allocate the size at some point in time and initialize the array.

bills = (int*)malloc(sizeof(int)*items);

The same goes for arrays of other data types. If you don't know the size of the array until runtime, you should use pointers to memory that are allocated to the correct size at runtime.

mike65535
  • 483
  • 2
  • 10
  • 21
Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • 7
    [Do I cast the return of malloc ?](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858) – Stargateur Apr 29 '18 at 06:16
  • @EdwinBuck I didn't well understand your comment, I'm not a native english speaker. Sorry if you feel like I personal attack you. I think any argument about "old compiler could" is no sense we are in 2018. I think you under estimated the problem if you cast to a wrongly type of pointer. I think any modern C code should use `bills = malloc(sizeof *bills * items);` and that old programmer should stop their old bad habit. Modern C want to live without old C. It's the experience of 30 years of C programmer that conclude that it was better to write it like that. – Stargateur Apr 30 '18 at 13:06
6

You could use a combination of malloc() (or calloc()), realloc() and free() to achieve this.

Memory can be allocated as blocks of a fixed size rather than reallocating memory for each number to be stored.

Let's define a macro (or a const if you like) BLOCK_SIZE.

#define BLOCK_SIZE 10

First declare a pointer of appropriate type and allocate the first block.

Note that malloc() as well as realloc() return NULL if some error occurred due to reasons like insufficient memory.

int *ptr=malloc(sizeof(int)*BLOCK_SIZE);    
if(ptr==NULL)
{
    perror("some error");
    return 1;
}

Now declare a variable to store the maximum possible index as per the currently allocated memory (to avoid illegal memory access).

int max_index = BLOCK_SIZE-1;

Now use a loop.

for(int i=0; ; ++i)
{
    if(i > max_index)
    {
        ptr=realloc(ptr, (max_index+1 + BLOCK_SIZE)*sizeof(int));
        if(ptr == NULL)
        {
            perror("insufficient memory!");
            break;
        }
        printf("\nRealloced!");
        max_index += BLOCK_SIZE;
    }
    scanf("%d", &ptr[i]);
    printf("\n%d: %d", i, ptr[i]);
}

In each iteration, we check if i is greater than max_index. If it is, another block is allocated using realloc() before reading the value.

Don't forget to deallocate the memory once you are done using it.

free(ptr);

Also, as discussed in this post, malloc() is effectively the same as realloc() with the latter's first argument NULL.

And in the code you posted, there's no need to explicitly cast the return value of calloc() as what's returned is a void pointer which would implicitly be converted to the target pointer type.

See this and this.

J...S
  • 5,079
  • 1
  • 20
  • 35
0

i think you can give it a max size ,if you only want to show the first few elements you can put a a for loop upto that element only,same goes for input if u want to initiallize first 30 elements put a for loop upto 30.

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 08 '22 at 08:29