Allocate a 3D char array in C (char ***) - c

I would like to allocate a char***.
I have a got a sentence like this: "This is a command && which I || need to ; split"
I need to put in each box a full sentence just like that:
cmd[0] = "This is a command"
cmd[1] = "wich I"
cmd[2] = "need to"
cmd[3] = "split"
Sentences are separated by tokens like &&, ||, ;, |.
My problem is that I don't know how to allocate my triple dimension array.
I always get a Segmentation Fault.
This is what I do :
for(k = 0; k < 1024; k++)
for( j = 0; j < 1024; j++)
cmd[k][j] = malloc(1024);
But a few line later, in an other loop :
ยป cmd[k][l] = array[i];
I get a segfault here.
How can I do this please ?
Thanks in advance

Please keep in mind that a 2/3D array in C is not the same as a char ***.
If all you wish is to have a 1024^3 character array then you will be good with
char array[1024][1024][1024];
But keep in mind that this will allocate 1 GB of space on your stack which may or may not work.
To allocate this much on the heap you need to type it correctly:
char (*array)[1024][1024] = malloc(1024*1024*1024);
In this scenario array is a pointer to an array of 2D 1024x1024 character matrices.
If you really want to work with char *** (which I do not recommend if your array lengths are static) then you need to allocate all intermediary arrays too:
char *** cmd = malloc(sizeof(char **) * 1024);
for(k = 0; k < 1024; k++) {
cmd[k] = malloc(sizeof(char *) * 1024);
for( j = 0; j < 1024; j++)
cmd[k][j] = malloc(1024);
}

If you are going to be splitting your string by delimiters that are longer then a single character then this is how you could do it with string search.
The following function will accept an input string and a delimiter string.
It will return a char ** which has to be freed and it will destroy your input string (reusing it's memory to store the tokens).
char ** split_string(char * input, const char * delim) {
size_t num_tokens = 0;
size_t token_memory = 16; // initialize memory initially for 16 tokens
char ** tokens = malloc(token_memory * sizeof(char *));
char * found;
while ((found = strstr(input, delim))) { // while a delimiter is found
if (input != found) { // if the strind does not start with a delimiter
if (num_tokens == token_memory) { // increase the memory array if it is too small
void * tmp = realloc(tokens, (token_memory *= 2) * sizeof(char *));
if (!tmp) {
perror("realloc"); // out of memory
}
tokens = tmp;
}
tokens[num_tokens++] = input;
*found = '\0';
}
// trim off the processed part of the string
input = found + strlen(delim);
}
void * tmp = realloc(tokens, (num_tokens +1) * sizeof(char *));
if (!tmp) {
perror("realloc"); // something weird happened
}
tokens = tmp;
// this is so that you can count the amount of tokens you got back
tokens[num_tokens] = NULL;
return tokens;
}
You will need to recursively run this to split by more then one delimiter.

Related

Taking large character array as input in c

I want to take a large character array as input.
E.g.: char array[c][d]
where c <= 200000 and d <= 500000.
Is there any way in C programming language to take such a character array as input?
After declaring an array, you can use calloc function in order to give size to your array. So your code will approximately look like this:
char** array;
array = calloc(c, sizeof(char*));
And there is no need, for defining second value, as it will change autimatically. But if you still want, you can write:
for (int i = 0; i < c; i++)
array[i] = calloc(d, sizeof(char));
Taking large size character array as input in c ...
Is there any way in C programming language to take input such a character array?
If code truly needs to retain all c <= 200000 test cases at once ...
Yes, read in each line, one at a time and then allocate per its length. There is certainly little need for a char array[200000][500000] array. Use an array of char * pointers instead.
#define c_MAX 200000
#define d_MAX 500000
// Allocate pointer array and a single buffer for reading the lines
char **array = malloc(sizeof *array * c_MAX);
if (array == NULL) Handle_OutOfMemory();
char *buffer = malloc(sizeof *buffer * (d_MAX + 3)); // Add room for 1 extra, \n, \0
if (buffer == NULL) Handle_OutOfMemory();
size_t c_count = 0;
while (fgets(buffer, d_MAX + 3, stdin)) {
if (c_count >= c_MAX) Handle_too_many_lines();
size_t len = strlen(buffer);
// lop off potential \n
if (len > 0 && buffer[len-1] == '\n') { // lop off potential \n
buffer[--len] = '\0';
}
if (len > d_MAX) Handle_too_long_a_line();
// Make a copy
size_t sz = sizeof array[c_count][0] * (len + 1);
array[c_count] = malloc(sz);
if (array[c_count] == NULL) Handle_OutOfMemory();
memcpy(array[c_count], buffer, sz);
c_count++;
}
free(buffer);
// TBD code
// Right-size `array` if desired with realloc()
// Use the `c_count` elements of `array`
// when done free them all
for (size_t i = 0; i< c_count; i++) {
free(array[c_count]);
}
free(array);

how to use a 2D array in C

I am new in C and I am trying to create a 2D array of chars.
The logic behind this is to get unknown amount of string inputs
from the user and to be able to get to those strings
(each string ends with a ":") but when I tried to debug I got:
<Error reading characters of string>
This is the code:
int main()
{
int j = 0, rows = 50;
int i=0, lines = 50;
char **names;
names = (char**)malloc(lines*sizeof(char*));
if (i >= lines)
{
names = (char**)realloc(names, 10 * sizeof(char*));
lines = lines * 10;
}
for (j ; names[i][j] != ':'; j++)
{
*names = (char*)malloc(rows * sizeof(char));
if (j >= rows)
{
*names = (char*)realloc(names, 10 * sizeof(char));
rows = rows * 10;
}
scanf("%c", &names[i][j]);
}
i++;
return 0;
}
for (j ; names[i][j] != ':'; j++)
In this loop your test condition tests for ':' in names . names has been allocated memory but it does not contain any content (what will it compare to ?).
Use a do-while loop , in order to execute loop before reading characters in names.
Also you allocate memory for char *'s but you don't allocate memory to these pointers correctly . And without allocating memory correctly you try to store characters at location they point to . This will cause problem .
Allocate memory to each char * and then take input .
Some this like this can be done -
do{
names[i] =malloc(rows * sizeof(char));
if(names!=NULL){
if (j >= rows)
{
*names = (char*)realloc(names, 10 * sizeof(char));
rows = rows * 10;
}
scanf("%c", &names[i][j]);
j++;
i++;
}
}while(names[i][j]!=':')'
Note-
1. You should free the allocated memory . And you first if will not execute (can't understand its use ).
2. Check return of malloc.

Invalid read - Valgrind and C

New to C and Valgrind and manual memory management and I'm having trouble locating an error that I'm getting when I run Valgrind. I have this function which gets strings from the user:
char **get_fragments_from_user(){
// No more than 20k strings containing at most 1k characters
char **strings = malloc(20000 * sizeof(char *));
char tempstring[MAX_INPUT]; //MAX_INPUT = 1001
int count = 0;
while(true){
printf("\n> ");
fgets(tempstring, MAX_INPUT, stdin);
if((strlen(tempstring) > 0) && (tempstring[strlen(tempstring) - 1] == '\n')){
tempstring[strlen(tempstring) - 1] = '\0';
}
if(tempstring[0] == 'q') break;
strings[count] = malloc(sizeof(char) * (strlen(tempstring)+1));
strcpy(strings[count], tempstring);
count++;
}
int i = 0;
char **fstrings = malloc((count)*sizeof(char *)); // count+1 needed? Something I tried removing while debugging
for(i = 0; i < count; i++){
fstrings[i] = malloc(sizeof(char) * (strlen(strings[i])+1));
strcpy(fstrings[i], strings[i]);
free(strings[i]);
}
free(strings);
return fstrings;
}
The idea here is simply to get strings and put them in an array. I initially allocate an array that is large enough to fit the maximum number of strings that could ever be entered (20,000), but I then resize the array so that I don't allocate more memory than the each string needs. I am a little embarrassed with the above code, since its less clean than anything I would have written in another language, but that was my first pass through.
I then get "Invalid read of size 8" from Valgrind when I try to calculate the number of strings in the array using this function:
int lengthOf(char **arr){
int i = 0;
while(arr[i] != NULL){
i++;
}
return i;
}
I'm pretty sure this is due to a dereferenced pointer or something, but I can't find it for the life of me and I've been looking at this code for an hour or so.
So, I believe the problem was that I wasn't allocating enough memory to store the whole array.
Instead of doing:
malloc(count * sizeof(char *));
I should have been allocating count+1, so either:
malloc((count + 1) * sizeof(char *))
or
calloc((count + 1), sizeof(char *));

Array of separators for strtok() function

I want to divide my text into words. Separator is any symbol except latin letters.
Here i have loop, filling my separators array:
for(i = 0; i <= 127; i ++) {
if(!isalpha(i)) {
separators = (char*) realloc(separators, (length + 1) * sizeof(char));
separators[length] = i;
length ++;
}
}
Then i use it here:
char text[] = "hello world!";
char** words = NULL;
char* p = strtok(text, separators);
int cnt = 0;
while(p != NULL) {
words = (char**) realloc(words, (cnt + 1) * sizeof(char*));
words[cnt] = strdup(p);
cnt ++;
p = strtok(NULL, separators);
}
for(i = 0; i < pnt; i ++) {
printf(" - %d %s\n", i + 1, words[i]);
}
As a result a have:
-1 hello world!
If separators array is replaced by " " is works well.
What's the problem with array?
The first value of i in your loop, 0, is not alpha; so a 0 will be stored as the very first byte in the separator array.
strtok() expects to receive the separator list as a string, and strings in C are terminated by a zero. So strtok() receives a sequence beginning with a terminator, and it thinks is an empty list, with no separators at all.
You can start the array from 1 to get rid of that interfering zero:
for (i = 1; i <= 127; i ++) {
if(!isalpha(i)) {
separators = (char*) realloc(separators, (length + 1) * sizeof(char));
separators[length] = i;
length ++;
}
}
// then you also need to terminate it, otherwise strtok() will continue reading
// past the end of the array, with unpredictable (but very likely undesirable) results.
separators[length] = 0x0;
You might also want instead to allocate the string only once (you waste some space, but save some time);
#define MAX_SEPARATORS 128
separators = (char*) malloc(separators, MAX_SEPARATORS * sizeof(char));
for (i = 1; i < MAX_SEPARATORS; i++) {
if (!isalpha(i)) {
separators[length++] = i;
}
}
separators[length] = 0x0;
You have to remember that the strtok wants the separators as a string, complete with a string terminator character ('\0'). Unfortunately you don't have that terminator in the separators "string", so strtok will read that one beyond what you have allocated leading to undefined behavior.

problem in char array?

char *funcNames[]= {"VString","VChar","VArray","VData"};
for(int i=0;i<4;i++)
{
char* temp = funcNames[i];
int len = strlen(funcNames[i]);
for(int j = 0;j<len ;j++)
{
if(j!=0)
{
char arr = temp[j];
}
}
}
here i want to separate "V" from all string in char array ...and create another char array without "V" in starting of the string.i want another char array {String,char,array,data}...i cant make a char array ....help me to solve my issue...
Do you really need a copy? You could just make a new array pointing into the original strings:
char *newArray[4];
for (i = 0; i < 4; i++) {
newArray[i] = funcNames[i] + 1;
}
If you do need to make copies then you'll have to use dynamical allocation to create the buffers to hold the copies. What you will do is declare an array of pointers and place an allocated string buffer in each of the array's entries:
char *newArray[4];
for (i = 0; i < 4; i++) {
newArray[i] = malloc(sizeof(char) * streln(funcNames[0]));
strcpy(newArray[i], funcNames[i] + 1);
}
You will have to call free() on each allocated buffer.
Or if you don't want to do allocation and are know the maximum length of the strings in funcNames:
#define MAX_FUNC_NAME_LEN 32
char newArray[4][MAX_FUNC_NAME_LEN];
for (i = 0; i < 4; i++) {
strcpy(newArray[i], funcNames[i] + 1);
}
There's only small differences between arrays and pointers so I'd opt for:
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main (void) {
int i;
char *funcNames[]= {"VString","VChar","VArray","VData"};
// This is the code that dupicates your strings by allocating an array,
// then allocating each string within that array (and copying).
// Note we use strlen, not strlen+1 to mallocsince we're replacing the
// 'V' at the start with the zero byte at the end. Also we strcpy
// from char offset 1, not 0 (to skip the fist char).
char **newNames = malloc (sizeof(char*) * sizeof(funcNames) / sizeof(*funcNames));
assert (newNames != NULL);
for (i = 0; i < sizeof(funcNames) / sizeof(*funcNames); i++) {
newNames[i] = malloc (strlen (funcNames[i]));
assert (newNames[i] != NULL);
strcpy (newNames[i], funcNames[i] + 1);
}
/* Use your newNames here */
for (i = 0; i < sizeof(funcNames) / sizeof(*funcNames); i++) {
printf ("funcNames[%d] #%08x = '%s'\n", i, funcNames[i], funcNames[i]);
printf (" newNames[%d] #%08x = '%s'\n", i, newNames[i], newNames[i]);
putchar ('\n');
}
// Finished using them.
// Free the strings themselves, then free the array.
for (i = 0; i < sizeof(funcNames) / sizeof(*funcNames); i++)
free (newNames[i]);
free (newNames);
return 0;
}
You can see from the output that the locations of the variables in memory are different and that the content of the new strings is what you wanted:
funcNames[0] #00402000 = 'VString'
newNames[0] #006601c0 = 'String'
funcNames[1] #00402008 = 'VChar'
newNames[1] #006601d0 = 'Char'
funcNames[2] #0040200e = 'VArray'
newNames[2] #006601e0 = 'Array'
funcNames[3] #00402015 = 'VData'
newNames[3] #006601f0 = 'Data'

Resources