0

If i'm writing a program in C that i suspect might take a lot of memory, can i use the malloc function. Does malloc know how much memory my program wants? I know it returns a value of the memory it found but how does it know if its enough?

E.g

int * buffer;
buffer = (int*)malloc(sizeof(int)*70);

if(buffer==NULL){
    fprintf(stderr, "malloc error!");
}
  • How does the question title and the contents match? Do you have a particular problem with `malloc` in your program? What is it? Or are you asking a principal question on how `malloc` works in general? Then better read a book google for it there are plenty of ressources out there. – Jens Gustedt Feb 07 '12 at 15:12
  • 2
    In C, never typecast the result of malloc. Read [this](http://stackoverflow.com/questions/1565496/specifically-whats-dangerous-about-casting-the-result-of-malloc). – Lundin Feb 07 '12 at 15:25

7 Answers7

2

Does malloc know how much memory my program wants?

You specify the amount of memory as malloc parameter, so that it knows how much memory to allocate

In your example it will allocate sizeof(int)*70 bytes (on 32bit Windows 4*70=280 bytes, for example)

I know it returns a value of the memory it found but how does it know if its enough?

It looks at your parameter and checks if OS has enough memory for you, if memory is not enough it returns NULL.

Timofey
  • 2,478
  • 3
  • 37
  • 53
  • 1
    (Note that the OS can outright lie - overcommit. There's nothing you can do (portably) inside the app about that though.) – Mat Feb 07 '12 at 14:19
  • @Mat may you give an example, where OS *can outright lie*? Do you want to say that I may receive memory even if there is no enough or may not receive it when it is enough? – Timofey Feb 07 '12 at 14:23
  • If the OS is set with a loose "overcommit" mode, you can request more than the available memory (including swap), and the OS will let you do so (i.e. malloc will return a valid pointer) - but your process (or something else) will get killed if it actually _uses_ more memory than is really available. This is OS-specific. Tunable with `/proc/sys/vm/overcommit_memory` on Linux for example. – Mat Feb 07 '12 at 14:32
1

Yes, if there isn't enough memory, malloc returns NULL.

Phonon
  • 12,549
  • 13
  • 64
  • 114
1

malloc takes a parameter that defines the amount of memory required - and in your example it is enough memory to hold an array of 70 integers.

If it is unable to do so, it returns null.

undur_gongor
  • 15,657
  • 5
  • 63
  • 75
Ed Heal
  • 59,252
  • 17
  • 87
  • 127
1

The parameter you give to malloc is the number of bytes you want for this particular piece of memory.

If you want a char array to store 4 chars, you have to do

char *tab;
tab = malloc(sizeof(*tab) * 4);

This will take the size of *tab which is a char, times 4, and allocate this space in memory.
You have to know yourself how much is enough for your program to work.

Eregrith
  • 4,263
  • 18
  • 39
  • Or if you for some crazy reason want the code to be readable, you could just write `malloc(sizeof(char)*4);` – Lundin Feb 07 '12 at 15:30
  • 1
    Or if for some reason you want to be sure never to put the wrong type in `sizeof`, even if you change it, you can keep `sizeof(*tab)` ? A sizeof inside a malloc does not need to have the type hardcoded to be readable... Here you clearly read "I want tab to point to an array of 4", who cares if it's `char` or `struct bmp_header` during malloc ? – Eregrith Feb 07 '12 at 15:33
  • Technically, accessing the contents of an uninitialized pointer is undefined behavior, but you probably get away with it on most compilers because sizeof() is evaluated at compile time. Strictly speaking, you allow the compiler to make your program go haywire with that line. Also, how often do you find yourself writing code for any kind of variable without actually knowing what type that variable actually is...? – Lundin Feb 08 '12 at 08:23
  • You are not dereferencing here because, __yes__, sizeof() is evaluated at compile time and not just on "most" compilers. You are not doing this because you don't know the type, you are doing this because it is more readable, and if you __change__ the type of tab, you won't have to peer through lines of code to change it everywhere and try not to forget anything. Because your compiler won't tell you if you do `tab = malloc(sizeof(char*) * 4);` when tab is in fact a `int*`. – Eregrith Feb 08 '12 at 08:51
  • I meant: you probably get away with the undefined behavior in your code since most C compilers have been kind to not make your program crash & burn when accessing the contents of an uninitialized pointer. Most compilers have been kind to not crash & burn your program solely because sizeof() is always evaluated at compile time. The C compiler is however free to do _anything_ when encountering that line of code. I'm just saying it is probably a bad idea to rely on the level of kindness of compiler vendors to determine how robust your code turns out. – Lundin Feb 08 '12 at 09:06
  • Can you point me to some kind of specifications about what compilers must and can do based on, say, a C BNF or something like that, where I could find what is safe to do with every compliant compiler, please ? – Eregrith Feb 08 '12 at 09:12
  • ISO/IEC 9899:2011 _Programming languages - C_. 6.5.3.2 Address and indirection operators. §4: `"If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined."` – Lundin Feb 08 '12 at 09:34
  • For a complete list of all undefined behavior, see Annex J.2 of the standard. – Lundin Feb 08 '12 at 09:37
  • 1
    The last [C11 draft standard](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) is free of charge, and likely identical to the real standard in 99.99% of the cases. – Lundin Feb 08 '12 at 10:33
  • Well I found this : §6.5.3.4, part 6 : `EXAMPLE 1 A principal use of the sizeof operator is in communication with routines such as storage allocators and I/O systems. A storage-allocation function might accept a size (in bytes) of an object to allocate and return a pointer to void. For example: extern void *alloc(size_t); double *dp = alloc(sizeof *dp);` And this : §7.17.7.4 : `NOTE 1 For example, the effect of atomic_compare_exchange_strong is : if (memcmp(object, expected, sizeof (*object)) == 0) memcpy(object, &desired, sizeof (*object)); else memcpy(expected, object, sizeof (*object));` – Eregrith Feb 08 '12 at 12:39
  • Is this enough to think that compliance with sizeof(*dp) and sizeof(*object) is mandatory ? – Eregrith Feb 08 '12 at 12:40
  • You aren't citing any normative parts of the standard. However, I found this further up: 6.5.3.4/2 `"..., the operand is not evaluated and the result is an integer constant."`. So indeed it appears that the * operator on an uninitialized pointer is safe to use together with sizeof, but only there. – Lundin Feb 08 '12 at 13:02
  • Yes of course, I won't advise using *xxx when xxx is not initialised anywhere else, quite the contrary ! Thanks for the link – Eregrith Feb 08 '12 at 13:05
0

You always have to tell malloc how much memory you require. It never knows by itself.

Rob
  • 14,746
  • 28
  • 47
  • 65
0

No, malloc does not know how much memory your program wants, you're the one who should know that, and you're telling malloc how much memory you need for that particular buffer.

However, this buffer is not the total memory allocated to your program, only the memory allocated to this particular variable. If there isn't enough memory in your system, malloc will return null.

Bottom line, there's no way for you to tell the operating system that your program will need a lot of memory, you need to allocate the memory for each buffer you need and then check whether it returned NULL to see whether there's enough memory in the system.

Zoltán
  • 21,321
  • 14
  • 93
  • 134
0

As others have said, you have to tell malloc how much memory you want. Note that if you don't allocate enough memory initially, you can use realloc to request more memory.

Here's an example. Suppose you want to store a line of text (delimited by a newline) from an input stream, but you don't know how long the line is, so you don't know ahead of time how much memory to allocate. You can read the input stream piecemeal into a smaller fixed-size buffer, and append that to a dynamic buffer that you can resize as necessary:

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

/**
 * Reads a line of text (delimited by a newline character) from an 
 * input stream into a dynamic buffer. 
 */
char *getNextLine(FILE *stream, size_t *lineSize)
{
  char inbuf[SIZE];  // fixed-size input buffer
  char *line = NULL; // points to our dynamic buffer
  char *newline = NULL;
  char *(*append)(char *, const char *) = strcpy;

  *lineSize = 0;

  /**
   * Read from the input stream until we see a newline character
   * or the read fails (EOF or error).
   */
  while (!newline && fgets(inbuf, sizeof inbuf, stream))
  {
    /**
     * Resize the buffer to accomodate the string in the input buffer
     * (allocates the buffer the first time through).
     */
    char *tmp = realloc(line, *lineSize + strlen(inbuf) + 1);
    if (tmp)
    {
      /**
       * Check for a newline in the input buffer
       */
      newline = strchr(inbuf, '\n');

      /**
       * If present, overwrite the newline with a 0 (nul terminator
       * character).  If you want to keep the newline in the target
       * buffer, skip this step.
       */
      if (newline)
        *newline = 0;

      /**
       * Assign the temporary variable back to line and update the 
       * output buffer size.
       */
      line = tmp;
      *lineSize += strlen(inbuf) + 1;

      /**
       * Write the contents of the input buffer to the target buffer.
       * First time through the loop we'll use strcpy (called through
       * the append function pointer); on each successive iteration
       * we'll use strcat to append the contents of the input buffer
       * to the target buffer.
       */
      append(line, inbuf);
      append = strcat;
    }
    else
    {
      /**
       * The realloc function failed; at this point, we'll just return
       * what we have.
       */
      printf("Unable to extend buffer\n");
    }
  }

  if (!newline && !feof(stream))
  {
    printf("Error on read!\n");
  }

  return line;
}

This code uses realloc instead of malloc for the initial allocation (calling realloc with NULL as the first argument is the same as calling malloc).

Note that we assign the result of realloc to a temporary variable. If realloc fails (there isn't enough memory to satisfy the request) it will return NULL; if we assigned this to line we'd lose our pointer to any memory we've already allocated, causing a memory leak.

There's a little trickery with the append function pointer. The first time through the loop we want to copy our input buffer to the target buffer, so we set append to point to strcpy. After that, we want to append what's in the input buffer to the contents of the target buffer, so we set append to point to strcat.

Note that it's up to the caller to free the dynamic buffer when it's done with it.

Community
  • 1
  • 1
John Bode
  • 119,563
  • 19
  • 122
  • 198