I am having trouble understanding the concept of pointers in C. I have an array of pointers to character strings,
char ** args;
and I want to copy args to another array of pointers to character strings.
char ** args2;
I tried doing,
args2 = args;
but when I manipulate args2, I also manipulate args. I know this is because of the fact that the arrays are pointers. Also, if I run a for loop,
// by the time I get to the for loop
// args holds {"string1", "string2", "string3"}
// args2 = NULL;
for(i = 0; args[i] != NULL; i++){
args2[i] = args[i];
}
I get a segmentation fault error.
Thanks in advance.
You need to allocate space for args2 first! Even when you do that, don't forget that you'll still just be copying pointers to the original strings, so if you modify the strings in args2, the original strings are the ones being being modified.
You probably want something like:
i = 0;
while (args[i] != NULL)
i++;
args2 = malloc(i * sizeof *args2);
for(j = 0; j < i; j++)
{
args2[i] = strdup(args[i]);
}
Which both allocates memory for the pointers in arg2 and also duplicates all of the original strings. Add one to i before the second loop and set args2[i] to NULL after the second loop if you want to have a NULL terminated array like you started with.
Imagine char** as a pointer of pointers. Each element of it is another pointer, which points to a string (basically you have an array of strings).
Assuming you have a char** args2. If you do:
for(i = 0; args[i] != NULL; i++){
args2[i] = args[i];
}
You copy the value of each pointer from args to args2. It is the same thing as saying:
char *a = "abc";
char *b; // allocate memory
b = a;
Or, in order to copy strings you need to copy their content, not just the pointer to the first element. So, you need to use something like strcpy. Note that in order for this to work, you also need to allocate memory for args2, which I omit here.
for(i = 0; args[i] != NULL; i++){
strcpy(args2[i], args[i]);
}
Also, you might want to add the NULL terminator of args:
args2[i] = NULL;
In order to allocate memory, you need to know the size of the args array. Assume args has n element. Then, the memory allocation for args2 could be done as following (I also add the copying part):
// Allocate memory for n pointers to char
char **args2 = malloc(sizeof(char*) * n);
for(i = 0; args[i] != NULL; i++){
// For each pointer of args 2, allocate memory to store the
// correspondent string of args, including the NUL.
args2 = malloc(strlen(args[i] + 1));
strcpy(args2[i], args[i]);
}
You might want to consider using a static char matrix at the beginning that handles memory allocation, in order to facilitate learning.
Related
I'm trying to create a string parser that breaks a string up into an array of strings using a delimiter. I'm using the function "strtok" which I understand how to use. What I don't understand is how to get my parsing fuction to return an array of the different words in my string. It's been quite some time since I've used C, so this is proving to be quite difficult.
char ** parseString(char * inString){
char **ptr; //What I want to return, an array of strings
int count = 0;
ptr[count] = strtok(inString, " ");
count++;
while(inString!=NULL){
ptr[count]=strtok(NULL, " ");
count++;
}
return ptr;
}
I know that this above code won't work. I vaguely remember how to use malloc, and I know that my above code will result in a seg fault since I haven't malloced, but above is essentially what I want to have happen. How do I appropriately malloc if I don't even know how many words I need to have in my array of strings?
For allocating the vector since you don't know from the start its size there are a couple of strategies:
you can first parse inString to get the number of words, allocate then parse again.
you can reallocate for each word.
you can reallocate with a geometric growth.
In the first variant you do the parsing twice, making it the worst of them
The second has the disadvantage of multiple reallocations, but could be acceptable on small strings and if the function is not critical.
The third one is the best and is what std::vector in C++ does.
I show you the 2nd variant here.
You also need to keep the size of the vector in a separate variable.
char** parseString(char* inString, size_t* tokensLen)
{
char **tokens = NULL; // realloc on NULL acts like malloc so we simplify the code
size_t i = 0;
// parse the first word
char* inToken = strtok(inString, " ");
while (inToken)
{
// allocate for one more pointer
tokens = realloc(tokens, (i + 1) * sizeof *tokens);
assert(tokens);
// point to word
tokens[i] = inToken;
// parse the next word
inToken = strtok(NULL, " ");
++i;
}
// set size
*tokensLen = i;
return tokens;
}
int main()
{
char str[] = "here we go";
size_t len;
char** tokens = parseString(str, &len);
for (size_t i = 0; i < len; ++i)
{
printf("%s\n", tokens[i]);
}
}
I have created double pointer char to be used as a 2d array to store strings. The append function is meant to add the string provided to the end of the array, the num_strings pointer is provided to keep track of the elements in the array (since I can't use sizeof). It seems that at some point, the function isn't allocating enough memory but I can't seem to figure out where and can't find any other issues.
I have already tried giving both the outer array and the inner array large amounts of memory, much more than they need. The issue persists. I have also tried copying the string to the array after the function had run.
int main(int argc, char *argv[]) {
char **strings = NULL;
int num_strings = 0;
append(&strings, &num_strings, "Alex");
append(&strings, &num_strings, "Edward");
// Do things with array
for (int i = 0; i < num_strings; i++) {;
printf("%s\n", strings[i]);
}
// Free memory after use
for (int i = 0; i < num_strings; i++) {
free(strings[i]);
}
free(strings);
strings = NULL;
return 0;
}
void append(char ***array, int * num_strings, char *string) {
if (*array == NULL) {
*array = malloc(sizeof(*array)); // start with enough room for 1 item (pointer)
} else {
// reallocate memory for new item
*array = realloc(*array, (((*num_strings) + 1) * sizeof(*array)));
}
printf("Char Size: %lu\n", sizeof(char));
printf("Given Size: %lu\n", sizeof(***(array)));
*(array[*num_strings]) = malloc((strlen(string) + 1) * sizeof(***(array + 0)));
strcpy(*(array[*num_strings]), string);
(*num_strings)++; // increment the number of strings
}
The output of the program should be the two strings, at the moment it only prints the first and then crashs due to the segmentation fault.
The problem is there are a couple instances of *(array[*num_strings]) that should be (*array)[*num_strings].
The difference is that the first form tries to index through the pointer passed to the function, as if the passed strings were an array, corrupting the caller's stack. The corrected version first derefernces the pointer, then indexed through the target as desired.
There are also a few places where sizeof(*array) is used where it should be sizeof(**array). x = malloc(sizeof(x)) is never correct. But this isn't causing a visible problem.
I have really been confused about this 2D char array
char **arg = malloc(sizeof(char*) * argc)
for (int i = 0; i < argc; i++)
arg[i] = malloc(sizeof(char) * size)
...
...
Now suppose after a series of operations, I forget the variable argc, how can I free those memory?
Can I do something like this? Is this absolutely correct under all circumstances?
char **tmp = arg;
while (*tmp != NULL){
free(*tmp);
tmp++;
}
free(arg);
No
while(*tmp != NULL){
you may reach above a point where you will dereference memory which hasn't been assigned to and trigger undefined behaviour.
Or as suggested you can explicitly assign a NULL to the last allocated pointer, and in that case it will work.
As others have said, the problems with freeing in a loop as shown is that an extra item (argc + 1) needs to be allocated and it has to be set to NULL. An alternative technique is to first allocate the space for the pointers as you have done
char **arg = malloc(sizeof(char*) * argc)
Then, if you know all the subsequent items are the same size, allocate it in one huge block and set the rest of the elements at spaced offsets
arg[0] = malloc(sizeof(char) * size * argc);
for (int i = 1; i < argc; ++i)
arg[i] = arg[i - 1] + size;
Freeing the space is a doddle: no need to even remember argc
free(arg[0]); /* this will free the memory used by all the elements */
free(arg);
The big disadvantages to this technique is that if any of the array elements overrun, it will corrupt the next item. This cannot be detected so easily with a heap check unless it is the last item.
if you define your char * array like this:
char **arg = calloc( 1, sizeof( char * ) * argc );
you can be sure that every undefined pointer will be equal to NULL
and then you can use the while loop almost like you suggested:
char *tmp = *arg;
while ( tmp != NULL ) {
free( tmp );
tmp++;
}
free(arg);
I am attempting to set the last element in a second char ** array to NULL after I encounter a specific char in the first array.
int test(char ** args){
char ** chmd1;
for(int i = 0; args[i] != NULL; ++i){
if(!strncmp(args[i], "<", 1)){
chmd1[i] = NULL;
break;
}
chmd1[i] = args[i];
}
for(int i = 0; chmd1[i] != NULL; ++i){
printf("%s", chmd1[i]);
}
return 0;
}
This code segfaults as the second for loop goes on for more iterations past where the NULL should be.
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
This code segfaults as the second for loop goes on for more iterations past where the the NULL should be.
You have not allocated memory for chmd1 and yet you are using it like it points to valid memory.
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
You can't do that. You have use malloc (or one of the other functions from the malloc group of functions: calloc, realloc) to allocate memory for chmd1 before you can use it.
allocate memory for pointer
char ** chmd1;
I want to be able to be able to do this just by manipulating pointers and not using any mallocs, but I'm completely stuck.
Without allocating memory to chmd1, it will not be possible.
You have to allocate memory for char ** chmd1; before assigning value NULL (or copy elements from args) to any element.
It can be something like
char ** chmd1 = malloc(NUMBER * sizeof(char*));
or even
char * chmd1[NUMBER];
To determine NUMBER value find the NULL in the args first.
EDIT:
Also you can use realloc in your loop as:
char **chmd1 = NULL;
int i;
for(i = 0; argv[i] != NULL; ++i){
chmd1 = (char**)realloc(chmd1, i * sizeof(char*) );
if(!strncmp(argv[i], "<", 1)){
chmd1[i] = NULL;
break;
}
chmd1[i] = argv[i];
}
// then use i as size of chmd1
for(int cnt = 0; cnt < i; cnt++)
{
if( chmd1[i] == NULL ) ; // do something
}
chmd1[i] = args[i];
chmd[i] is a pointer in 2D space and you are not allocating memory for the pointer.
I'm reading in a line from a file (char by char, using fgetc()), where all fields(firstname, lastname, ...) are seperated by an ;. What I now want to do is create a char**, add all the chars to that and replace the ; by \0 so that I effectively get a list of all the fields.
Is that actually possibly? And when I create a char**, e.g. char ** buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
EDIT
Sry, meant to replace ; by \0, bot \n.
EDIT 2
This code should demonstrate what I intend to do (may clarify things a little):
int length = 80; // initial length char *buf = malloc(length); int num_chars = 0;
// handle malloc returning NULL
// suppose we already have a FILE pointer fp for (int ch = fgetc(fp); ch != EOF && ferror(fp) == 0; ch = fgetc(fp)) {
if (length == size) { // expand array if necessary
length += 80;
buf = realloc(buf, length);
// handle realloc failure
}
if (ch == ';') {
buf[num_chars] = '\0'; // replace delimiter by null-terminator
} else {
buf[num_chars] = ch; // else put char in buf
} }
// resize the buffer to chars read buf
= realloc(buf, num_chars);
// now comes the part where I'm quite unsure if it works / is possible
char **list = (char **)buf; // and now I should be able to handle it like a list of strings?
Yes, it's possible. Yes, you can treat it as a one dimensional array. Yes, the memory is contiguous.
But you probably want:
char ** fields = malloc(sizeof(char *) * numFields);
Then you can do:
// assuming `field` is a `char *`
fields[i++] = field;
It's not exactly possible as you describe it, but something similar is possible.
More specifically, the last line of your example char **list = (char **)buf; won't do what you believe. char **list means items pointed by *list will be of the type char*, but the content of *buf are chars. Hence it won't be possible to change one into another.
You should understand that a char ** is just a pointer, it can hold nothing by itself. But the char ** can be the start address of an array of char *, each char * pointing to a field.
You will have to allocate space for the char * array using a first malloc (or you also can use a static array of char * instead of a char**. You also will have to find space for every individual field, probably performing a malloc for each field and copying it from the initial buffer. If the initial buffer is untouched after reading, it would also be possible to use it to keep the field names, but if you just replace the initial ; by a \n, you'll be missing the trailing 0 string terminator, you can't replace inplace one char by two char.
Below is a simplified example of what can be done (removed malloc part as it does not add much to the example, and makes it uselessly complex, that's another story):
#include <stdio.h>
#include <string.h>
main(){
// let's define a buffer with space enough for 100 chars
// no need to perform dynamic allocation for a small example like this
char buf[100];
const char * data = "one;two;three;four;";
// now we want an array of pointer to fields,
// here again we use a static buffer for simplicity,
// instead of a char** with a malloc
char * fields[10];
int nbfields = 0;
int i = 0;
char * start_of_field;
// let's initialize buf with some data,
// a semi-colon terminated list of strings
strcpy(buf, data);
start_of_field = buf;
// seek end of each field and
// copy addresses of field to fields (array of char*)
for (i = 0; buf[i] != 0; i++){
if (buf[i] == ';'){
buf[i] = 0;
fields[nbfields] = start_of_field;
nbfields++;
start_of_field = buf+i+1;
}
}
// Now I can address fields individually
printf("total number of fields = %d\n"
"third field is = \"%s\"\n",
nbfields, fields[2]);
}
I now want to do is create a char**, add all the chars to that
You would allocate an array of char (char*) to store the characters, not an array of char* (char**).
And when I create a char*, e.g. char * buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
Yes, malloc always returns continguous blocks. Yes, you can treat it as single-dimensional an array of char* (with 80/sizeof char* elements). But you'll need to seperately allocate memory for each string you store in this array, or create another block for storing the string data, then use your first array to store pointers into that block.
(or something else; lots of ways to skin this cat)
It may be that Helper Method wants each of the "fields" written to a nul-terminated string. I cannot tell. You can do that with strtok(). However strtok() has problems - it destroys the original string "fed" to it.
#define SPLIT_ARRSZ 20 /* max possible number of fields */
char **
split( char *result[], char *w, const char *delim)
{
int i=0;
char *p=NULL;
for(i=0, result[0]=NULL, p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim), i++ )
{
result[i]=p;
result[i+1]=NULL;
}
return result;
}
void
usage_for_split(void)
{
char *result[SPLIT_ARRSZ]={NULL};
char str[]="1;2;3;4;5;6;7;8;9;This is the last field\n";
char *w=strdup(str);
int i=0;
split(result, w, ";\n");
for(i=0; result[i]!=NULL; i++)
printf("Field #%d = '%s'\n", i, result[i]);
free(w);
}