imll
imll

Reputation: 341

Allocate memory for buffer (of type struct) in C

I'm writing a C program with the following structs:

struct packet_info{
    char *ip_src;
    char *ip_dst; 
    u_short th_sport;
    u_short th_dport; 
    u_int   th_seq;
};

struct key_value{
    struct packet_info key;
    long unsigned int value;
};

I want to create a buffer of the type key_value with 1000 items (MAX_SIZE). However, I don't know how to allocate memory for it.

I was doing it like this:

struct key_value *buffer = malloc(MAX_SIZE * sizeof(struct key_value))

However, after reading the following question/answer - malloc for struct and pointer in C - I came to the conclusion that I would also need to allocate space for the char* ip_src and char* ip_dst.

What is, after all, the right way to allocate memory for the buffer?

Upvotes: 3

Views: 2123

Answers (2)

LegendofPedro
LegendofPedro

Reputation: 1414

Assuming you want everything on the heap, I would do something like this:

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

#define MAX_SIZE 1000

typedef struct packet_info{
    char *ip_src;
    char *ip_dst; 
    unsigned short th_sport;
    unsigned short th_dport; 
    unsigned int   th_seq;
} packet_info;

typedef struct key_value{
    packet_info key;
    long unsigned int value;
} key_value;

int main() {
    key_value *buffer = malloc(MAX_SIZE * sizeof(key_value));
    for(int i=0; i<MAX_SIZE; i++) {
        buffer[i].key.ip_src = calloc(16, sizeof(char)); // space for `###.###.###.###\0`
        buffer[i].key.ip_dst = calloc(16, sizeof(char));
    }

    //... do stuff ...
    buffer[0].value = 10;
    buffer[0].key.th_sport = 69;
    buffer[0].key.th_dport = 420;
    strcpy(buffer[0].key.ip_src, "127.0.0.1");
    strcpy(buffer[0].key.ip_dst, "8.8.8.8");
    // ...

    // now cleanup
    for(int i=0; i<MAX_SIZE; i++) {
        free(buffer[i].key.ip_src);
        free(buffer[i].key.ip_dst);
    }
    free(buffer);
    buffer = NULL;
}

Use a loop to allocate and zero-out the blocks of memory. Remember to free everything you allocate, and free *buffer only after you're done using it to free the IPs!

It may be useful to keep track of how many of each struct are allocated, and realloc() as necessary. You may also want to think about how the data will be accessed and store it as a linked list or array or array of pointers (etc.).


I compile this example with

gcc -g -std=c11 -c main.c
gcc main.o -o main.exe

And then start debugging with gdb main.exe.

(gdb) b 36
Breakpoint 1 at 0x401632: file main.c, line 37.
(gdb) r
Starting program: C:\Users\Pedro\Desktop\t\main.exe

Breakpoint 1, main () at main.c:37
37              for(int i=0; i<MAX_SIZE; i++) {
(gdb) p buffer
$1 = (key_value *) 0x5044d0
(gdb) p buffer[0]
$2 = {key = {ip_src = 0x5020d0 "127.0.0.1", ip_dst = 0x502110 "8.8.8.8", 
             th_sport = 69, th_dport = 420, th_seq = 3131961357}, value = 10}
(gdb) p buffer[0].key.th_seq
$3 = 3131961357

@OznOg wants to know why I use calloc() instead of malloc(). Notice how, despite not setting it to anything, buffer[0].key.th_seq = 3131961357. Not 0. Not NULL. This is because malloc() instantiates memory, but does not initialise it. If you're not immediately working with the memory, it may be prudent to call memset(buffer, 0, MAX_SIZE * sizeof(key_value)); just after malloc(), to zero-out all allocated memory locations.

For example:

(gdb) p buffer[2]
$3 = {key = {ip_src = 0x3021d0 "", ip_dst = 0x302210 "",
             th_sport = 0, th_dport = 0, th_seq = 0}, value = 0}

Upvotes: 2

SaltyPleb
SaltyPleb

Reputation: 353

The right way would be for each pointer, you allocate some memory for them.

Assuming you want to store an IPv4, you can also specify a size for ip_src and ip_dst like so

struct packet_info{
/* to represent IPv4 in raw bytes */
    char ip_src[4];
    char ip_dst[4]; 
/* to represent IPv4 in string */
    char ip_src[16];
    char ip_dst[16];
    u_short th_sport;
    u_short th_dport; 
    u_int   th_seq;
};

which removes the hassle of having allocating memory for each of these pointers.

EDIT: I did not account you maybe trying to store an IP string instead of raw bytes.

Why a size of 4? to make it simple, an IPv4 has a total of 4 bytes, each representing a part of the address. You can also apply the same logic if you want a string instead by putting 16 (12 numbers + 3 dot + trailing \0).

For IPv6, 16 bytes is required to represent it in raw bytes, so just replace the 4 to 16, and if you wanted a string instead, 40 bytes are required to represent any IPv6 string (32 hexanumbers + 7 colons + trailing \0)

struct packet_info{
/* to represent IPv6 in raw bytes */
    char ip_src[16];
    char ip_dst[16]; 
/* to represent IPv6 in string */
    char ip_src[40];
    char ip_dst[40];
    u_short th_sport;
    u_short th_dport; 
    u_int   th_seq;
};

Upvotes: 2

Related Questions