This is the relevant piece of code I'm working on. I tokenize an input from the stdin with no issue and when I go to copy that input, I'm getting a segfault. However, I get no segfault with "strcpy(s,input)". Am I missing something fundamental here? Thank you
char *s = malloc(64 * sizeof(char));
char *token = malloc(64 * sizeof(char));
char *currstring = malloc(128 * sizeof(char));
currstring = NULL;
fgets(input,100, stdin);
strcpy(s, input);
token = strtok(s,delim);
while (token)
{
//Condition checking
strcpy(currstring,token);
}
char *currstring = malloc(128 * sizeof(char));
currstring = NULL;
You allocate memory, but then you immediately discard it and set the pointer to NULL. Get rid of the second line.
If you were trying to set it to an empty string (""), instead do:
currstring[0] = '\0';
// or
strcpy(currstring, "");
This probably isn't necessary, though. You don't need to set the string to "" if you're going to do a strcpy() later.
char *token = malloc(64 * sizeof(char));
You also do not need to allocate memory for token. strtok() will cause token to point somewhere within s, so allocating memory for token will simply leak memory once you do token = strtok(s, delim);.
Related
I'm trying to take an unknown amount of lines of console input, convert each line to a String using malloc, and then add each string to an array of strings by dynamically reallocating memory each time. My goal is to have an array where each element is a different line entered by the console (the while loop should end with EOF).
My code is below.
char * inputString = (char *)malloc(100);
char * aWord = (char *)malloc(1);
char ** listWords = (char **)malloc(1);
int wordCount = 0;
while (fgets(inputString, 100, stdin) != NULL)
{
wordCount++;
*(inputString + (strlen(inputString) - 1)) = '\0';
aWord = (char *)realloc(aWord, strlen(inputString) + 1);
aWord = inputString;
listWords = realloc(listWords, sizeof(char) * wordCount);
*(listWords + (wordCount - 1)) = aWord;
}
for (int i = 0; i < wordCount; i++)
printf("%s\n", listWords[i]);
If I were to input in console
abc\n
b\n
cad\n
^Z\n
In theory, I would like my code to print out
abc\n
b\n
cad\n
Each line of console input. Instead it prints
cad\n
cad\n
cad\n
The last line entered. Help much appreciated
You can't copy a string using an assignment, you have to use strcpy():
strcpy(aWord, inputString);
Also,
*(listWords + (wordCount - 1)) = aWord;
can be simplified to:
listWords[wordCount-1] = aWord;
First is this:
aWord = (char *)realloc(aWord, strlen(inputString) + 1);
aWord = inputString;
Here you allocate space for aWord, but then overwrite the returned address with the address contained in inputString. So each string you read in ends up overwriting the previous string. You also should be using malloc instead of realloc because you want to get a new buffer instead of resusing an existing one.
Change the realloc call to malloc and instead of assigning one pointer to the other, usestrcpy` to copy it.
aWord = malloc(strlen(inputString) + 1);
strcpy(aWord, inputString);
Then on this line:
listWords = realloc(listWords, sizeof(char) * wordCount);
You only allocate enough memory for an array of characters instead of an array of pointers to characters. This results in writing past the end of allocated memory, invoking undefined beahvior.
The proper allocation is:
listWords = realloc(listWords, sizeof(char *) * wordCount);
There are three issues.
First, realloc does not allocate an additional aWord but increases/decreases the buffer of the current aWord, thereby probably giving back a new address while releasing the old one. So aWord = (char *)realloc(aWord, strlen(inputString) + 1) will adapt the memory to a new size, but you will loose the content written to aWord previously. Note that *(listWords + (wordCount - 1)) = aWord will let your final list point to aWord. Use malloc instead of realloc.
Second, with aWord = inputString;, you let aWord point to inputString instead of copying its contents. Use strcpy instead:
char *aWord = malloc(aWord, strlen(inputString) + 1);
strcpy (aWord,inputString);
Third, you need to reserve space for character pointers, not just characters.
listWords = realloc(listWords, sizeof(char*) * wordCount);
Create a function in C that takes a string as a parameter and copy it to a new string.
If the original string is "abc", then the new string should be "aabbcc", if the original string is "4", then the newstring should be 44 etc. I believe i understand the concepts needed to solve a problem like this, but i just can't get the new string to be printed in the console. Here is my function:
void eco(char * str)
{
int count = 0;
/*Counts the number of symbols in the string*/
while(*(str + count) != '\0')
{
count++;
}
/*Memory for the new string, wich should be 6 chars long ("aabbcc").*/
char * newstr = malloc(sizeof(char *) * (count * 2));
/*Creating the content for newstr.*/
while(count > 0)
{
*newstr = *str; //newstr[0] = 'a'
*newstr++; //next newstr pos
*newstr = *str; //newstr[1] = 'a'
*str++; //next strpos
count--;
}
/*I can't understand why this would not print aabbcc*/
printf("%s", newstr);
/*free newstr from memory*/
free(newstr);
}
I have tried to print every char individually inside the while loop that creates the content for newstr, and that work. But when i try with the "%s"-flag i either get strange non-keyboard symbols or nothing at all.
I can't understand why this would not print "aabbcc"
It wouldn't do it for two reasons:
You are not passing a pointer to the beginning of the string, and
Because you did not add a null terminator
To fix the first problem, store the pointer to the block allocated to newstr in a temporary before doing the increments.
To fix the second problem, add *newstr = '\0' after the loop, and adjust malloc call to add an extra char for the terminator.
// Do not multiply by sizeof(char), because the standard requires it to be 1
// You used sizeof(char*), which is wrong too.
char * newstr = malloc((count * 2) + 1);
char *res = newstr; // Store the original pointer
// Your implementation of the actual algorithm looks right
while (...) {
... // Do the loop
}
*newstr = '\0';
printf("%s\n", res); // Pass the original pointer
Your loop advances newstr, so after it completes, it's not pointing at the beginning of the string anymore. You need to save the original pointer to use for printing.
For a start this line
char * newstr = malloc(sizeof(char *) * (count * 2));
should be
char * newstr = malloc(1 + (count * 2));
to include the null character
Then you forgot to add it
Also newstr points to the end to the new string
I am a total begginer at C programming and am trying to write a program that reads the value of "stat" file in /proc/. It works for the first few entries, but then it returns "Segmentation error (core dumped)".
So far I found out that the error has to do with the allocation of memory, but I cant seem to find a way to fix it.
My code so far is:
char* readFile(char* filename)
{
FILE *fp;
struct stat buf;
fp=fopen(filename,"r");
stat(filename,&buf);
char *string = malloc(buf.st_size);
char *s;
while(!feof(fp))
{
s=malloc(1024);
fgets(s,1024,fp);
s[strlen(s)-1]='\0';
strcat(string,s);
}
return string;
}
char* readStat(char* path, int statNumber)
{
char* str = malloc(sizeof(readFile(path)));
str = readFile(path);
char * pch = malloc(sizeof(str));
char * vals;
pch = strtok (str," ");
int i = 1;
while (pch != NULL)
{
if(i == statNumber)
vals = pch;
pch = strtok(NULL, " ");
i++;
}
return vals;
}
1) the
s=malloc(1024);
should not into the while it should be oitside the while loop and before the while.
And free it before leaving the function:
free(s);
2) add
string[0] = '\0';
just after
char *string = malloc(buf.st_size);
Otherwise the strcat will not work properly
3) You do not need to allocate memory for str pointer because the readFile function already did
char* str = malloc(sizeof(readFile(path)));
Just replaced with
char* str;
4) And also replace
char * pch = malloc(sizeof(str));
by
char * pch = str;
To start with, you don't allocate space for the terminator for the string variable. You also need to terminate it before you can use it as a destination for strcat.
To continue, when you do sizeof on a pointer, you get the size of the pointer and not what it points to. You have this problem in readStat.
You also have memory leaks, in that you call readFile twice, but never free the memory allocated in it. Oh, and one of the memory allocations in readFile is not needed at all.
And there's another memory leak in that you allocate memory for pch, but you loose that pointer when you assign the result of the strtok call. strtok returns a pointer to the string in the strtok call, so no need to allocate memory for it (which you didn't attempt to free anyway).
s=malloc(1024); should not be in loop, you should allocate the memory once and reset s with NULL before use next time in loop.
Also you should make a habit to free the memory after its usage.
I'm trying to read CSV from a text file in C. The text file format is
1,Bob,bob#gmail.com
2,Daniel,daniel#gmail.com
3,John,john#gmail.com
When I run the program, the number displays fine but the name and email are being displayed as garbage. Here is my program...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int number;
char* name;
char* email;
} Owner;
Owner owners[100];
int load(char* filename)
{
char buffer[200];
char token[50];
Owner* owner;
int owners_size = 0;
FILE* file = fopen(filename, "r");
while(fgets(buffer, 200, file) != NULL)
{
owner = (Owner*)malloc(sizeof(Owner));
owner->number = atoi(strtok(buffer, ","));
owner->name = strtok(NULL, ",");
owner->email = strtok(NULL, ",");
owners[owners_size++] = *owner;
}
fclose(file);
return owners_size;
}
int main()
{
int choise, owners_size, index;
char* owners_filename = "owners2.txt";
owners_size = load(owners_filename);
if(owners_size)
{
printf("owners size: %d\n\n", owners_size);
for(index = 0; index < owners_size; index++)
printf("%d, %s %s\n", owners[index].number, owners[index].name, owners[index].email);
}
}
Can anyone tell me what the reason is. I appreciate your help.
Two problems:
You didn't allocate space for the strings in the structure:
typedef struct
{
int number;
char *name;
char *email;
} Owner;
You need to provide space for those pointers to point at to hold the names.
You keep on supplying pointers to the buffer which is reused for each line of input:
while(fgets(buffer, 200, file) != NULL)
{
owner = (Owner*)malloc(sizeof(Owner));
owner->number = atoi(strtok(buffer, ","));
owner->name = strtok(NULL, ",");
owner->email = strtok(NULL, ",");
owners[owners_size++] = *owner;
}
The first line gets stored as some pointers into the buffer. The next line then overwrites the buffer and chops the line up again, trampling all over the original input.
Consider using strdup():
while (fgets(buffer, 200, file) != NULL)
{
owner = (Owner *)malloc(sizeof(Owner));
owner->number = atoi(strtok(buffer, ","));
owner->name = strdup(strtok(NULL, ","));
owner->email = strdup(strtok(NULL, ","));
owners[owners_size++] = *owner;
}
This is slightly dangerous code (I'd not use it in production code) because it doesn't check that strtok() found a token when expected (or that strdup() was successful). There again, I wouldn't use strtok() in production code either; I'd use POSIX strtok_r() or Microsoft's strtok_s() if they were available, or some alternative technique, probably using strspn() and strcspn(). If strdup() is not available, you can write your own, with the same or a different name:
char *strdup(const char *str)
{
size_t len = strlen(str) + 1;
char *dup = malloc(len);
if (dup != 0)
memmove(dup, str, len); // Or memcpy() - that is safe in this context
return(dup);
}
You might note that your code is only suitable for simple CSV files. If you encountered a line like this (which is legitimate CSV), you'd have problems (with quotes in your values, and mis-splitting because of the comma inside the quoted string):
1,"Bob ""The King"" King","Bob King, Itinerant Programmer <bob#gmail.com>"
The pointer returned by strtok() points to an address within the buffer it is parsing, in this case the local variable buffer. When load() returns the variable it is out of scope (even if it wasn't all instances of owners would be pointing the same address). You need to copy the string returned by strtok(). You could use strdup() if available or use malloc() and strcpy().
There is no need to malloc() new instances of Owner as an array of them already exist (the code as is stands has a memory leak).
Note there is no protection against going beyond the bounds of the owners array. If the file has more than 100 entries then the loop will go beyond the bounds of the array. Extend the terminating condition of the while to prevent this:
while(owners_size < sizeof(owners) / sizeof(owners[0]) &&
fgets(buffer, 200, file) != NULL)
{
}
You just stored pointers into a local buffer. When you leave load() this buffer is gone and not accessible anymore.
You must allocate memory for name and email before you can copy it into the Owner struct.
char *tok;
tok = strtok(NULL, ",");
len = strlen(tok);
owner->name = malloc(len + 1);
strcpy(owner->name, tok);
...
[EDIT: you need to allocate len+1 bytes so you have space for the terminating NUL character. -Zack]
You've only got one line buffer. Every cycle of the loop in load clobbers the text from the previous cycle. And if that wasn't bad enough, the buffer is destroyed when load returns.
The quick fix is to change
owner->name = strtok(NULL, ",");
owner->email = strtok(NULL, ",");
to
owner->name = strdup(strtok(NULL, ","));
owner->email = strdup(strtok(NULL, ","));
(If you don't have strdup, get a real computer it's very simple to write.)
If I were reviewing your code, though, I would ding you for the fixed-size line buffer, the fixed-size owners array, the memory leak, using atoi instead of strtol, using strtok instead of strsep, and the absence of quote handling and parse error recovery, and point out that it would be more efficient to allocate each line as a unit and then save pointers into it.
I have lineget function that returns char *(it detects '\n') and NULL on EOF.
In main() I'm trying to recognize particular words from that line.
I used strtok:
int main(int argc, char **argv)
{
char *line, *ptr;
FILE *infile;
FILE *outfile;
char **helper = NULL;
int strtoks = 0;
void *temp;
infile=fopen(argv[1],"r");
outfile=fopen(argv[2],"w");
while(((line=readline(infile))!=NULL))
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if(temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
} else {
helper=temp;
}
while (ptr != NULL) {
strtoks++;
fputs(ptr, outfile);
fputc(' ', outfile);
ptr = strtok(NULL, " ");
helper[strtoks-1] = ptr;
}
/*fputs(line, outfile);*/
free(line);
}
fclose(infile);
fclose(outfile);
return 0;
}
Now I have no idea how to put every of tokenized words into an array (I created char ** helper for that purpose), so that it can be used in qsort like qsort(helper, strtoks, sizeof(char*), compare_string);.
Ad. 2 Even if it would work - I don't know how to clear that line, and proceed to sorting next one. How to do that?
I even crashed valgrind (with the code presented above) -> "valgrind: the 'impossible' happened:
Killed by fatal signal"
Where is the mistake ?
The most obvious problem (there may be others) is that you're reallocating helper to the value of strtoks at the beginning of the line, but then incrementing strtoks and adding to the array at higher values of strtoks. For instance, on the first line, strtoks is 0, so temp = realloc(helper, (strtoks)*sizeof(char *)); leaves helper as NULL, but then you try to add every word on that line to the helper array.
I'd suggest an entirely different approach which is conceptually simpler:
char buf[1000]; // or big enough to be bigger than any word you'll encounter
char ** helper;
int i, numwords;
while(!feof(infile)) { // most general way of testing if EOF is reached, since EOF
// is just a macro and may not be machine-independent.
for(i = 0; (ch = fgetc(infile)) != ' ' && ch != '\n'; i++) {
// get chars one at a time until we hit a space or a newline
buf[i] = ch; // add char to buffer
}
buf[i + 1] = '\0' // terminate with null byte
helper = realloc(++numwords * sizeof(char *)); // expand helper to fit one more word
helper[numwords - 1] = strdup(buffer) // copy current contents of buffer to the just-created element of helper
}
I haven't tested this so let me know if it's not correct or there's anything you don't understand. I've left out the opening and closing of files and the freeing at the end (remember you have to free every element of helper before you free helper itself).
As you can see in strtok's prototype:
char * strtok ( char * str, const char * delimiters );
...str is not const. What strtok actually does is replace found delimiters by null bytes (\0) into your str and return a pointer to the beginning of the token.
Per example:
char in[] = "foo bar baz";
char *toks[3];
toks[0] = strtok(in, " ");
toks[1] = strtok(NULL, " ");
toks[2] = strtok(NULL, " ");
printf("%p %s\n%p %s\n%p %s\n", toks[0], toks[0], toks[1], toks[1],
toks[2], toks[2]);
printf("%p %s\n%p %s\n%p %s\n", &in[0], &in[0], &in[4], &in[4],
&in[8], &in[8]);
Now look at the results:
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
0x7fffd537e870 foo
0x7fffd537e874 bar
0x7fffd537e878 baz
As you can see, toks[1] and &in[4] point to the same location: the original str has been modified, and in reality all tokens in toks point to somewhere in str.
In your case your problem is that you free line:
free(line);
...invalidating all your pointers in helper. If you (or qsort) try to access helper[0] after freeing line, you end up accessing freed memory.
You should copy the tokens instead, e.g.:
ptr = strtok(NULL, " ");
helper[strtoks-1] = malloc(strlen(ptr) + 1);
strcpy(helper[strtoks-1], ptr);
Obviously, you will need to free each element of helper afterwards (in addition to helper itself).
You should be getting a 'Bad alloc' error because:
char **helper = NULL;
int strtoks = 0;
...
while ((line = readline(infile)) != NULL) /* Fewer, but sufficient, parentheses */
{
ptr = strtok(line, " ");
temp = realloc(helper, (strtoks)*sizeof(char *));
if (temp == NULL) {
printf("Bad alloc error\n");
free(helper);
return 0;
}
This is because the value of strtoks is zero, so you are asking realloc() to free the memory pointed at by helper (which was itself a null pointer). One outside chance is that your library crashes on realloc(0, 0), which it shouldn't but it is a curious edge case that might have been overlooked. The other possibility is that realloc(0, 0) returns a non-null pointer to 0 bytes of data which you are not allowed to dereference. When your code dereferences it, it crashes. Both returning NULL and returning non-NULL are allowed by the C standard; don't write code that crashes regardless of which behaviour realloc() shows. (If your implementation of realloc() does not return a non-NULL pointer for realloc(0, 0), then I'm suspicious that you aren't showing us exactly the code that managed to crash valgrind (which is a fair achievement — congratulations) because you aren't seeing the program terminate under control as it should if realloc(0, 0) returns NULL.)
You should be able to avoid that problem if you use:
temp = realloc(helper, (strtoks+1) * sizeof(char *));
Don't forget to increment strtoks itself at some point.