I have the following code:
char* get_line(char *buffer, char *line, char *partialLine) {
int i = 0;
while (buffer[i] != '\n' && buffer[i] != '\0') i++;
if (buffer[i] == '\n') {
line = malloc(sizeof(char) * (i+2));
memcpy(line,buffer,i+1);
line[i+1] = '\0';
char *temp = append(partialLine,line);
if (partialLine) {
free(partialLine);
partialLine = NULL;
}
free(line);
line = temp;
buffer = pop_buffer(buffer,i+1);
return buffer;
}
}
I want the function to fill in "line", so that when I return I have the value of temp in "line". At the moment, when debugging I see line gets the right value after the line = temp instruction. I want this to be kept after the return.
I think I need to pass a pointer to a pointer making the function prototype char* get_line(char *buf, char **line, char *partialLine);, but I'm only just getting to grips with regular pointers. What is the best way to solve this type of problem? Am I on the right tracks?
You already give the better part of the answer. Just one nitpick:
At the moment, when debugging I see line gets the right value after the line = temp instruction. I want this to be kept after the return.
That's impossible. C function calls pass arguments by value, so the line inside the function is a different variable than that outside and doesn't exist any more once the function exits. That's why you need to pass a pointer to a pointer here.
The bit of information you seem to be missing: A pointer isn't that much different from a normal variable, it stores a value. The only difference is that the value stored is the address of some other variable. Of course, you can handle a pointer to a pointer just the same way as a pointer to anything, and you can dereference twice. In your case, the simplest way would be to just change the parameter type and then replace every line by *line (which would access the pointer line points to).
I think I need to pass a pointer to a pointer
Yes you need a douple pointer, so that the value of the pointer (where the pointer points to) persists after the function terminates.
If you don't do that, you will have a memory leak, since the memory allocated to line, will not be referencable.
Related
I have a struct like this:
typedef struct TEXT {
char *text;
struct TEXT *next;
} TEXT;
and in some function I have something like:
TEXT *line = (TEXT *) malloc(sizeof(TEXT));
line->text = NULL; // was "\0" before
char current = getchar();
while (current != '\n') {
AddChar(&(line->text), current); // Was AddChar(line->text, current);
current = getchar();
}
and the AddChar function was something like this:
void AddChar(char **text, char c) { //was char *text
*text = (char *) realloc(*text, strlen(*text)+2); //was text = ...
char char_array[2] = {c, '\0');
strcat(*text, char_array); //was strcat(text, char_array);
}
And unfortunately, program crashed.
As far as I understand, turns out that strlen can't figure out that if text == NULL, length should be 0...
Anyway, with this AddChar function, everything works:
void AddChar(char **text, char c) {
if (*text == NULL) {
*text = (char *) malloc(2);
(*text)[0] = c;
(*text)[1] = '\0';
}
else {
*text= (char *) realloc(*text, sizeof(*text)+2);
char char_array[2] = { c , '\0' };
strcat(*text, char_array);
}
}
.
.
I also had a problem with
void AddChar(char *text, char c) {
text = "something";
}
not changing line->text, but changing *text to **text fixed that.
Only a NULL initialized pointer or a pointer returned by malloc family functions (malloc, calloc and realloc) can be passed to another malloc family functions. line->text is initialized with a string literal "\0" and therefore line->text can't be passed to realloc function.
Also note that you can't modify a string literal.
One of the problems is that you try to reallocate something you didn't allocate yourself.
The other problem is that you try to reassign a local variable inside the AddChar function and expect it to have any effect on the calling function, which it doesn't.
When passing an argument to a function it's passed by value which means that the value is copied into the local argument variable in the function, that variables local inside the function only, and changing that will not change the original variable used when calling the function. What you need is passing the argument by reference which C does not support, but you can emulate it by using a pointer. In your case by using a pointer to the pointer using the address-of operator &.
As far as I understand, turns out that strlen can't figure out that if text == NULL, length should be 0...
On the other hand, section 7.1.4, paragraph 1 the C standard states that NULL is an invalid value for strlen:
If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined.
While we're on the subject of invalid values, strcat will expect that both of it's arguments contain strings. What this means is they must both contain a sequence of characters that terminates at the first '\0' character. You've demonstrated this by adding a '\0' character to the array representing your second argument, but can you prove that there's a '\0' character in the array representing your first argument?
*text = (char *) realloc(*text, strlen(*text)+2);
Null pointer aside, anything of the pattern x = realloc(x, ...); is wrong. For more information on that, see Proper usage of realloc.
*text= (char *) realloc(*text, sizeof(*text)+2);
As for this, it may seem to work for you at first, but I can assure you that it's broken. It is important to realise that you intend to allocate characters, not char *s, so sizeof(*text) (which is sizeof (char *)) is invalid here.
Typically this would allocate between 6 and 10 characters. If you overflow that buffer, the behaviour is undefined. What does that mean? Well, it'd be a paradox to define the undefined behaviour, but typical consequences of undefined behaviour range from "nothing at all" and "it works fine" for some systems to "segfaults" and "heartbleed" for other systems; it's not portable nonetheless.
I also had a problem with
void AddChar(char *text, char c) {
text = "something";
}
not changing line->text, but changing *text to **text fixed that.
I'm curious as to what you think AddChar(NULL, 42); would do, when AddChar is defined like this... Do you think it'd assign the null pointer constant that is NULL to the string literal that is "something"?
C doesn't support pass-by-reference. When you pass an argument (for example, a pointer, such as an array expressions implicitly converted to a pointer), what happens behind the scenes is a new variable is declared (in this case, the argument char *text is a variable) and a copy of that value (in this case, a pointer) is assigned to that variable. This is known as "pass-by-value".
When you pass a pointer, and modify the thing that the pointer is pointing at (e.g. by using *pointer = ... or pointer[x] = ...), you're emulating pass-by-reference; you're modifying an object that the variable refers to, rather than modifying the variable itself.
Ohh, and one more thing, despite the link to the "Proper usage of realloc", you should not cast the return value of malloc (or `realloc) in C.
Disagree with OP's final "Anyway, with this AddChar function, everything works:" as that does not allocate sufficient memory.
Suggest
void AddChar(char **text, char c) {
size_t length = strlen(*text);
*text = realloc(*text, length + 2); // proper size calculation
if (*text == NULL) { // check if successful
abort(-1);
}
(*text)[length] = c;
(*text)[length + 1] = '\0';
}
EDIT Thanks Joachim. Used the function signature as you pointed out, and passed the address of my strings in. Inside the function, I did the realloc() using *currBuffer, and placed the values into the string using (*currBuffer)[lenBuffer] .... :)
Passing the value of the pointer by-value was working fine until realloc() decided it needed to assign a different memory space. I must have only checked the pointer's address on entry to the function and not noticed that it changed later.
.........................................................
My program gets user input from stdin, and then parses it into tokens.
As I read each character entered, I make a call addChrToLine(userInput, charCount-1, (char) inputChr); to void addChrToLine (char *currBuffer, int lenBuffer, char inputChr) where I add the new character to the string.
Later, when I'm parsing the input, I using the same addChrToLine function while I build the parsed string. However, when the parser calls the function, it gives an error on the 25th character.
My code to read user input:
char * userInput = malloc(sizeof(char));
int charCount = 0;
LOOP
if (!(inputChr == LF)) { // ignore the LF character as it denotes end of input line
charCount++;
addChrToLine(userInput, charCount-1, (char) inputChr);
continue;
}
My code to add the char to the current line:
void addChrToLine (char *currBuffer, int lenBuffer, char inputChr) {
currBuffer = realloc(currBuffer, sizeof(char) * (lenBuffer +2));
if (currBuffer == NULL) {
perror(NULL);
exit(ENOMEM);
}
currBuffer[lenBuffer] = (char) inputChr;
currBuffer[lenBuffer+1] = (char) '\0';
}
My code where I parse the input:
char * parsedCmd = malloc(sizeof(char));
int ndxOutput = 0;
Switch statement inside a loop to handle variety of cases
char currChr = command[ndxInput];
if (addChr) {
ndxOutput++;
addChrToLine2(parsedCmd,ndxOutput-1,currChr);
// parsedCmd = realloc(parsedCmd, sizeof(char) * (ndxOutput+2));
// parsedCmd[ndxOutput] = command[ndxInput];
// parsedCmd[ndxOutput+1] = '\0';
addChr = FALSE;
}
For input = alphabet, eg "abcde...yz", the user input string is read correctly and everything is as expected on entry to the parsing stage.
While parsing "a" to "w", the char * pointer parsedCmd correctly shows the contents. When I pass "x" to addChrToLine, it correctly places the character and adjusts the position of the null. BUT on return to the parser's next line, parsedCmd shows a null string.
The memory location of the pointer is the same during addChrToLine and after the call as it was before (so realloc() hasn't moved my data). However, when I look at the memory, the first 8 characters of the string are now 0x00 and the the string is intact inclusively after "i", all the way to "w" (no "x").
When the code tries to add "y" to parsedCmd, the realloc() statement issues an error double free or corruption (fasttop): 0x0000000000605160 ***
I'm a bit puzzled why this is happening.
And what puzzles me even more is that if I comment out the call to addChrToLine and uncomment the next three lines (which do the same thing), there is no error and the parser finishes fine.
Clearly I'm doing something wrong with my pointers or memory allocations, but I can't see what's wrong.
Can someone please help me understand what's going on here?
The error is that C passes arguments by value, meaning they are copied. So when you assign to currBuffer in the function you only assign (and modify) the local copy. When the function returns, the old userInput is still unchanged.
To fix this you have to pass the pointer by reference which is done with pointers in C, that is you have to pass a pointer to the pointer:
void addChrToLine (char **currBuffer, int lenBuffer, char inputChr);
...
addChrToLine(&userInput, charCount-1, (char) inputChr);
Another solution is to return the new pointer:
char *addChrToLine (char *currBuffer, int lenBuffer, char inputChr)
{
...
return currBuffer;
}
...
userInput = addChrToLine(userInput, charCount-1, (char) inputChr);
struct integer
{
int len;
char* str;
int* arr;
}int1, int2;
int main(void) {
printf("Please enter 1st number\n");
int1.str= str_input();
int1.len=chars_read-1;
int1.arr= func2(int1.len, int1.str);
printf(("\%c\n"), *int1.str);
printf("Please enter 2nd number\n");
int2.str = str_input();
int2.len=chars_read-1;
printf(("\n %c\n"), *int1.str );
int2.arr= func2(int2.len, int2.str);
if the input is 4363 and 78596 , the output is 4 and 7 respectively.
The output is not 4 and 4. Given that both are different objects, shouldn't both have different memory allocation?
Please note: this is NOT a typographical error. I have used the same *int1.str both times. the problem is that although I have made no changes in it, its value is changing. How?
I do not think that str_input() can make a difference.
char* str_input(void) {
char cur_char;
char* input_ptr = (char*)malloc(LINE_LEN * sizeof(char));
char input_string[LINE_LEN];
//while ((cur_char = getchar()) != '\n' && cur_char<='9' && cur_char>='0' && chars_read < 10000)
for(chars_read=1; chars_read<10000; chars_read++)
{
scanf("%c", &cur_char);
if(cur_char!='\n' && cur_char<='9' && cur_char>='0')
{
input_string[chars_read-1]= cur_char;
printf("%c\n", input_string[chars_read-1]);
}
else{
break;
}
}
input_string[chars_read] = '\n';
input_ptr = &input_string[0]; /* sets pointer to address of 0th index */
return input_ptr;
}
//chars_read is a global variable.
Thanks in advance.
you have printed the same variable, *int1.str
It will be helpful have the source code of str_input(), but it's probably that it returns a pointer to the same buffer in each call, so the second call to str_input() updates also the target of int1.str (beacuse it's pointing to the same char* than int2.str)
As noted elsewhere, both of the printf calls in your question pass *int1.str to printf.
However, if that is merely a typographical error in your question, and the second printf call passes *int2.str, then most likely the problem is that str_input returns the address of a fixed buffer (with static or, worse, automatic storage duration). Instead, str_input should use malloc or strdup to allocate new memory for each string and should return a pointer to that. Then the caller should free the memory.
Alternatively, str_input may be changed to accept a buffer and size passed to it by the caller, and the caller will have the responsibility of providing a different buffer for each call.
About the newly posted code
The code for str_input contains this line:
char* input_ptr = (char*)malloc(LINE_LEN * sizeof(char));
That declares input_ptr to be a char * and calls malloc to get space. Then input_ptr is set to contain the address of that space. Later, str_input contains this line:
input_ptr = &input_string[0];
That line completely ignores the prior value of input_ptr and overwrites it with the address of input_string[0]. So the address returned by malloc is gone. The str_input function returns the address of input_string[0] each time it is called. This is wrong. str_input must return the address of the allocated space each time.
Typically, a routine like this would use input_ptr throughout, doing its work in the char array at that address. It would not use a separate array, input_string, for its work. So delete the definition of input_string and change str_input to do all its work in the space pointed to by input_ptr.
Also, do not set the size of the buffer to LINE_LEN in one place but limit the number of characters in it with chars_read < 10000. Use the same limit in all places. Also allow one byte for a null character at the end (unless you are very careful never to perform any operation that requires a null byte at the end).
I need a function/method that will take in a char array and set it to a string read from stdin. It needs to return the last character read as its return type, so I can determine if it reached the end of a line or the end of file marker.
here is what I have so far, and I kind of based it off of code from here
UPDATE: I changed it, but now it just crashes upon hitting enter after text. I know this way is inefficient, and char is not the best for EOF check, but for now I am just trying to get it to return the string. I need it to do it in this fashion and no other fashion. I need the string to be the exact length of the line, and to return a value that is either the newline or EOF int which I believe can still be used in a char value.
This program is in C not C++
char getLine(char **line);
int main(int argc, char *argv[])
{
char *line;
char returnVal = 0;
returnVal = getLine(&line);
printf("%s", line);
free(line);
system("pause");
return 0;
}
char getLine(char **line) {
unsigned int lengthAdder = 1, counter = 0, size = 0;
char charRead = 0;
*line = malloc(lengthAdder);
while((charRead = getc(stdin)) != EOF && charRead != '\n')
{
*line[counter++] = charRead;
*line = realloc(*line, counter);
}
*line[counter] = '\0';
return charRead;
}
Thank you for any help in advance!
You're assigning the result of malloc() to a local copy of line, so after the getLine() function returns it's not modified (albeit you think it is). What you have to do is either return it (as opposed to use an output parameter) or pass its address (pass it 'by reference'):
void getLine(char **line)
{
*line = malloc(length);
// etc.
}
and call it like this:
char *line;
getLine(&line);
Your key problem is that line pointer value does not propagate out of the getLine() function. The solution is to pass pointer to the line pointer to the function as a parameter instead - calling it like getLine(&line); while the function would be defined as taking parameter char **line. In the function, on all places where you now work with line, you would work with *line instead, i.e. dereferencing the pointer to a pointer and working with the value of the variable in main() where the pointer leads. Hope this is not too confusing. :-) Try to draw it on a piece of paper.
(A tricky part - you must change line[counter] to (*line)[counter] because you first need to dereference the pointer to the string, and only then to access a specific character in the string.)
There is a couple of other problems with your code:
You use char as the type for charRead. However, the EOF constant cannot be represented using char, you need to use int - both as the type of charRead and return value of getLine(), so that you can actually distringuish between a newline and end of file.
You forgot to return the last char read from your getLine() function. :-)
You are reallocating the buffer after each character addition. This is not terribly efficient and therefore is a rather ugly programming practice. It is not too difficult to use another variable to track the amount of space allocated and then (i) start with allocating a reasonable chunk of memory, e.g. 64 bytes, so that ideally you will never reallocate (ii) enlarge the allocation only if you need to based on comparing the counter and your allocation size tracker. Two reallocation strategies are common - either doubling the size of the allocation or increasing the allocation by a fixed step.
The way you use realloc is not correct. If it returns NULL then the memory block will be lost.
It is better to use realloc in this way:
char *tmp;
...
tmp = realloc(line, counter);
if(tmp == NULL)
ERROR, TRY TO SOLVE IT
line = tmp;
I've been studying C, and I decided to practice using my knowledge by creating some functions to manipulate strings. I wrote a string reverser function, and a main function that asks for user input, sends it through stringreverse(), and prints the results.
Basically I just want to understand how my function works. When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? Basically like saying &tempstr[0], right?
I guess answering this question would tell me: Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now? I want to know whether I'm sending a duplicate of the array tempstr, or a memory address.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char* stringreverse(char* tempstr, char* returnptr);
printf("\nEnter a string:\n\t");
char tempstr[1024];
gets(tempstr);
char *revstr = stringreverse(tempstr, revstr); //Assigns revstr the address of the first character of the reversed string.
printf("\nReversed string:\n"
"\t%s\n", revstr);
main();
return 0;
}
char* stringreverse(char* tempstr, char* returnptr)
{
char revstr[1024] = {0};
int i, j = 0;
for (i = strlen(tempstr) - 1; i >= 0; i--, j++)
{
revstr[j] = tempstr[i]; //string reverse algorithm
}
returnptr = &revstr[0];
return returnptr;
}
Thanks for your time. Any other critiques would be helpful . . only a few weeks into programming :P
EDIT: Thanks to all the answers, I figured it out. Here's my solution for anyone wondering:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void stringreverse(char* s);
int main(void)
{
printf("\nEnter a string:\n\t");
char userinput[1024] = {0}; //Need to learn how to use malloc() xD
gets(userinput);
stringreverse(userinput);
printf("\nReversed string:\n"
"\t%s\n", userinput);
main();
return 0;
}
void stringreverse(char* s)
{
int i, j = 0;
char scopy[1024]; //Update to dynamic buffer
strcpy(scopy, s);
for (i = strlen(s) - 1; i >= 0; i--, j++)
{
*(s + j) = scopy[i];
}
}
First, a detail:
int main()
{
char* stringreverse(char* tempstr, char* returnptr);
That prototype should go outside main(), like this:
char* stringreverse(char* tempstr, char* returnptr);
int main()
{
As to your main question: the variable tempstr is a char*, i.e. the address of a character. If you use C's index notation, like tempstr[i], that's essentially the same as *(tempstr + i). The same is true of revstr, except that in that case you're returning the address of a block of memory that's about to be clobbered when the array it points to goes out of scope. You've got the right idea in passing in the address of some memory into which to write the reversed string, but you're not actually copying the data into the memory pointed to by that block. Also, the line:
returnptr = &revstr[0];
Doesn't do what you think. You can't assign a new pointer to returnptr; if you really want to modify returnptr, you'll need to pass in its address, so the parameter would be specified char** returnptr. But don't do that: instead, create a block in your main() that will receive the reversed string, and pass its address in the returnptr parameter. Then, use that block rather than the temporary one you're using now in stringreverse().
Basically I just want to understand how my function works.
One problem you have is that you are using revstr without initializing it or allocating memory for it. This is undefined behavior since you are writing into memory doesn't belong to you. It may appear to work, but in fact what you have is a bug and can produce unexpected results at any time.
When I call it with 'tempstr' as the first param, is that to be understood as the address of the first element in the array? Basically like saying &tempstr[0], right?
Yes. When arrays are passed as arguments to a function, they are treated as regular pointers, pointing to the first element in the array. There is no difference if you assigned &temp[0] to a char* before passing it to stringreverser, because that's what the compiler is doing for you anyway.
The only time you will see a difference between arrays and pointers being passed to functions is in C++ when you start learning about templates and template specialization. But this question is C, so I just thought I'd throw that out there.
When I call it with 'tempstr' as the first param, is that to be understood as the
address of the first element in the array? Basically like saying &tempstr[0],
right?
char tempstr[1024];
tempstr is an array of characters. When passed tempstr to a function, it decays to a pointer pointing to first element of tempstr. So, its basically same as sending &tempstr[0].
Would there be any difference if I assigned a char* pointer to my tempstr array and then sent that to stringreverse() as the first param, versus how I'm doing it now?
No difference. You might do -
char* pointer = tempstr ; // And can pass pointer
char *revstr = stringreverse(tempstr, revstr);
First right side expression's is evaluavated and the return value is assigned to revstr. But what is revstr that is being passed. Program should allocate memory for it.
char revstr[1024] ;
char *retValue = stringreverse(tempstr, revstr) ;
// ^^^^^^ changed to be different.
Now, when passing tempstr and revstr, they decayed to pointers pointing to their respective first indexes. In that case why this would go wrong -
revstr = stringreverse(tempstr, revstr) ;
Just because arrays are not pointers. char* is different from char[]. Hope it helps !
In response to your question about whether the thing passed to the function is an array or a pointer, the relevant part of the C99 standard (6.3.2.1/3) states:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
So yes, other than the introduction of another explicit variable, the following two lines are equivalent:
char x[] = "abc"; fn (x);
char x[] = "abc"; char *px = &(x[0]); fn (px);
As to a critique, I'd like to raise the following.
While legal, I find it incongruous to have function prototypes (such as stringreverse) anywhere other than at file level. In fact, I tend to order my functions so that they're not usually necessary, making one less place where you have to change it, should the arguments or return type need to be changed. That would entail, in this case, placing stringreverse before main.
Don't ever use gets in a real program.. It's unprotectable against buffer overflows. At a minimum, use fgets which can be protected, or use a decent input function such as the one found here.
You cannot create a local variable within stringreverse and pass back the address of it. That's undefined behaviour. Once that function returns, that variable is gone and you're most likely pointing to whatever happens to replace it on the stack the next time you call a function.
There's no need to pass in the revstr variable either. If it were a pointer with backing memory (i.e., had space allocated for it), that would be fine but then there would be no need to return it. In that case you would allocate both in the caller:
char tempstr[1024];
char revstr[1024];
stringreverse (tempstr, revstr); // Note no return value needed
// since you're manipulating revstr directly.
You should also try to avoid magic numbers like 1024. Better to have lines like:
#define BUFFSZ 1024
char tempstr[BUFFSZ];
so that you only need to change it in one place if you ever need a new value (that becomes particularly important if you have lots of 1024 numbers with different meanings - global search and replace will be your enemy in that case rather than your friend).
In order to make you function more adaptable, you may want to consider allowing it to handle any length. You can do that by passing both buffers in, or by using malloc to dynamically allocate a buffer for you, something like:
char *reversestring (char *src) {
char *dst = malloc (strlen (src) + 1);
if (dst != NULL) {
// copy characters in reverse order.
}
return dst;
}
This puts the responsibility for freeing that memory on the caller but that's a well-worn way of doing things.
You should probably use one of the two canonical forms for main:
int main (int argc, char *argv[]);
int main (void);
It's also a particularly bad idea to call main from anywhere. While that may look like a nifty way to get an infinite loop, it almost certainly will end up chewing up your stack space :-)
All in all, this is probably the function I'd initially write. It allows the user to populate their own buffer if they want, or to specify they don't have one, in which case one will be created for them:
char *revstr (char *src, char *dst) {
// Cache size in case compiler not smart enough to do so.
// Then create destination buffer if none provided.
size_t sz = strlen (src);
if (dst == NULL) dst = malloc (sz + 1);
// Assuming buffer available, copy string.
if (dst != NULL) {
// Run dst end to start, null terminator first.
dst += sz; *dst = '\0';
// Copy character by character until null terminator in src.
// We end up with dst set to original correct value.
while (*src != '\0')
*--dst = *src++;
}
// Return reversed string (possibly NULL if malloc failed).
return dst;
}
In your stringreverse() function, you are returning the address of a local variable (revstr). This is undefined behaviour and is very bad. Your program may appear to work right now, but it will suddenly fail sometime in the future for reasons that are not obvious.
You have two general choices:
Have stringreverse() allocate memory for the returned string, and leave it up to the caller to free it.
Have the caller preallocate space for the returned string, and tell stringreverse() where it is and how big it is.