function to clear malloc, and make pointer to null - c

in my last question, I've asked how to use function to free an malloc'ed array, I wanted to improve my code so that the function won't just free the memory but also will set the pointer to NULL once it finishes the clearing.
Also I want a single function to do both - setting and clearing, depending on the command I'm passing, this is what I've done so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
char **set_arr(int number, char *command);
int command_read(char *command);
void clear_arr(char *arr[], int size);
char set[] = "set";
char clear[] = "clear";
int main() {
int num = // get number from user;
char** my_arr = NULL;
my_arr = set_arr(num, set);
// so far the code works as excepted
set_arr((size_t)&my_arr, clear);
return 0;
}
int command_read(char *command) {
if (strcmp(command, set) == 0)
return 'S';
if (strcmp(command, clear) == 0)
return 'C';
}
char **set_arr(int number, char *command) {
static char **arr = NULL;
static int size;
switch (command_read(command)) {
case 'S':
size = (int)number;
arr = malloc((size + 1) * sizeof(char *));
for (int i = 0; i <= size; i++) {
arr[i] = NULL;
if (i == size)
break;
arr[i] = malloc((string_len) * sizeof(char));
}
break;
case 'C':
clear_arr(arr, size);
free(arr);
uintptr_t value = number;
uint64_t *temp = (void *)value;
*temp = 0x0;
break;
}
return arr;
}
void clear_arr(char *arr[], int size) {
for (int i = 0; i < size; i++) {
free(arr[i]);
arr[i] = NULL;
}
}
I know that there is better methods to clear (and allocate memory?) but my primary question is, did I free all the memory I allocated for the array, and after the clearing, does the pointer my_arr is set correctly to NULL?

Writing a generic function to achieve your goal is not possible in Standard C because pointers to different types of objects may have a different representation so you cannot pass the address of a pointer and expect the function to handle it in a generic manner.
Yet this provision in the C Standard is not used on most current systems today. In particular, the POSIX standard mandates that all pointers have the same representation. Hence your generic function can work on these systems, with some precautions to avoid compilation warnings:
// free an array of allocated things
void free_array(void ***p, size_t count) {
void **array = *p;
for (size_t i = 0; i < count; i++) {
free(array[i]);
array[i] = NULL; // for safety
}
free(array);
*p = NULL;
}
// deal with the non portable conversion with macros
#define FREE_ARRAY(p, n) free_array((void ***)(void *)&(p), n)
// allocate an array of pointers to allocated things of size `size`.
// return a pointer to the array or `NULL` if any allocation failed
void **malloc_array(size_t count, size_t size) {
void **array = malloc(count * sizeof(*array));
if (array) {
for (size_t i = 0; i < count; i++) {
array[i] = calloc(size, 1); // allocate and initialize to all bits zero
if (array[i] == NULL) {
while (i-- > 0) {
free(array[i]);
array[i] = NULL;
}
return NULL;
}
}
}
return array;
}
#define MALLOC_ARRAY(n, type) ((type **)(void *)malloc_array(n, sizeof(type)))
#define MALLOC_2D_ARRAY(n1, n2, type) ((type **)(void *)malloc_array(n1, (n2) * sizeof(type)))
Passing the command as a string is very inefficient. You should use an int or an enum for the command, but you can use the above macros and code in your program this way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
int main() {
int string_len = 100;
int num = 10; // get number from user;
char **my_arr = MALLOC_2D_ARRAY(num, string_len, char);
FREE_ARRAY(my_arr, num);
return 0;
}

Related

How do I allocate memory for a new string in a C Multiarray?

I am trying to find a way to create a dynamically allocated array of C strings. So far I have come with the following code that allows me to initialize an array of strings and change the value of an already existing index.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void replace_index(char *array[], int index, char *value) {
array[index] = malloc(strlen(value) + 1);
memmove(array[index], value, strlen(value) + 1);
}
int main(int argc, const char * argv[]) {
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
replace_index(strs, 2, "new_value");
// The above code works fine, but I can not use it to add a value
// beyond index 4.
// The following line will not add the string to index 5.
replace_index(strs, 5, "second_value");
}
The function replace_index will work to change the value of a string already include in the initializer, but will not work to add strings beyond the maximum index in the initializer. Is there a way to allocate more memory and add a new index?
First off, if you want to do serious string manipulation it would be so much easier to use almost any other language or to get a library to do it for you.
Anyway, onto the answer.
The reason replace_index(strs, 5, "second_value"); doesn't work in your code is because 5 is out of bounds-- the function would write to memory unassociated with strs. That wasn't your question, but that's something important to know if you didn't. Instead, it looks like you want to append a string. The following code should do the trick.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **content;
int len;
} string_array;
void free_string_array(string_array *s) {
for (int i = 0; i < s->len; i++) {
free(s->content[i]);
}
free(s->content);
free(s);
}
int append_string(string_array *s, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->len++;
char **resized = realloc(s->content, sizeof(char *)*s->len);
if (!resized) {
s->len--;
free(value);
return -1;
}
resized[s->len-1] = value;
s->content = resized;
return 0;
}
string_array* new_string_array(char *init[]) {
string_array *s = calloc(1, sizeof(string_array));
if (!s || !init) {
return s;
}
while (*init) {
if (append_string(s, *init)) {
free_string_array(s);
return NULL;
}
init++;
}
return s;
}
// Note: It's up to the caller to free what was in s->content[index]
int replace_index(string_array *s, int index, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->content[index] = value;
return 0;
}
int main() {
string_array *s = new_string_array((char *[]) {"help", "me", "learn", "dynamic", "strings", NULL});
if (!s) {
printf("out of memory\n");
exit(1);
}
free(s->content[2]);
// Note: No error checking for the following two calls
replace_index(s, 2, "new_value");
append_string(s, "second value");
for (int i = 0; i < s->len; i++) {
printf("%s\n", s->content[i]);
}
free_string_array(s);
return 0;
}
Also, you don't have to keep the char ** and int in a struct together but it's much nicer if you do.
If you don't want to use this code, the key takeaway is that the array of strings (char ** if you prefer) must be dynamically allocated. Meaning, you would need to use malloc() or similar to get the memory you need, and you would use realloc() to get more (or less). Don't forget to free() what you get when you're done using it.
My example uses strdup() to make copies of char *s so that you can always change them if you wish. If you have no intention of doing so it might be easier to remove the strdup()ing parts and also the free()ing of them.
Static array
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
This declares strs as an array of pointer to char and initializes it with 5 elements, thus the implied [] is [5]. A more restrictive const char *strs[] would be more appropriate if one were not intending to modify the strings.
Maximum length
char strs[][32] = {"help", "me", "learn", "dynamic", "strings"};
This declares strs as an array of array 32 of char which is initialized with 5 elements. The 5 elements are zero-filled beyond the strings. One can modify this up to 32 characters, but not add more.
Maximum capacity singleton for constant strings
static struct str_array { size_t size; const char *data[1024]; } strs;
This will pre-allocate the maximum capacity at startup and use that to satisfy requests. In this, the capacity is 1024, but the size can be any number up to the capacity. The reason I've made this static is this is typically a lot to put the stack. There is no reason why it couldn't be dynamic memory, as required.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
static struct { size_t size; const char *data[1024]; } strs;
static const size_t strs_capacity = sizeof strs.data / sizeof *strs.data;
/** Will reserve `n` pointers to strings. A null return indicates that the size
is overflowed, and sets `errno`, otherwise it returns the first string. */
static const char **str_array_append(const size_t n) {
const char **r;
if(n > strs_capacity - strs.size) { errno = ERANGE; return 0; }
r = strs.data + strs.size;
strs.size += n;
return r;
}
/** Will reserve one pointer to a string, null indicates the string buffer is
overflowed. */
static const char **str_array_new(void) { return str_array_append(1); }
int main(void) {
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new())) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
return success;
}
Dynamic array
struct str_array { const char **data; size_t size, capacity; };
I think you are asking for a dynamic array of const char *. Language-level support of dynamic arrays is not in the standard C run-time; one must write one's own. Which is entirely possible, but more involved. Because the size is variable, it will probably be slower, but in the limit as the problem grows, by a constant average.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/** A dynamic array of constant strings. */
struct str_array { const char **data; size_t size, capacity; };
/** Returns success allocating `min` elements of `a`. This is a dynamic array,
with the capacity going up exponentially, suitable for amortized analysis. On
resizing, any pointers in `a` may become stale. */
static int str_array_reserve(struct str_array *const a, const size_t min) {
size_t c0;
const char **data;
const size_t max_size = ~(size_t)0 / sizeof *a->data;
if(a->data) {
if(min <= a->capacity) return 1;
c0 = a->capacity < 5 ? 5 : a->capacity;
} else {
if(!min) return 1;
c0 = 5;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) {
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** Returns a pointer to the `n` buffered strings in `a`, that is,
`a + [a.size, a.size + n)`, or null on error, (`errno` will be set.) */
static const char **str_array_buffer(struct str_array *const a,
const size_t n) {
if(a->size > ~(size_t)0 - n) { errno = ERANGE; return 0; }
return str_array_reserve(a, a->size + n)
&& a->data ? a->data + a->size : 0;
}
/** Makes any buffered strings in `a` and beyond if `n` is greater then the
buffer, (containing uninitialized values) part of the size. A null on error
will only be possible if the buffer is exhausted. */
static const char **str_array_append(struct str_array *const a,
const size_t n) {
const char **b;
if(!(b = str_array_buffer(a, n))) return 0;
return a->size += n, b;
}
/** Returns a pointer to a string that has been buffered and created from `a`,
or null on error. */
static const char **str_array_new(struct str_array *const a) {
return str_array_append(a, 1);
}
/** Returns a string array that has been zeroed, with zero strings and idle,
not taking up any dynamic memory. */
static struct str_array str_array(void) {
struct str_array a;
a.data = 0, a.capacity = a.size = 0;
return a;
}
/** Erases `a`, if not null, and returns it to idle, not taking up dynamic
memory. */
static void str_array_(struct str_array *const a) {
if(a) free(a->data), *a = str_array();
}
int main(void) {
struct str_array strs = str_array();
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(&strs, 5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new(&strs))) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
str_array_(&strs);
return success;
}
but will not work to add strings beyond the maximum index in the initializer
To do that, you need the pointer array to be dynamic as well. To create a dynamic array of strings is one of the very few places where using a pointer-to-pointer to emulate 2D arrays is justified:
size_t n = 5;
char** str_array = malloc(5 * sizeof *str_array);
...
size_t size = strlen(some_string)+1;
str_array[i] = malloc(size);
memcpy(str_array[i], some_string, size);
You have to keep track of the used size n manually and realloc more room in str_array when you run out of it. realloc guarantees that previous values are preserved.
This is very flexible but that comes at the cost of fragmented allocation, which is relatively slow. Had you used fixed-size 2D arrays, the code would perform much faster but then you can't resize them.
Note that I used memcpy, not memmove - the former is what you should normally use, since it's the fastest. memmove is for specialized scenarios where you suspect that the two arrays being copied may overlap.
As a side-note, the strlen + malloc + memcpy can be replaced with strdup, which is currently a non-standard function (but widely supported). It seems likely that strdup will become standard in the upcoming C23 version of C, so using it will become recommended practice.

How to declare a pointer to an array of pointer

I have a task to create an array of pointers to structure. I need to use just void functions and "malloc". I have no idea how to do it, could you help me?
void create1(apteka*** a, int size)
{
**a = (apteka**) malloc(size* sizeof(apteka*));
for (int i = 0; i < size; i++)
{
x[0][i] = (apteka*)malloc(size * sizeof(apteka));
}
}
I have a task to create an array of pointers to structure
You need two "sizes":
The number of pointers
The size of the struct
You only pass one.
So fix your code for example like this
#include <stdlib.h> /* for malloc(), free() */
void create1(void *** pppv, size_t n, size_t s)
{
assert(NULL != pppv);
*pppv = malloc(n * sizeof **pppv);
if (NULL != *pppv)
{
for (size_t i = 0; i < n; ++i)
{
(*pppv)[i] = malloc(s);
if (NULL == (*pppv)[i])
{
/* Failed to completely allocate what has been requested,
so clean up */
for (--i; i >= 0; --i)
{
free((*pppv)[i]);
}
free(*pppv);
*pppv = NULL;
break;
}
}
}
}
Use it like this:
#include <stdlib.h> /* for size_t, free(), exit(), EXIT_FAILURE */
#include <stdio.h> /* for fputs() */
void create1(void ***, size_t, size_t);
struct my_struct
{
int i;
... /* more elements here */
}
#define N (42) /* Number of elements */
int main(void)
{
struct my_struct ** pps = NULL;
create1(&pps, N, sizeof **pps);
if (NULL == pps)
{
fputs(stderr, "create1() failed\n", stderr);
exit(EXIT_FAILURE);
}
/* use array pps[0..N]->i here */
/*Clean up */
for (size_t i = 0; i < N; --i)
{
free(pps[i]);
}
free(pps);
}

realloc seems to not be reallocating memory

I need to dynamically append a char to a string, so I'm using realloc() to add more memory as I need it.
I'm new to C (coming from Python) so I've been reading a lot and this was the best I could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void append_to(char *array, char value) {
size_t buffer = (strlen(array) * sizeof(char)) + sizeof(char);
char *new_array = realloc(array, buffer);
if (new_array == NULL) {
printf("CRITICAL ERROR\n");
exit(-1);
}
array = new_array;
int position = strlen(array);
array[position] = value;
}
int main() {
char *list = malloc(sizeof(char));
for (int i = 1; i < 26; i++){
append_to(list, 'a');
printf("%d -> %s\n", i, list);
}
}
This is just an example to showcase the issue. The code runs flawlessly until iteration 24, see below:
1 -> a
2 -> aa
[...] //omitted
23 -> aaaaaaaaaaaaaaaaaaaaaaa
24 -> aaaaaaaaaaaaaaaaaaaaaaaa
25 ->
What am I missing?
First you forget to add another NUL char at the end of your c-string.
Second, realloc may change the memory location of the data, but you passed the list as value, so the relocation is not visible in the case of data relocation.
That should lokks like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void append_to(char **array, char value) { // pass pointer's address
size_t buffer = (strlen(*array) * sizeof(char)) + sizeof(char) + sizeof(char); // one more to tackle the end of the string
char *new_array = realloc(*array, buffer);
if (new_array == NULL) {
printf("CRITICAL ERROR\n");
exit(-1);
}
*array = new_array;
int position = strlen(*array);
(*array)[position] = value;
(*array)[position+1] = 0; // end of string
}
int main() {
char *list = malloc(sizeof(char));
list[0] = 0; // end of string
for (int i = 1; i < 26; i++){
append_to(&list, 'a'); // pass address of array so that it can be changed by the call
printf("%d -> %s\n", i, list);
}
free(list); // always explicitly free unused resources
}
You didn't receive array as a double pointer, so you can't reassign the caller's pointer when realloc has to move the allocation.
To fix,
// Receive double pointer
void append_to(char **array, char value) {
// Add dereferencing as needed
size_t buffer = (strlen(*array) + 2) * sizeof(char);
char *new_array = realloc(*array, buffer);
if (new_array == NULL) {
printf("CRITICAL ERROR\n");
exit(-1);
}
*array = new_array;
int position = strlen(*array);
array[0][position] = value;
array[0][position+1] = '\0'; // Explicitly NUL terminate, don't assume new memory is zeroed
}
int main() {
char *list = malloc(sizeof(char));
for (int i = 1; i < 26; i++){
append_to(&list, 'a'); // Pass address of list
printf("%d -> %s\n", i, list);
}
}

Using an array of structures with call by reference

Here is my problem: I have to make this program for school and I spent the last hour debugging and googling and haven't found an answer.
I have an array of structures in my main and I want to give that array to my function seteverythingup (by call by reference) because in this function a string I read from a file is split up, and I want to write it into the structure but I always get a SIGSEV error when strcpy with the struct array.
This is my main:
int main(int argc, char *argv[])
{
FILE* datei;
int size = 10;
int used = 0;
char line[1000];
struct raeume *arr = (raeume *) malloc(size * sizeof(raeume*));
if(arr == NULL){
return 0;
}
if(argc < 2){
return 0;
}
datei = fopen(argv[1], "rt");
if(datei == NULL){
return 0;
}
fgets(line,sizeof(line),datei);
while(fgets(line,sizeof(line),datei)){
int l = strlen(line);
if(line[l-1] == '\n'){
line[l-1] = '\0';
}
seteverythingup(&line,arr,size,&used);
}
ausgabeunsortiert(arr,size);
fclose(datei);
return 0;
}
and this is my function:
void seteverythingup(char line[],struct raeume *arr[], int size,int used)
{
char *token,raumnummer[5],klasse[6];
int tische = 0;
const char c[2] = ";";
int i=0;
token = strtok(line, c);
strcpy(raumnummer,token);
while(token != NULL )
{
token = strtok(NULL, c);
if(i==0){
strcpy(klasse,token);
}else if(i==1){
sscanf(token,"%d",&tische);
}
i++;
}
managesize(&arr[size],&size,used);
strcpy(arr[used]->number,raumnummer);
strcpy(arr[used]->klasse,klasse);
arr[used]->tische = tische;
used++;
}
Edit: Since there is more confusion I wrote a short program that works out the part you are having trouble with.
#include <cstdlib>
struct raeume {
int foo;
int bar;
};
void seteverythingup(struct raeume *arr, size_t len) {
for (size_t i = 0; i < len; ++i) {
arr[i].foo = 42;
arr[i].bar = 53;
}
}
int main() {
const size_t size = 10;
struct raeume *arr = (struct raeume*) malloc(size * sizeof(struct raeume));
seteverythingup(arr, size);
return 0;
}
So basically the signature of your functions is somewhat odd. Malloc returns you a pointer to a memory location. So you really dont need a pointer to an array. Just pass the function the pointer you got from malloc and the function will be able to manipulate that region.
Original Answer:
malloc(size * sizeof(raeume*));
This is probably the part of the code that gives you a hard time. sizeof returns the size of a type. You ask sizeof how many bytes a pointer to you raeume struct requires. what you probably wanted to do is ask for the size of the struct itself and allocate size times space for that. So the correct call to malloc would be:
malloc(size * sizeof(struct raeume));

Why do I get the segmentation fault message at run time? (2-dimensional array of struct) [duplicate]

I was playing with double pointers in C and was wondering if I create a function that initializes the table, it crashes on going back to main when I try to make use of the memory allocated by InitStringTable. I believe a simple fix is to make strTable global and then I believe its OK, but I prefer not to do so as this is more of a learning exercise for me in passing the table around for modification i.e. I should be able to modify strTable from main or another function modifyTable after InitStringTable.
Thanks for any help you can give.
int main()
{
char** strTable;
// Allocates memory for string table.
InitStringTable(strTable);
// Below lines should be able to copy strings into newly allocated table.
// Below lines cause crash however.
strcpy(strTable[0], "abcdef");
strcpy(strTable[1], "xy");
}
// Allocates memory for the string table. This function should create a table
// of size 10 strings with each string 50 chars long. The code compiles fine.
void InitStringTable(char** table)
{
int i = 0;
table = (char**)malloc(sizeof(char)*10);
for(i = 0; i < 10; i++)
{
table[i] = (char*)malloc(sizeof(char)*50);
}
for(i = 0; i < 10; i++)
{
memset(table[i], 0, 50);
}
strcpy(table[0], "string1");
}
C is pass by value.
The value assigned to table is lost on returning from InitStringTable().
Also when allocating pointers to char ask for room for pointers to char.
So this:
... = (char**)malloc(sizeof(char)*10);
shall at least be (assuming C):
... = malloc(sizeof(char*)*10);
A possible approach to this would be:
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int InitStringTable(char *** ppptable, const size_t n, const size_t l)
{
int result = 0;
if (NULL == ppptable)
{
result = -1;
errno = EINVAL;
}
else
{
(*ppptable) = malloc(n * sizeof(**ppptable));
if (NULL == (*ppptable))
{
result = -1;
}
else
{
size_t i = 0;
for(; i < n; ++i)
{
(*ppptable)[i] = calloc(l, sizeof(*(*ppptable)[i]));
if (NULL == (*ppptable)[i])
{
result = -1;
/* Failing in the middle requires clean-up. */
for (; i > 0; --i)
{
free((*ppptable)[i-1]);
}
free(*ppptable);
(*ppptable) = NULL;
break;
}
}
}
}
return result;
}
Call it like this:
#include <stdlib.h>
#include <stdio.h>
int InitStringTable(char *** ppptable, const size_t n, const size_t l);
int main(void)
{
int result = EXIT_SUCCESS;
char ** strTable = NULL;
if ( -1 == InitStringTable(&strTable, 10, 42)) //* Allocate array with 10 "strings" à 42 chars. */
{
perror("InitStringTable() failed");
result = EXIT_FAILURE;
}
else
{
strcpy(strTable[0], "abcdef");
strcpy(strTable[1], "xy");
}
return result;
}
And no, I won't get into this ridiculous "You don't wanna be a 3-star-programmer!" discussion.
You have a pointer issue.
It's like if you say:
void inc(int a){
a++;
}
int main(){
int a = 0;
inc(a);
printf ("%d\n", a); // will display 0, not 1
}
does not work.
You must pass &strTable instead of strTable as InitStringTable argument, and change other things in InitStringTable consequently ..
Or just do strTable = InitStringTable(); , and return a char** from InitStringTable.
The lines below InitStringTable() crash, because they are trying to perform operations
on a memory address that is neither in the same scope as theirs nor have any reference to
that memory address.
The function InitStringTable() allocates memory to the table, but cannot be accessed by the
calling function (here main), because the memory is local to the function in which it
allocated.
Therefore in order to use the same memory address for operations in the
calling function you must pass a reference of that address to the calling function.
In your program you can do it as under :
Declare the function as :-
char **InitStringTable(char **);
int main()
{
char** strTable;
strTable = InitStringTable(strTable);
strcpy(strTable[0], "abcdef");
strcpy(strTable[1], "xy");
}
char **InitStringTable(char** table)
{
int i = 0;
table = (char**)malloc(sizeof(char)*10);
for(i = 0; i < 10; i++)
{
table[i] = (char*)malloc(sizeof(char)*50);
}
for(i = 0; i < 10; i++)
{
memset(table[i], 0, 50);
}
strcpy(table[0], "string1");
/* after the function has finished its job, return the address of the table */
return table;
}

Resources