2

This is my code (it is and must be pure C):

unsigned long buffSize = 65536; /* 64 KB */
char *block;

block = calloc(1, buffSize);
if (block == NULL)
{
    /* This is always triggered */
}

I want a 64 KB of zeroed memory that I will use in a loop somewhere else in my application.

Unfortunatly calloc always return NULL. When I use it with 64 (bytes) it works.

I'm not sure how to allocate "bigger" memory blocks like in my example?

Edit: After reading comments, here are some clarifications:

  1. malloc and memset have the same behavior with the same buffSize.
  2. sizeof(size_t) == 2 while sizeof(unsigned long) == 4 so it's 16-bit.
  3. The target platform is MS-DOS 6.22.

So under a 16 bits system how can you create a char * of 64 KB zero-filled?

AlexV
  • 22,658
  • 18
  • 85
  • 122
  • This is most likely an OS/buildtree specific response, so...OS Build Tree? – IdeaHat Dec 10 '14 at 16:38
  • 5
    does malloc and memset work? – mch Dec 10 '14 at 16:38
  • 3
    Crazy idea, `calloc(buffSize,1)`. Also try `unsigned long buffSize = 65536L;'. I have no idea what the limits are on your platform. – Persixty Dec 10 '14 at 16:45
  • 1
    64KB does not strike me as all that large. I'll ask the obvious question, how much memory does your system have available? May I suggest that you try defining buffSize as hving type `size_t` or even plain old `int`. If that doesn't work it might be interesting to repeatedly reduce the buff size by half and see if there is a size that does work. – Brad S. Dec 10 '14 at 16:45
  • 1
    Is this on a 16bit OS? Eg. MS/DOS? – Hartmut Holzgraefe Dec 10 '14 at 16:56
  • 2
    `unsigned long` must be at least 32 bits, but `size_t` could conceivably be 16 bits. What is `sizeof (size_t)` on your system? Does `malloc(buffSize)` return a null pointer? Try a range of values between `64` and `65536`; is that a particular size where it starts failing? – Keith Thompson Dec 10 '14 at 17:03
  • 1
    If you are on a 16 bit system with a non compliant compiler, then 65536 may overflow to 0. – Marian Dec 10 '14 at 17:08
  • Edited the question to clarify some points. – AlexV Dec 10 '14 at 17:40
  • 3
    Look for `farmalloc` or similar function. (OMG farmalloc, how many moons have passed, didn't expect to meet you again...) – n. m. could be an AI Dec 10 '14 at 17:49
  • MS-DOS... Isn't the memory model `tiny` by chance? – user58697 Dec 10 '14 at 18:01
  • You can try to do something like `calloc(256, 256)`. – Jens Gustedt Dec 10 '14 at 18:19
  • Excuse me for a c++ quote of a much newer standard, but "[std::size_t](http://en.cppreference.com/w/cpp/types/size_t) can store the maximum size of a theoretically possible object..."; your `size_t` cannot hold 65536, so it will definitely not work. – anatolyg Dec 10 '14 at 18:26
  • Must be a Windows thing. I can `calloc(1000, 1000000);` (a gigabyte), no problem. – Lee Daniel Crocker Dec 10 '14 at 18:54

2 Answers2

5

You've said that sizeof (size_t) == 2 (this is for MS-DOS 6.22).

That means that the maximum value of size_t is 65535.

unsigned long buffSize = 65536; /* 64 KB */

No problem so far. unsigned long must be at least 32 bits (and is 32 bits on your system), so it can easily hold the value 65536.

char *block;

block = calloc(1, buffSize);

Both arguments to calloc are of type size_t. Any argument that's not of type size_t is implicitly converted. Converting 65536 to size_t yields 0. So you're requesting an allocation of 0 bytes. The behavior of such an allocation is implementation-defined; it can return a null pointer, or it can return a unique non-null pointer, similar to malloc(1) except that you can't necessarily dereference it. Your implementation apparently does the former.

calloc takes two arguments, both of type size_t. Their values are not simply multiplied to yield a result of type size_t. Instead, calloc must either successfully allocate the number of bytes specified or fail to do so and return a null pointer.

In principle, you can write:

block = calloc(64, 1024);

or something similar. If it succeeds, it will allocate a 65536-byte object.

But since size_t is only 16 bits, that almost certainly means that the implementation cannot create objects bigger than at most 65535 bytes. There's no actual prohibition against creating objects bigger than SIZE_MAX bytes, but any implementation that's able to do so would almost certainly make its size_t bigger, so that it can represent the size of any object.

(SIZE_MAX is the maximum value of the type size_t. It's a macro defined in <stdint.h>, which was introduced by the C99 standard. Your implementation probably doesn't support <stdint.h>. The expression ((size_t)-1) is equivalent.)

You're probably out of luck, unless you use a different implementation (which might mean using the same compiler with different options).

Perhaps you can redesign your program so it can use, for example, two 32768-byte objects rather than a single 65536-byte object (though that might not be supported either).

MS-DOS systems support (supported?) multiple memory models, some of which might permit you to do what you want. I don't know the details. Consult your compiler's documentation for more information.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • I tried `block = calloc(64, 1024);` but it seems the maximum I can do is `60, 1024`... Maybe it's the compiler or the system, I don't know. – AlexV Dec 11 '14 at 14:44
  • @AlexV: As I said, if `size_t` is 16 bits, then it's very unlikely that your implementation can support an object bigger than 65535 bytes (and the limit could be smaller than that). I'm afraid that's probably the best answer you're going to get -- unless you're able to invoke your compiler in a mode that permits larger objects (look for "memory models"). – Keith Thompson Dec 11 '14 at 18:43
1

For 16 bit real mode, you may need to specify "huge" model, which uses far pointers to support arrays > 65536 in size. It's possible that compact or large model might work, using a single fixed segment value and variable offset to handle a buffer size of 65536. I don't know what tool set you are using, but there should be a command line parameter to specify the memory model the program will be using. For Microsoft 16 bit real mode compilers the parameter is /AH for huge, /AC for compact, and /AL for large.

Wiki link: Intel Memory Models

rcgldr
  • 27,407
  • 3
  • 36
  • 61