I am quite a newbie on C and am trying to get a C routine working.
const char *filenames[entry_count];
for (i = 0; i < entry_count; i++) {
char filename_inzip[256];
unz_file_info file_info;
err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip,
sizeof(filename_inzip), NULL, 0, NULL, 0);
//This doesn't work. My array gets filled with the last entry over and over.
filenames[i] = filename_inzip; //Option 1
//Here I get SIGSEV errors, or gibberish in output when debugging values
strcpy(filenames[i], filename_inzip); //Option 2
}
I guess the first assignment doesn't work because it just points to the same temporary memory address of the char[256] in the loop.
The second option doesn't work I think because I haven't use malloc or similar.
How would I get a properly filled array?
You're correct that you need to allocate memory in the second option.
filenames[i] = strdup(filename_inzip);
is the easiest way of doing this.
strdup is a Posix function rather than standard C. If it isn't available for you
filenames[i] = malloc(strlen(filename_inzip)+1);
if (filenames[i] != NULL) {
strcpy(filenames[i], filename_inzip);
}
does an equivalent job
Note that you'll also have to call free for each array element you allocate later in your program.
Related
char **loadValues()
{
char **toReturn;
int i;
toReturn = malloc(5 * sizeof(char *));
for (i = 0; i < 5; i++)
{
toReturn[i] = malloc(25); //Change the size as per your need
strncpy(toReturn[i], "string", i + 1); //Something to copy
}
return toReturn;
}
I copied above part of the code. In it, the "toReturn" variable is initialized by using malloc. Don't we have to "free" this "toReturn" variable?
Don't we have to free these variables when we return them in C. I still couldn't find a clear answer and I can't find a way to free it when I return it. Can someone explain it to me?
I assume that your question is: how does the function that calls loadValues and receives the pointer it returns, can free the loaded values.
If we leave loadValues as it is now, the best solution is to create another function, called freeValues and call it when we are done with the values:
void freeValues(char **values)
{
for (i = 0; i < 5; i++)
{
free(values[i]);
}
free(values);
}
Then you can do something like this in your main program:
char **values = loadValues();
// ... use values for something
freeValues(values);
Another option, is to allocate toReturn using a single call to malloc, in which case it can be freed simply by calling to free.
You cannot free the allocated memory inside the function and return that data so that it can be used outside the function. But you still can free() the memory outside the function.
Don't we have to "free" this "toReturn" variable?
Yes, eventually you'd want to free the memory after you're done with it.
I still couldn't find clear answer and I can't find a way to free it when I return it. Can someone explain it to me?
When you use the function you assign it to a new pointer, you can use this pointer, not only to access the memory block you returned, but also to free it, example:
int main()
{
char** values = loadValues();
// do something with values
// don't need them anymore
for (int i = 0; i < 5; i++){
free(values[i]);
}
free(values);
}
Note that strncpy(toReturn[i], "string", i + 1); is likely not what you want, in each iteration you are copying only from 1 to 5 characters respectively, not the entire string, and more importantly, not the null terminator.
The third argument of strncpy should be the length of "string" plus 1 more byte for the null terminator, so 7 in this case. Alternatively, if want to copy only part of ther string, you have to null-terminate it yourself.
If the goal is to copy the entire string, you can let the compiler do that for you:
//...
toReturn = malloc(5 * sizeof(char *));
for (i = 0; i < 5; i++)
{
toReturn[i] = strdup("string"); // duplicate string and store it in toReturn[i]
}
//...
Of course you still need to free it yourself like in the first situation.
I'm attempting to read sequences from a FASTA file into a table of structs that I've created, which each contain a character array member called "seq". My code seems to work well for the first loop, but when I realloc() memory for the second sequence, the pointer seems to point to garbage values and then the strcat() method gives me a segfault.
Here's the whole FASTA file I'm trying to read from:
>1
AAAAAAAAAAGWTSGTAAAAAAAAAAA
>2
LLLLLLLLLLGWTSGTLLLLLLLLLLL
>3
CCCCCCCCCCGWTSGTCCCCCCCCCCC
Here's the code (sorry that some of the variable names are in french):
typedef struct _tgSeq { char *titre ; char *seq ; int lg ; } tgSeq ;
#define MAX_SEQ_LN 1000
tgSeq* readFasta(char *nomFile) {
char ligne[MAX_SEQ_LN];
tgSeq *lesSeq = NULL;
int nbSeq=-1;
FILE *pF = fopen(nomFile, "r");
while(fgets(ligne, MAX_SEQ_LN, pF) != NULL) {
if(ligne[0] == '>') {
/*create a new sequence*/
nbSeq++;
//reallocate memory to keep the new sequence in the *lesSeq table
lesSeq = realloc(lesSeq, (nbSeq)*sizeof(tgSeq));
//allocate memory for the title of the new sequence
lesSeq[nbSeq].titre = malloc((strlen(ligne)+1)*sizeof(char));
//lesSeq[nbSeq+1].titre becomes a pointer that points to the same memory as ligne
strcpy(lesSeq[nbSeq].titre, ligne);
//Now we create the new members of the sequence that we can fill with the correct information later
lesSeq[nbSeq].lg = 0;
lesSeq[nbSeq].seq = NULL;
} else {
/*fill the members of the sequence*/
//reallocate memory for the new sequence
lesSeq[nbSeq].seq = realloc(lesSeq[nbSeq].seq, (sizeof(char)*(lesSeq[nbSeq].lg+1+strlen(ligne))));
strcat(lesSeq[nbSeq].seq, ligne);
lesSeq[nbSeq].lg += strlen(ligne);
}
}
// Close the file
fclose(pF);
return lesSeq;
}
For the first line (AAAAAAAAAAGWTSGTAAAAAAAAAAA), lesSeq[nbSeq].seq = realloc(lesSeq[nbSeq].seq, (sizeof(char)*(lesSeq[nbSeq].lg+1+strlen(ligne)))); gives me an empty character array that I can concatenate onto, but for the second line (LLLLLLLLLLGWTSGTLLLLLLLLLLL) the same code gives me garbage characters like "(???". I'm assuming the problem is that the reallocation is pointing towards some sort of garbage memory, but I don't understand why it would be different for the first line versus the second line.
Any help you could provide would be greatly appreciated! Thank you!
The problem here is the first realloc gets the value of nbSeq as 0 which does not allocate any memory.
Replace
int nbSeq=-1;
with
int nbSeq=0;
Access the index with lesSeq[nbSeq - 1]
Some programmer dude already pointed out that you do not allocate enough memory.
You also seem to expect some behaviour from realloc that will not happen.
You call realloc with NULL pointers. This will make it behave same as malloc.
For the first line (AAAAAAAAAAGWTSGTAAAAAAAAAAA), ...= realloc(); gives me an empty character array that I can concatenate onto, but for the second line (LLLLLLLLLLGWTSGTLLLLLLLLLLL) the same code gives me garbage characters like "(???".
You should not expect any specifiy content of your allocated memory. Especially the memory location is not set to 0. If you want to rely on that, you can use calloc.
Or you simply assign a 0 to the first memory location.
You do not really concatenaty anything. Instead you allocate new memory where you could simply use strcpy instead of strcat.
I have an array, say, text, that contains strings read in by another function. The length of the strings is unknown and the amount of them is unknown as well. How should I try to allocate memory to an array of strings (and not to the strings themselves, which already exist as separate arrays)?
What I have set up right now seems to read the strings just fine, and seems to do the post-processing I want done correctly (I tried this with a static array). However, when I try to printf the elements of text, I get a segmentation fault. To be more precise, I get a segmentation fault when I try to print out specific elements of text, such as text[3] or text[5]. I assume this means that I'm allocating memory to text incorrectly and all the strings read are not saved to text correctly?
So far I've tried different approaches, such as allocating a set amount of some size_t=k , k*sizeof(char) at first, and then reallocating more memory (with realloc k*sizeof(char)) if cnt == (k-2), where cnt is the index of **text.
I tried to search for this, but the only similar problem I found was with a set amount of strings of unknown length.
I'd like to figure out as much as I can on my own, and didn't post the actual code because of that. However, if none of this makes any sense, I'll post it.
EDIT: Here's the code
int main(void){
char **text;
size_t k=100;
size_t cnt=1;
int ch;
size_t lng;
text=malloc(k*sizeof(char));
printf("Input:\n");
while(1) {
ch = getchar();
if (ch == EOF) {
text[cnt++]='\0';
break;
}
if (cnt == k - 2) {
k *= 2;
text = realloc(text, (k * sizeof(char))); /* I guess at least this is incorrect?*/
}
text[cnt]=readInput(ch); /* read(ch) just reads the line*/
lng=strlen(text[cnt]);
printf("%d,%d\n",lng,cnt);
cnt++;
}
text=realloc(text,cnt*sizeof(char));
print(text); /*prints all the lines*/
return 0;
}
The short answer is you can't directly allocate the memory unless you know how much to allocate.
However, there are various ways of determining how much you need to allocate.
There are two aspects to this. One is knowing how many strings you need to handle. There must be some defined way of knowing; either you're given a count, or there some specific pointer value (usually NULL) that tells you when you've reached the end.
To allocate the array of pointers to pointers, it is probably simplest to count the number of necessary pointers, and then allocate the space. Assuming a null terminated list:
size_t i;
for (i = 0; list[i] != NULL; i++)
;
char **space = malloc(i * sizeof(*space));
...error check allocation...
For each string, you can use strdup(); you assume that the strings are well-formed and hence null terminated. Or you can write your own analogue of strdup().
for (i = 0; list[i] != NULL; i++)
{
space[i] = strdup(list[i]);
...error check allocation...
}
An alternative approach scans the list of pointers once, but uses malloc() and realloc() multiple times. This is probably slower overall.
If you can't reliably tell when the list of strings ends or when the strings themselves end, you are hosed. Completely and utterly hosed.
C don't have strings. It just has pointers to (conventionally null-terminated) sequence of characters, and call them strings.
So just allocate first an array of pointers:
size_t nbelem= 10; /// number of elements
char **arr = calloc(nbelem, sizeof(char*));
You really want calloc because you really want that array to be cleared, so each pointer there is NULL. Of course, you test that calloc succeeded:
if (!arr) perror("calloc failed"), exit(EXIT_FAILURE);
At last, you fill some of the elements of the array:
arr[0] = "hello";
arr[1] = strdup("world");
(Don't forget to free the result of strdup and the result of calloc).
You could grow your array with realloc (but I don't advise doing that, because when realloc fails you could have lost your data). You could simply grow it by allocating a bigger copy, copy it inside, and redefine the pointer, e.g.
{ size_t newnbelem = 3*nbelem/2+10;
char**oldarr = arr;
char**newarr = calloc(newnbelem, sizeof(char*));
if (!newarr) perror("bigger calloc"), exit(EXIT_FAILURE);
memcpy (newarr, oldarr, sizeof(char*)*nbelem);
free (oldarr);
arr = newarr;
}
Don't forget to compile with gcc -Wall -g on Linux (improve your code till no warnings are given), and learn how to use the gdb debugger and the valgrind memory leak detector.
In c you can not allocate an array of string directly. You should stick with pointer to char array to use it as array of string. So use
char* strarr[length];
And to mentain the array of characters
You may take the approach somewhat like this:
Allocate a block of memory through a call to malloc()
Keep track of the size of input
When ever you need a increament in buffer size call realloc(ptr,size)
I'm working in C trying to create a huffman decoder. This piece of code only works if codearray comes in uninitialized, otherwise it gives me a segmentation fault. However, valgrind complains that codearray is uninitialized if I do it that way. I went through it with ddd and the segmentaion fault happens once strcpy is called and I cannot figure out why.
void printtree_inorder(node* n,char* code,char* letarray,char** codearray)
{
if (n == NULL) {
return;
}
static int counter=0;
appenddigit(code,'0');
printtree_inorder(n -> left,code,letarray,codearray);
remdigit(code);
if (n->let!='\0') {
letarray[counter]=n->let;
strcpy(codearray[counter],code);
counter++;
}
appenddigit(code,'1');
printtree_inorder(n -> right,code,letarray,codearray);
remdigit(code);
}
Here is the calling function:
char code[100]={'\0'};
char** codearray=(char**)malloc(numchars*sizeof(char*));
for (i=0;i<numchars;i++) {
codearray[i]=(char*)malloc(100*sizeof(char));
}
char* letarray=(char*)malloc((numchars+1)*sizeof(char));
letarray[0]='\0';
printtree_inorder(root,code,letarray,codearray);
for (i=0;i<numchars;i++) {
codearray[i]=(char*)malloc(100*sizeof(char));
}
this is the code you talking about? it is not really initialization code, it is making room for data code.
char** codearray=(char**)malloc(numchars*sizeof(char*));
just creates you an array of char *, but they do not point to any valid memory.
so, your "initialization code" just makes sure, that your memory is created correcly.
the other thing what really scares me is, that your counter variable is static.
calling
printtree_inorder(root,code,letarray,codearray);
printtree_inorder(root,code,letarray,codearray);
will also end in a segmentation fault, since counter will be > then numchars when you call it a second time (from outside).
so, lets rewrite your code a bit and make it more safe
char* code = (char *)malloc(numchars + 1);
memset(code, 0, numchars + 1);
char* letarray = (char *)malloc(numchars + 1);
memset(letarray, 0, numchars + 1);
char** codearray = (char **)malloc(numchars * sizeof(char *));
memset(codearray, 0, numchars * sizeof(char *));
printtree_inorder(root, code, letarray, codearray, 0);
free(code);
// do not forget the free the other allocations later as well as
void printtree_inorder(node* n,char* code,char* letarray,char** codearray, int counter)
{
if (n == NULL) {
return;
}
appenddigit(code,'0');
printtree_inorder(n -> left,code,letarray,codearray, counter);
remdigit(code);
if (n->let!='\0')
{
letarray[counter] = n->let;
codearray[counter] = strdup(code);
++counter;
}
appenddigit(code,'1');
printtree_inorder(n -> right,code,letarray,codearray, counter);
remdigit(code);
}
Probably in the "initialized" call the array isn't really correctly initialized at all, therefore the function crashes.
When "not initialized", the array probably contains values that (by chance) don't lead to a segmentation fault, depending on what the program has done previously with the memory that ends up being used for codearray.
The function tries to copy a string to wherever codearray[counter] points to:
strcpy(codearray[counter],code);
In the function call you show this codearray[counter] is a random value since only the array was malloc'ed, but the elements weren't initialized to any specific values. strcpy() then tries to write to that random memory address.
You have to allocate memory for the copy of the string, for example by using strdup() instead of strcpy().
I am using this example:
char *myData[][2] =
{{"John", "j#usa.net"},
{"Erik", "erik#usa.net"},
{"Peter","peter#algonet.se"},
{"Rikard","rikard#algonet.se"},
{"Anders","anders#algonet.se"}};
char **tableData[6];
tableData[0] = myData[0];
tableData[1] = myData[1];
tableData[2] = myData[2];
tableData[3] = myData[3];
tableData[4] = myData[4];
tableData[5] = NULL;//null terminated array
and instead want to place my own strings for name and emails.
(trying to place string xyz into myData, then tableData)
strcpy with myData wont work. I have tried all combination's of pointers and referencing but it doesn't seem to copy the string. Any suggestions?
ok--> strncpy(xyz, argv[i], strlen(argv[i]));
ok--> strcpy(xyz + strlen(argv[i]), "\0");
run time stops here--> strncpy(myData[i][0], xyz, strlen(xyz));
tableData[i] = myData[i];
The pointers in myData[][] as you have it initialized point to literal strings. That memory cannot be written to.
You can allocate new memory for your new strings and place the pointers to the new strings into myData. Or for what you seem to be doing, just store the pointers to the argv[] strings (as long as you're not planning to modify the strings later).
Also, please make sure that the memory block you're copying strings into is large enough to hold the new string.
Software Monkey Edit: Including the \0 terminator; and make sure you free the memory when appropriate.
Create you own local copy of the data and change the pointer in the list:
char **newentry = malloc(sizeof(char*) * 2);
newentry[0] = strdup(myNewName);
newentry[1] = strdup(myNewEmail);
tableData[i] = newentry;
That's the easy part. The hard part is freeing the memory when you are finished. If this is just a little throwaway C program, you can probably get away without freeing memory since the operating system will automatically free it when the app exits.
But if you want to do it Properly with a capital 'P', I'd make a couple of little functions to copy and free table items:
void tableFreeItem(int i)
{
if(tableData[i] != 0)
{
free(tableData[i][0]);
free(tableData[i][1]);
free(tableData[i]);
}
}
void tableSetItem(int i, char *name, char *email)
{
tableFreeItem(i);
tableData[i] = malloc(sizeof(char *) * 2);
tableData[i][0] = strdup(name);
tableData[i][1] = strdup(email);
}
Now we can replace items in the list at will and we can easily free the memory by calling the tableFreeItem() function. Here is an example of how you can use these functions:
#define TABLE_SIZE 5
char **tableData[TABLE_SIZE + 1]; /* +1 to make room for NULL terminator */
/* Clear out the table. This also sets up the NULL terminator at the end. */
memset(tableData, 0, sizeof(tableData));
/* Copy the original data into the table */
for(i = 0; i < TABLE_SIZE; i++)
tableSetItem(i, mydata[i][0], myData[i][1]);
/* Change a data item */
tableSetItem(3, "Adam Pierce", "adam#doctort.org");
/* Free memory when we have finished */
for(i = 0; i < TABLE_SIZE; i++)
tableFreeItem(i);
DISCLAIMER: I have not tried to compile or run this code, I just banged it off from the top of my head. It will probably work.