0

It is more than a funny question. :-)

I wish to initialize an array in C, but instead of zeroing out the array with calloc. I want to set all element to one. Is there a single function that does just that?

I have used my question above to search in google, no answer. Hope you can help me out! FYI, I am first year CS student just starting to program in C.

oz123
  • 27,559
  • 27
  • 125
  • 187
donkey
  • 4,285
  • 7
  • 42
  • 68
  • No there is no such thing. Nor is there a single function that set all elements to ttwo or to three etc. – Jabberwocky Mar 25 '14 at 22:39
  • 4
    Maybe 'opposite' isn't what you're looking for. Perhaps: "Is there a variant of calloc() that can initialize the data to a non-zero value?" – Jonathan Leffler Mar 25 '14 at 22:39
  • No. In normal C development it is practically never needed. But you can write one, first with a malloc, then with a for(;;)-loop to fill the allocated memory interval with something. – peterh Mar 25 '14 at 22:39
  • @JonathanLeffler good call. But this does not give me answer on google either. I suppose I am the only one curious about this on StackOverflow then. – donkey Mar 25 '14 at 22:41
  • @PeterHorvath I just found this "standard" way not simple enough. But thanks! – donkey Mar 25 '14 at 22:42
  • 1
    I've been coding in C for around 30 years and AFAICR I have not needed the functionality you're asking about. That's by no means saying you're wrong to need it; it just isn't a commonly required functionality. – Jonathan Leffler Mar 25 '14 at 23:20
  • Thank you for telling me this. @JonathanLeffler Then yet if I ever need it, does it normally mean there must be a simpler, more correct way to do the same? – donkey Mar 25 '14 at 23:44
  • It mainly means that you'll have to look outside the standard C library for the functionality. It isn't hard to provide (see the `set_alloc()` and `set_alloc2()` functions in my answer), which is probably one reason it has not been standardized. When people need it (I seriously doubt you're the first to need it), then they've used their own code, or code found in a non-standard library. – Jonathan Leffler Mar 26 '14 at 00:01

3 Answers3

6

There isn't a standard C memory allocation function that allows you to specify a value other than 0 that the allocated memory is initialized to.

You could easily enough write a cover function to do the job:

void *set_alloc(size_t nbytes, char value)
{
    void *space = malloc(nbytes);
    if (space != 0)
        memset(space, value, nbytes);
    return space;
}

Note that this assumes you want to set each byte to the same value. If you have a more complex initialization requirement, you'll need a more complex function. For example:

void *set_alloc2(size_t nelems, size_t elemsize, void *initializer)
{
    void *space = malloc(nelems * elemsize);
    if (space != 0)
    {
        for (size_t i = 0; i < nelems; i++)
            memmove((char *)space + i * elemsize, initializer, elemsize);
    }
    return space;
}

Example usage:

struct Anonymous
{
    double d;
    int    i;
    short  s;
    char   t[2];
};

struct Anonymous a = { 3.14159, 23, -19, "A" };

struct Anonymous *b = set_alloc2(20, sizeof(struct Anonymous), &a);
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you, but since you answered this way, I can't delete it anymore. I'll accept it instead lol. In 12 mins.. – donkey Mar 25 '14 at 22:40
  • I just tried memset in Xcode. I think memset is dedicated to strings. Therefore, Xcode gave me bad access for doing so. Since my question is to specifically try to set array to numeric value. I can't accept your answer! Still thanks! – donkey Mar 25 '14 at 22:56
  • Hmmm...that code should not trigger a bad access, in Xcode or anywhere else. If you've got the space, the `memset()` should succeed (unless, perhaps, you're on Linux and you run into the OOM Killer). The code is not tied to strings; neither is `memset()`. The `mem*()` functions are intended for arbitrary memory, not null-terminated strings. Are you compiling 32-bit or 64-bit? I'd assume 64-bit. What size of space are you allocating? Under 2 GiB or bigger than that? Have you checked what you're allocating with the limits from `ulimit -a`? – Jonathan Leffler Mar 25 '14 at 23:08
3

memset is there for you:

memset(array, value, length);
Gandaro
  • 3,427
  • 1
  • 17
  • 19
  • 1
    ... but only works if the array is an array of `char`s – user3386109 Mar 25 '14 at 22:44
  • Well, the first argument is a `void*`, so you can also feed it with other arrays. :P Although I don't guarantee you that it will set the elements to the value specified... But it will definitely set it to something other than 0 if `value != 0`! :) Oh, and you have to set *length* to a special value then because it expects it the length to be in bytes and not in "number of elements". Yeah, maybe I could have elaborated more on the usage. :) – Gandaro Mar 25 '14 at 22:46
  • 1
    Yup, it _will_ definitely set it to something other than 0 :) But for example if OP wants an array of `int` all set to `1`, then `memset` just doesn't do it, because memset will set **every** byte to `1`, not every fourth byte, which is what you need to set an `int` to `1` (assuming 32bit ints). – user3386109 Mar 25 '14 at 22:49
  • @Gandaro I am coding in Xcode, and Xcode will throw bad access assert if doing so. Just FYI. – donkey Mar 25 '14 at 22:53
1

There is no such function. You can implement it yourself with a combination of malloc() and either memset() (for character data) or a for loop (for other integer data).

The impetus for the calloc() function's existence (vs. malloc() + memset()) is that it can be a nice performance optimization in some cases. If you're allocating a lot of data, the OS might be able to give you a range of virtual addresses that are already initialized to zero, which saves you the extra cost of manually writing out 0's into that memory range. This can be a large performance gain because you don't need to page all of those pages in until you actually use them.

Under the hood, calloc() might look something like this:

void *calloc(size_t count, size_t size)
{
    // Error checking omitted for expository purposes
    size_t total_size = count * size;
    if (total_size < SOME_THRESHOLD)  // e.g. the OS's page size (typically 4 KB)
    {
        // For small allocations, allocate from normal malloc pool
        void *mem = malloc(total_size);
        memset(mem, 0, total_size);
        return mem;
    }
    else
    {
        // For large allocations, allocate directory from the OS, already zeroed (!)
        return mmap(NULL, total_size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0);
        // Or on Windows, use VirtualAlloc()
    }
}
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589