I am trying to put all the lines of a file in a char **.
My function is very simple:
the only parameter is a pointer to a char array, which containts the file.
I first caculate the number of lines to allocate my char **.
Once is it allocated, I use strtok_r to parse file. and then Segfault.
I wanted to know if it was possible to do that with this way?
char **getlines(char *file)
{
int i = 0;
int nblines = 0;
while (file[i] != '\0')
{
if (file[i] == '\n')
nblines++;
i++;
}
char **array = malloc(sizeof(char*) * nblines);
char *saveptr;
if (nblines == 0)
return NULL;
int a = 0;
char *c = strtok_r(file, "\n", &saveptr);
while (c)
{
array[a] = strtok_r(NULL, "\n", &saveptr);
a++;
}
return array;
}
Should be:
char **array = malloc(sizeof(char*) * nblines);
which allocates an array of pointers to your lines.
It's confusing to speak about a file while you're actually having a char* string.
Then your while(c) loop does not end because you're not updating c in it. I leave that to you to fix.
Also, you have a memory leak with return NULL;. Put that check above array's malloc().
Sure you need the re-entrant version of strtok()?
Related
I have a school project where I have to code a shell in C and I'm stuck on the parser function. Would really appreciate some help in understanding where is my error and if my code is good at all. Thanks in advance !
#define BUFFSIZE 1024
char buf[BUFFSIZE];
int parse_line(char *s,char **newargv[]){
char *sppos;
int arraypos = 0;
int offset;
int newargc;
*newargv = (char **)malloc(sizeof(char *)*(1+100));
do{
if(strpbrk(s," ") != NULL){
char *cpy = calloc(40,sizeof(char));
sppos = strpbrk(s," "); //Locates position of space char in string
offset = sppos-s;
strncpy(cpy,s,offset); //copies string before space
*(newargv)[arraypos] = cpy;
arraypos++;
}
else{
*(newargv)[arraypos] = s;
}
s=s+offset+1; //moves pointer to position after space
}while(strpbrk(s," ") != NULL);
*(newargv)[arraypos] = NULL;
newargc = arraypos+1;
return newargc;
}
int main(int argc, char **argv){
char *input;
char **newargv;
int newargc;
while(1) {
if (scanf("%s",input) == EOF) break;
char *cwd = getcwd(buf,BUFFSIZE);
*(cwd+100) = '\0';
newargc = parse_line(input,&newargv);
printf("%s",cwd);
}
return 0;
}
UPEC L2 INFO - TP Shell
The segmentation fault is probably from not initializing argv.
An example of the call might help.
And the type might be wrong too. The current type is an array of pointers to pointers to char. You probably wanted a pointer to an array of pointers to char. Or maybe pointer to pointer to pointer to char.
char *(*argv)[]
char ***argv
In the current usage, you must make an array, and then a collection of pointers to char, which the array values must be initialized to point to. Then parse_line() gets called.
With the revised definition, parse_line should determine how many entries it is returned, and allocate an array of pointers of that count plus one. Then assign this to *argv. Then the string assignments are are (*argv)[arraypos] = whatever;
Also, another failure will occur because you free(cpy); after returning cpy in the argv array. This will only happen after you return from parse_line(), and won't normally be a segmentation fault, at least not at once.
Finally, I agree with not using strtok. Once you start adding additional parsing rules (like quoting), it becomes non-viable.
This is the code after correction and it seems to work.
Still need to check if the strings are well parsed inside the array and make the program terminate properly when CTRL+D(EOF) is pressed.
Thanks for all the help
int parse_line(char *s,char **newargv[]){
char *sppos;
int arraypos = 0;
int offset;
int newargc;
*newargv = (char **)malloc(sizeof(char *)*(1+BUFFSIZE));
do{
if(strpbrk(s," ") != NULL){
char *cpy = calloc(100,sizeof(char));
sppos = strpbrk(s," "); //Locates position of space char in string
offset = sppos-s;
strncpy(cpy,s,offset); //copies string before space
*(newargv)[arraypos] = cpy;
arraypos++;
}
else{
*(newargv)[arraypos] = s;
}
s=s+offset+1; //moves pointer to position after space
}while(strpbrk(s," ") != NULL);
*(newargv)[arraypos] = NULL;
newargc = arraypos+1;
return newargc;
}
int main(int argc, char **argv){
char input[BUFFSIZE];
char **newargv;
int newargc;
while(1) {
if (read(1,input,BUFFSIZE) == EOF) break;
char *dir;
dir = getcwd ((char *) NULL, BUFFSIZE);
if (dir == NULL) perror ("getcwd");
else
{
write(1,dir,BUFFSIZE);
free (dir);
}
printf("$ ");
newargc = parse_line(input,&newargv);
printf("%s",input);
}
return 0;
}
I've written the following C function to split a string in a delimiter and return the word number "num_words":
void split_string(char *string, char **result, char *delimiter, int num_words){
long unsigned int cont_len = strlen(string);
char line[cont_len];
strcpy(line, string);
int i = 0;
char *tmp;
char *p = strtok (line, delimiter);
while ((p != NULL) & (i <= num_words)){
if (i == num_words){
*result = strdup(p);
break;
}
p = strtok (NULL, delimiter);
i = i+1;
}
free(p);
}
and in main:
char *string = "hello whole world";
char *result;
char *delimiter = " ";
int num_words = 2;
split_string(string, &result, delimiter, num_words);
In this example, split_string would make result equal to "world". However, when I try to debug with gdb, I get a munmap_chunk(): invalid pointer error originated in the free(p) code line of the split_string function.
I know that strdup allocates memory, and that's why I was trying to free the p pointer. Where should I place this free? Should I only free(result) in the main instead? I've read answers on similar stackoverflow questions, but none could solve my problem...
You don't need to free the memory after strtok.
For further reading: Do I need to free the strtok resulting string?
char * removeChar(char * str, char c){
int len = strlen(str);
int i = 0;
int j = 0;
char * copy = malloc(sizeof(char) * (len + 1));
while(i < len){
if(str[i] != c){
copy[j] = str[i];
j++;
i++;
}else{
i++;
}
}
if(strcmp(copy, str) != 0){
strcpy(str,copy);
}else{
printf("Error");
}
return copy;
}
int main(int argc, char * argv[]){
char str[] = "Input string";
char * input;
input = removeChar(str,'g');
printf("%s\n", input);
free(input);
return 0;
}
I don't know why every time I try to run it ,it always says uninitialized variable and sticks in the strcpy line and printf line.
Basically this function is to take a string, and a character and removes the that character from the string (because I am learning malloc so that's why I wrote the function like this).
After the while loop do:
copy[j] = '\0';
to NULL-terminate your string; that way it can work with methods coming from <string.h>, which assume that the string is nul-terminated.
PS: One warning you should see is about not returning copy in your function in any case, because now if the condition of the if statement is wrong, your function won't return something valid, so add this:
return copy;
at the end of your function (which is now corrected with your edit).
Other than that, the only warning you should still get are for the unused arguments of main(), nothing else:
prog.c: In function 'main':
prog.c:32:14: warning: unused parameter 'argc' [-Wunused-parameter]
int main(int argc, char * argv[]){
^~~~
prog.c:32:27: warning: unused parameter 'argv' [-Wunused-parameter]
int main(int argc, char * argv[]){
^~~~
While you copy over bytes from str to copy, you don't add a terminating null byte at the end. As a result, strcmp reads past the copied characters into unitialized memory, possibly past the end of the allocated memory block. This invokes undefined behavior.
After your while loop, add a terminating null byte to copy.
Also, you never return a value if the if block at the end is false. You need to return something for that, probably the copied string.
char * removeChar(char * str, char c){
int len = strlen(str);
int i = 0;
int j = 0;
char * copy = malloc(sizeof(char) * (len + 1));
while(i < len){
if(str[i] != c){
copy[j] = str[i];
j++;
i++;
}else{
i++;
}
}
// add terminating null byte
copy[j] = '\0';
if(strcmp(copy, str) != 0){
strcpy(str,copy);
}
// always return copy
return copy;
}
You never initialised input and the some compilers don't notice,
that the the value is never used before the line
input = removeChar(str, 'g');
in your code. So they emit the diagnostic just to be sure.
strcpy(str, copy)
gets stuck in your code, as copy never got a closing 0 byte and
so depends on the nondeterministic content of your memory at the
moment of the allocation of the memory backing copy, how long strcpy
will run and if you get eventually a SIGSEGV (or similar).
strcpy will loop until it finds a 0 byte in your memory.
For starters to remove a character from a string there is no need to create dynamically a character array and then copy this array into the original string.
Either you should write a function that indeed removes the specified character from a string or a function that creates a new string based on the source string excluding the specified character.
It is just a bad design that only confuses users. That is the function is too complicated and uses redundant functions like malloc, strlen, strcmp and strcpy. And in fact it has a side effect that is not obvious. Moreover there is used incorrect type int for the length of a string instead of the type size_t.
As for your function implementation then you forgot to append the terminating zero '\0' to the string built in the dynamically allocated array.
If you indeed want to remove a character from a string then the function can look as it is shown in the demonstrative program.
#include <stdio.h>
char * remove_char(char *s, char c)
{
char *p = s;
while (*p && *p != c) ++p;
for ( char *q = p; *p++; )
{
if (*p != c) *q++ = *p;
}
return s;
}
int main( void )
{
char str[] = "Input string";
puts(str);
puts(remove_char(str, 'g'));
return 0;
}
The program output is
Input string
Input strin
If you are learning the function malloc and want to use it you in any case should try to implement a correct design.
To use malloc you could write a function that creates a new string based on the source string excluding the specified character. For example
#include <stdio.h>
#include <stdlib.h>
char * remove_copy_char(const char *s, char c)
{
size_t n = 0;
for (const char *p = s; *p; ++p)
{
if (*p != c) ++n;
}
char *result = malloc(n + 1);
if (result)
{
char *q = result;
for (; *s; ++s)
{
if (*s != c) *q++ = *s;
}
*q = '\0';
}
return result;
}
int main( void )
{
char *str = "Input string";
puts(str);
char *p = remove_copy_char(str, 'g');
if ( p ) puts(p );
free(p);
return 0;
}
The program output will be the same as above.
Input string
Input strin
Pay attention to the function declaration
char * remove_copy_char(const char *s, char c);
^^^^^^
In this case the source string can be a string literal.
char *str = "Input string";
I'm trying to allocate a string within a struct with fscanf,
I tried this:
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <Windows.h>
#include <string.h>
typedef struct _SPerson {
char *name;
char *surname;
char *id;
char *telephone;
}SPerson;
void main (void) {
unsigned int ne;
SPerson Archive[1000];
Load(Archive,&ne);
}
int Load(SPerson Archive[],unsigned int *ne) {
int k,i=0;
char s[4][20];
FILE *f;
f = fopen("archive.txt","r");
if(f==0) return 0;
while((k=fscanf(f,"%s %s %s %s",s[0],s[1],s[2],s[3]))==4) {
Archive[i].id = (char*) malloc( sizeof(char) *strlen(s[0]));
Archive[i].id =s[0];
Archive[i].name = (char*) malloc( sizeof(char) *strlen(s[1]));
Archive[i].name = s[1];
Archive[i].surname = (char*) malloc( sizeof(char) *strlen(s[2]));
Archive[i].surname = s[2];
Archive[i].telephone = (char*) malloc( sizeof(char) *strlen(s[3]));
Archive[i].telephone =s[3];
i++;
}
*ne = i;
fclose(f);
return 1;
}
Maybe in my brain it's correct, but something went wrong while loading the data, so is this the correct and clear method to read strings dynamically?
I thought to use fgets, but my string is separated by a space, so I need to implement another function, the split. Can Anyone help me?
while((k=fscanf(f,"%s %s %s %s",s[0],s[1],s[2],s[3]))==4) {
//your code
i++;
}
Instead of this use fgets to read complete data and then tokenize it using strtok -
char data[1024],*token;
int j=0;
while(fgets(data,sizeof data,f)!=NULL){ //read from file
token=strtok(data," "); //tokenize data using space as delimiter
while(token!=NULL && j<4){
j=0;
sprintf(s[j],"%s",token); //store it into s[i]
j++;
token=strtok(NULL," ");
}
Archive[i].id = malloc(sizeof *Archive[i].id * (strlen(s[0])+1)); //allocate memory
strcpy(Archive[i].id,s[i]); //copy at that allocated memory
//similar for all
i++;
}
This can be used instead of your loop.
Note - Do not do this -
Archive[i].id = (char*) malloc( sizeof(char) *(strlen(s[0])+1));
Archive[i].id =s[0]; <-- 2.
As after 2. statement you will lose reference to previous allocated memory and will not be able to free it — causing a memory leak.
This is same for all such following statements.
A couple of quick suggestions to improve:
1) void main (void) is really not a good prototype for main. Use:
int main(void);
Or:
int main(int argc, char **argv);
2) There is no need to cast the return of [m][c][re]alloc in C.
This line in your code:
Archive[i].id = (char*) malloc( sizeof(char) *strlen(s[0]));
^^^^^^^ ^^^^^^^^^^^^^^ //^^^ == remove
Should be written:
Archive[i].id = malloc( strlen(s[0]) + 1);//no cast, and sizeof(char) is always == 1
//"+ 1" for NULL termination
3) Suggest using fgets(), strtok() and strcpy() as a minimal method to read, parse and copy strings from file into struct members:
Note: there will be a number of calls to malloc() here, and
each will have to be freed at some point. To avoid all that, it would
be better if your struct contained members with hard coded stack
memory:
typedef struct
{
char name[80];
char surname[80];
char id[80];
char telephone[80];
}SPerson;
But assuming you have a reason to use heap memory, here is a way to do it using the struct as you have defined it:
char line[260];//line buffer (hardcoded length for quick demo)
char *tok={0};//for use with strtok()
int len = 0;//for use with strlen()
FILE *f;
f = fopen("archive.txt","r");//did not have example file for this demo
//therefore do not have delimiters, will guess
if(f==0) return 0;
i = 0;//initialize your index
while(fgets(line, 260, f))
{
tok = strtok(line, " ,\n\t");// will tokenize on space, newline, tab and comma
if(tok)
{
len = strlen(tok);
Archive[i].id = malloc(len + 1);//include space for NULL termination
strcpy(Archive[i].id, tok);//note correct way to assign string
//(Archive[i].id = tok is incorrect!!)
}
else {//handle error, free memory and return}
tok = strtok(NULL, " ,\n\t");//note NULL in first arg this time
if(tok)
{
len = strlen(tok);
Archive[i].name= malloc(len + 1);//include space for NULL termination
strcpy(Archive[i].name, tok);
}
else {//handle error, free memory and return}
//...And so on for rest of member assignments
//
i++;//increment index at bottom of while loop just before reading new line
}
I'm fairly new to C and I'm trying to work out dynamic memory allocation for reading from a file. At least I think that's what I'm doing.
Anyway, this code works:
int readfromfile(FILE *filepointer)
{
size_t size = 2;
char *str = (char *) malloc(sizeof(char));
int character = 0;
size_t counter = 0;
while((character = fgetc(filepointer)) != EOF)
{
str = (char *) realloc(str, size);
str[counter] = (char) character;
size ++;
counter++;
}
str[counter] = '\0';
printf("+%s+", str);
free(str);
return 0;
}
And this code does not:
int main()
{
char *str = (char *) malloc(sizeof(char));
...
readfromfile(ifpointer, &str);
}
int readfromfile(FILE *filepointer, char **str)
{
size_t size = 2;
int character = 0;
size_t counter = 0;
while((character = fgetc(filepointer)) != EOF)
{
*str = (char *) realloc(*str, size);
*str[counter] = (char) character;
size ++;
counter++;
}
str[counter] = '\0';
printf("+%s+", *str);
free(str);
return 0;
}
I cannot understand why because as far as I understand I'm sending a pointer to the location of the char array to the function and accessing the data everytime. The compilers shows no error messages, it just loops through once and on the second loop crashes after the realloc every time. The character assigned to the first value is garbage too.
I have spent an age trying to get this to worked and done a lot of research so I apologise if I've missed a solution but I'm truly stuck at this point.
You get a crash because
*str[counter] = (char) character;
is the same as
*(str[counter]) = (char) character;
as opposed to
(*str)[counter] = (char) character;
which is actually what you wanted. Read Operator Precedence on Wikipedia. You'll find that [] has more precedence than the * (dereference operator).
Also, the cast here, as well as in the calls to realloc and malloc, is unnecessary. Don't forget to check the return value of realloc, malloc etc to see if they were successful in allocating memory.
Now, you have another problem: free(str); in the second code should be free(*str);. Note that after *str has been freed from the function, you aren't supposed to read or write into this memory location from main as it has now become invalid for you to tamper with.
in your int readfromfile(FILE *filepointer, char **str) the parameter char **str is actually the same as char *str[], which means **str is expecting an array of char pointers. however you're passing to it char *str which is just an array of char
when you use readfromfile(...) you should do it this way (something like...):
char *str[2] = {"some char array", "another char array"};
readfromfile(ifpointer, str);
or:
char *a = "this char array";
char **str = &a[0];
readfromfile(ifpointer, str);
you'll get the idea...