I am teaching myself C. Right now I'm working on making a shell, based in part on
https://brennan.io/2015/01/16/write-a-shell-in-c/
I'm trying to add pipes like in bash and created a function called "nospace" to eliminate spaces between arguments so that strtok will then separate based on "|".
char* nospace(char *thestring)
{
char* returnline=(char*)malloc(sizeof(char)*50);
int charpos;
charpos=0;
while(*thestring != '\0')
{
if(*thestring!=' '){
returnline[charpos]=*thestring;
charpos++;
}
thestring++;
}
returnline[charpos]='\0';
return returnline;
}
Since I use malloc for the return line I was reading on SO that I need to free it somewhere so since read_args calls nospace I freed it in read_args.
char** read_args(char* line)
{
argamounts=0;
//tokens and strtok was taken from tutorialspoint regarding the strtok function
char**returnargs = (char**) malloc(sizeof(char*)*20);
char* token;
char* linenospace=nospace(line);
//printf("%s\n",linenospace);
token=strtok(linenospace,"|");
//printf("%s first token\n",token);
int argsub=0;
while (token!=NULL)
{
returnargs[argsub]=token;
//printf("%s\n",token); //test that all arguments are read
argamounts++;
token=strtok(NULL,"|");
//printf("%s second token\n",token);
argsub++;
}
//printf("%d",argamounts);
//returnargs[0]=line; //assumes only one arg for now
//cannot free memory here or returnargs is null, why?
//free(linenospace);
//printf("%s returnarg0\n", returnargs[0]);
//printf("%s returnarg1\n", returnargs[1]);
return returnargs;
}
But the shell wasn't reading the arguments and upon inserting all the printf's to find out where the arguments were falling through I realized that freeing "linenospace" drops my arguments. So if strtok returns a pointer which is set to token, and the "elements" of return args are token pointers, is the way that "linenospace" gets freed that I have to free the double pointer in the shell loop function?
void ypsh_loop(void)
{
char *line;
char **args;
int status;
do {
printf("ypsh > ");
line = read_line();
args=read_args(line);
status=shexecute(args);
}while(status);
free(line);
free(args);
}
(I suppose I would have to change the free(args); line to free a double pointer).
Actually in the process of writing this question I downloaded valgrind after a quick search through SO (CentOS is my home OS) and checked for a memory leak. Sure enough there was one and changing "free(args);" to
int freedouble;
for(freedouble=0; freedouble<argamounts; freedouble++)
{
free(args[freedouble]);
}
free(args);
where argamounts is a globally managed variable seemed to have solved the problem. I guess that answers my question but I'll post it here anyway.
Edit:
So apparently the loop function needs to be written this way:
void ypsh_loop(void)
{
char *line;
char **args;
int status;
do {
printf("ypsh > ");
line = read_line();
args=read_args(line);
status=shexecute(args);
free(line);
free(args[0]);
free(args);
}while(status);
}
Moving the free() statements into the do while loop as opposed to outside where they used to be makes sense because the shell keeps looping back and if I keep malloc-ing I need to free over and over.
However, for some reason if I loop through all the args and try to free them I get "Invalid free()" from valgrind. I have to free args[0] or the memory leaks, but I can only free args[0] and no more.
Adding:
printf("amount of args %i\n",argamounts);
int freedouble;
for(freedouble=0; freedouble<argamounts; freedouble++)
{
printf("argument %d is %s ",freedouble,args[freedouble]);
//free(args[freedouble]);
}
into the do while loop to check if all the arguments were registered indicates that they all are, but I can't free them one by one. I'm going to edit this again once I figure out why but if anybody knows, please tell me.
However, for some reason if I loop through all the args and try to free them I get "Invalid free()" from valgrind. I have to free args[0] or the memory leaks, but I can only free args[0] and no more.
You can't free args[1] etc. since you haven't malloc'ed them. Concerning args[0], you also haven't exactly malloc'ed it, but args[0] points to the first token in the memory space allocated by linenospace=nospace(line), usually at its beginning (unless the line starts with |), thus you can mostly abuse args[0] to free the memory allocated by nospace(line).
However, nospace(line) is useless, since a command with all spaces removed, i. e. all arguments concatenated, is unrecognizable (unless there are no arguments). So, I recommend removing nospace() entirely from your program; then there would be also no additional memory allocation to worry about.
Related
char* stringFunction(char *astrics){
char *string;
string = (char*)malloc(MAX);
string[0] = 0;
strcat(string,"Hello ");
strcat(string,astrics);
strcat(string,"\n");
return string;
//free(string);
}
int main(int argc, char* argv){
printf(stringFunction("Robert"));
printf(stringFunction("Robert Greene"));
return 0;
}
If no code is read after the return statement then how am I supposed to free the memory allocated for the pointer - string
When you return an allocated memory bloc, you tranfer ownership to the caller. That means that it is the responsability of the caller to later free the memory bloc when it has finished using it. Alternatively it could in turn transfer ownership to its own caller (and so on and so forth...)
Your program shouldn't free the memory until after it's finished using the memory. At the return statement, your program hasn't finished using that memory, because the main function still wants to print it! If you freed the memory inside stringFunction, then main would print freed memory.
Don't make the common mistake of thinking that you have to free pointers. You don't have to free pointers. You only have to free memory.
In this case main has to free the memory after it has finished using it and doesn't need it any more. This means we can't just write printf(stringFunction( since we need to put the pointer in a variable.
int main(int argc, char* argv){
char *mystring = stringFunction("Robert");
printf(mystring);
free(mystring);
mystring = stringFunction("Robert Greene");
printf(mystring);
free(mystring);
return 0;
}
Extra note: instead of printf(mystring); you should get into the habit of writing printf("%s", mystring);. Otherwise if you change "Robert Greene" to "Robert %s" your program will probably crash. But if you use printf("%s", mystring); it will just print "Hello Robert %s"
I'm having some trouble with some memory issues. The issue is when the line is freed (free(line)), there is a
free(): invalid size error.
From what I know, sscanf doesn't modify the string that is passed into it. Weirdly enough, the free(line) inside the if statement works fine. I'm not sure what the problem is because I've freed the char* like this in other parts of my program without issues, albeit without the sccanf call. Any help would be appreciated.
char* line;
read_line(read, &line, 0);
printf("%s\n", line); //gives "playinfoA/30"
char playerLetter[1];
char numberOfPlayers[2];
char temp[1];
if (sscanf(line, "playinfo%1s%1s%s", playerLetter, temp,
numberOfPlayers) != 3) {
free(line);
return -1;
}
//free(line);
return 0;
If your problem is, free(line) doesn't work when if block fails, then you might want to check if line is actually pointing to something.
Since you did not initialize line, which is a pointer to char, the only other possibility of having it point to some memory location is the call to read_line.
Now, I'm not sure what read_line does, but try passing line instead of &line ?
I have a tokenizer method that returns a char**. The results are being stored in a char** called lineTokens. When I use printf() to print the first token, I get the correct result, but when I use strcmp(lineTokens[0],"Some text"), I get a seg fault. The appropriate code is below.
lineTokens = tokenize(tempString);
printf("token[0] = %s\n", lineTokens[0]);
if(strcmp(lineTokens[0], "INPUTVAR")==0){
printf("It worked\n");
}
EDIT:
My tokenize code is as follows
char** tokenize(char* input){
int i = 0;
char* tok;
char** ret;
tok = strtok(input, " ");
ret[0] = tok;
while(tok != NULL){
printf("%s\n", tok);
ret[i] = tok;
tok = strtok(NULL, " ");
i++;
}
ret[i] = NULL;
return ret;
}
It's impossible to answer this without seeing the code of tokenize(), of course.
My guess is that there is some undefined behavior in that function, which perhaps corrupts the stack, so that when printf() runs and actually uses some more stack space, things go bad. The thing with undefined behavior is that it's really undefined, anything can happen.
Run the code in Valgrind.
Your tokenize function is broken. Every pointer in your code needs to point at allocated memory, which is not the case here. You get no memory allocated by simply declaring a pointer: a pointer is merely containing an address to memory allocated some place else. Given that you set it to point at "some place else", if you don't, it will point at a random garbage address.
So you need to rewrite that function from scratch. Either pass a pointer to allocated memory as a parameter or allocate memory dynamically inside the function. But before you do, I would strongly recommend that you study arrays and pointers some more, for example by reading this chapter of the C FAQ.
i have following code which use strdup function
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
char source[] = "The Source String ";
int main()
{
char *dest;
if ((dest = _strdup(source)) == NULL)
{
fprintf(stderr, " Error allocation memory. ");
exit(1);
}
printf("The destination = %s\n", dest);
return 0;
}
it successfully says The Source String,but i am interesting in which situation it fails and how good it is usage of it in daily problems?i know that strdup it is determined by
char *strdup (const char *s)
{
char *d = malloc (strlen (s) + 1); // Space for length plus nul
if (d == NULL) return NULL; // No memory
strcpy (d,s); // Copy the characters
return d; // Return the new string
}
if our string is not NULL,is there any chance of failing strdup function?
Yes, if malloc fails to allocate memory and returns NULL.
This could reasonably happen when you're trying to duplicate a very large string, or if your address space is very fragmented and nearly full (so taht malloc can't find a contiguous block of memory to allocate, or in embedded systems where not much memory is available.
The chance of strdup failing is determined by the chance of malloc failing. On modern operating systems with virtual memory, a malloc failure is a very rare thing. The OS may have even killed your entire process before the system gets so low on memory that malloc has to return NULL.
It's not unheard of to run out of memory, if there is a memory leak.
So it's not a bad idea to check for null, print out error message, and maybe even exit at that point.
Note that things like 'printf' won't work (or may not work, but in my experience don't work) if you run out of memory. So you gotta use low-level 'write' or such, and file descriptor you're using (if you're writing to log file), should already be opened.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Pointer to local variable
Can a local variable's memory be accessed outside its scope?
gcc 4.4.4 c89
In main I call a function to pass a line of text to a function. I want to perform some operation on it. However, that would mean that line is of no use. So in my get_string function I copy the contents and return the result. The only problem, is that the memory to that result would be lost and pointing to something unexpected.
I am just wondering how can I pass the result back, without and still keep the ordinal line of data?
Many thanks for any advice,
code snippet from main:
if(fgets(line_data, (size_t)STRING_SIZE, fp) == NULL) {
fprintf(stderr, "WARNING: Text error reading file line number [ %d ]\n", i);
}
if(get_string(line_data) != NULL) {
if(strcmp(get_string(line_data), "END") == 0)
break;
}
else {
fprintf(stderr, "WARNING: Cannot get name of student at line [ %d ]\n", i);
}
/* Fill student info */
strncpy(stud[i].name, line_data, (size_t)STRING_SIZE);
Call this function
char* get_string(char *line_data)
{
char *quote = NULL;
char result[STRING_SIZE] = {0};
strncpy(result, line_data, (size_t)STRING_SIZE);
/* Find last occurance */
if((quote = strrchr(result, '"')) == NULL) {
fprintf(stderr, "Text file incorrectly formatted for this student\n");
return NULL;
}
/* Insert nul in place of the quote */
*quote = '\0';
/* Overwite the first quote by shifting 1 place */
memmove(result - 1, result, strlen(result) + 1);
return result;
}
Just return strdup(result).
It will allocate and copy your string.
However, you have to free the result after using it in the outer function.
You also could take a buffer in input (with its size), and fill it with what you want.
For your direct question - either use malloc(3) and tell the user of the function to de-allocate the return pointer (this is sort of prone to memory leaks since it's so easy to ignore return value in C), or provide the second parameter as a receive buffer:
char* get_string( const char* line_data, char* receive_buf, size_t buf_size );
The third parameter is for the function to know how large the receive buffer is.
Now to your code - the line memmove(result - 1, result, strlen(result) + 1); corrupts your stack.
You want to malloc the memory for result:
char *result; result = malloc(STRING_SIZE);
As you have it, the memory for result exists on the stack and thus only during the time that execution is inside get_string()
You'll also need to free result before returning NULL to prevent a memory leak.
As a rule of thumb, you should never return a pointer to a function's local variable. You know why: once a function returns, the memory allocated for its variables can be reused for something else. The idea to return a pointer to the result buffer is inherently bad.
You should think whether you really need to keep a copy of the quoted string. What if you tested the "END" string before calling get_string? If you need to quote and output data later, it is done easily. Say:
printf("\"%s\"", student_record);
So get_string could actually work in the buffer in place and return the error code (0 for success). Since you know the final result is a smaller nul terminated string, you wouldn't even need a length parameter.
int get_string(char* student_record);
If you really need to keep a copy of the quoted string, then you need to pass another buffer. I'd still return an int to indicate success (0) or failure (say -1).
int get_string( const char* line_data, char* student_record, size_t buf_size );
I personally prefer letting the caller allocate its own buffer. It leaves it a chance to use a fixed length buffer (simpler memory management). Ex:
char student_record[512];
...
if (!get_string(student_record)) {
// error
}