Im having some trouble writing a getstring function, this is what I have so far.
Regards,
V
const char* getstring()
{
char *buffer;
int i = 255;
buffer = (char *)malloc(i*sizeof(char));
*buffer = getchar();
while ( *buffer != '\n' )
{
buffer++;
*buffer = getchar();
}
*buffer = '\0';
const char* _temp = buffer;
return _temp;
}
int main()
{
char* temp = getstring();
for ( ;temp++ ; *temp != '\0')
{
printf("%c", *temp);
}
return 0;
}
You're setting _temp to buffer when the latter points at the terminating '\0' of the string.
Move the line:
const char* _temp = buffer;
to be immediately after the line:
buffer = (char *)malloc(i*sizeof(char));
so that _temp is pointing to the start of the buffer.
You have some other problems:
Don't use the name _temp - names with a leading underscore are reserved;
You need to test that you don't write more than i bytes into the buffer;
You should test for malloc() returning NULL;
You need to test for getchar() returning EOF. This will mean you need to store the getchar() result in a variable of type int before you assign it to *buffer;
As Michael Mrozek points out in a comment, the expressions in your for loop are the wrong way around.
...and as a point of style, sizeof(char) is always 1, so multiplying by it is unnecessary; and casting the result of malloc() is unnecessary in C and considered undesirable (unlike C++, where it is required).
Why not just use
char buffer[255];
scanf("%254s", &buffer);
or
char* buffer = readline("GO GO GO:");
const char* _temp = buffer;
Move the above statement just after the call to malloc
Important:
Free the memory allocated to buffer after its use in main().
free(temp);
You need to keep track of the allocated pointer - the value returned by malloc() - immediately after calling malloc() so you can pass it back to the caller. You should also check for EOF as well as newline - and that requires an int (not a char) to hold the value from getchar(). At minimum!
Related
I've been trying to piece together a function that allows me to create a string out of a given file of unknown length.
What it's supposed to do, is set the size of the output string to a single character, then for each character besides EOF, increment the size of the string by 1 and add the newly read character to it.
void readstring(FILE *f, char *s[])
{
int size = 1;
int c = 0, i = 0;
s = malloc(size*sizeof(char));
while(c != -1)
{
c = fgetc(f);
s[i] = (char)c;
i++;
if(i == size)
{
size++;
s = realloc(s, size*sizeof(char));
}
}
s[i] = '\0';
}
int main()
{
char *in = malloc(2*sizeof(char));
FILE *IN;
IN = fopen("in.txt", "r");
readstring(IN, in);
printf("%s",&in);
fclose(IN);
free(in);
return 0;
}
If you are on a POSIX compliant system (any modern Linux), don't try to reinvent the wheel. Just use getline(). It will do the tricky stuff of managing a growing buffer for you, and return a suitably malloc()'ed string.
You are assigning the result of malloc()/realloc() to s instead of *s. s is purely local to readstring(), *s is the pointer variable that s points to. Pass in the adress of the pointer in main. Correct code looks like this:
void foo(char** out_string) {
*out_string = malloc(...);
}
int main() {
char* string;
foo(&string);
}
Without the address taking & and pointer dereferenciation *, your main() cannot know where readstring() has stored the characters.
You also need to dereference the double pointer when you set the characters of the string: Use (*s)[i] instead of s[i], as s[i] denotes a pointer, not a character.
Also, try running your program with valgrind, and see if you can learn from the errors that it will spew out at you. It's a great tool for debugging memory related problems.
I am writing a program that takes a list of path ( environmental variable), splits the paths and prints it. When compiling it I get a segfault. The following is my output on GDB :
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400eb0 in dest (name=0x7fffffffbce0 "PATH") at executables.c:100
100 dest[i] = malloc(srclen+1);
On valgrind:
==21574== 1 errors in context 2 of 3:
==21574== Use of uninitialised value of size 8
==21574== at 0x400EB0: dest (executables.c:100)
==21574== by 0x400B5B: main (main.c:9)
This is my function:
char** dest(char *name){
int i=0;
char *vp;
const char s[2]=":";
char *token;
char **dest;
name[strlen(name)-1]='\0';
vp=getenv(name);
if(vp == NULL){
exit(1);
}
token =strtok(vp,s);
while( token != NULL ){
size_t srclen = strlen(token);
dest[i] = malloc(srclen+1);
strcpy(dest[i], token);
token = strtok(NULL, s);
i++;
}
dest[i]=NULL;
return dest;
}
And this is my main:
#include "executables.h"
int main(int argc, char **argv){
char *path;
char name[BUFSIZ];
printf("enter name of environment variable:\n");
fgets(name,BUFSIZ,stdin);
char **p=dest(name);
int j=0;
while(p[j]!=NULL){
printf("%s\n",p[j]);
j++;
}
return(0);
}
Use strdup(). Saves steps (accounts for
'\0' too). You have to allocate some memory before hand for the approach you're using. Otherwise you might want a linked list and allocate packets instead of using the array pattern. When you say dest[i] = <ptr value> you're indexing to an offset of unallocated memory and storing something there, so it's a segvio.
#include <string.h>
#define MAXTOKENS 10000
char **get_dest(char *name) {
// Since dest has to be exposed/persist beyond this function all
// need dynamically allocate (malloc()) rather than stack allocate
// of the form of: char *dest[MAXTOKENS].
char *dest = malloc(MAXTOKENS * sizeof (char *)); // <--- need to allocate storage for the pointers
char *vp;
if ((vp = getenv(name)) == NULL)
exit(-1); // -1 is err exit on UNIX, 0 is success
int i = 0;
char *token = strtok(vp, ":");
while (token != NULL) {
dest[i] = strdup(token); // <=== strdup()
token = strtok(NULL, ":");
i++;
}
// dest[i] = NULL; // Why are you setting this to NULL after adding token?
return dest;
}
It's better if main() takes care of passing a proper null-terminated string to the get_dest() function because main is where the finicky fgets() is handled. Generally you want to do things locally where it makes the most sense and is most relevant. If you ever took your get_dest() function and used it somewhere where the strings were not read by fgets() it would just be a wasted step to overwrite the terminator there. So by initializing the char array to zeroes before fgets() you don't have to worry about setting the trailing byte to '\0'.
And finally probably not good to have your function name dest the same name as the variable it returns dest. In some situations having multiple symbols in your program with the same name can get you into trouble.
#include "executables.h"
int main(int argc, char **argv) {
char *path;
char name[BUFSIZ] = { 0 }; // You could initialize it to zero this way
printf("enter name of environment variable:\n");
// bzero(name, BUFSIZ); //... or you could initialize it to zero this way then
fgets(name, BUFSIZ, stdin);
char **p = get_dest(name);
int j = 0;
while(p[j] != NULL) {
printf("%s\n", p[j]);
j++;
free(p[j]); // like malloc(), strdup'd() strings must be free'd when done
}
free(p);
return 0;
}
dest[i] = malloc(srclen + 1);
You need to allocate memory for the pointer to char pointers (dest) as well as each char pointer stored in dest. In the code you provided, neither step is taken.
From the manpage of getenv:
Notes
...
As typically implemented, getenv() returns a pointer to a string
within the environment list. The caller must take care not to modify
this string, since that would change the environment of the process.
Your code violates that rule:
vp=getenv(name);
...
token =strtok(vp,s);
This is an illegal memory write operation.
I've walked through the following code, but I can't find what's wrong. The function getsxnremem() gets a string up to len chars using fgets(), overwrites the newline (if there is one) with a null-terminator, then re-sizes the memory to fit the string. That's the idea anyway.
The following code sometimes works and sometimes crashes. I've had this happen plenty of times in the past and I usually find the problem, but this time it's taking me too long.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned getsxnremem(char **str, unsigned len){
unsigned l, flag = 1;
free(*str);
char *buff;
if ((*str = malloc(len)) == NULL) return 0;
if(fgets(*str, len, stdin) == NULL) { free(*str); return 0; }
l = strlen(*str);
if (l && ((*str)[l-1] == '\n')) { *(str)[l-1] = '\0'; flag = 0; }
if ((buff = realloc(*str, l + flag)) == NULL){ free(*str); return 0; }
*str = buff;
return (l - 1);
}
int main(void){
char *buff = NULL;
unsigned l = getsxnremem(&buff, 256);
printf("%s\n%u chars long.", buff, l);
}
The problem is, you failed to collect the return value of realloc() there.
As per the C11 standard, chapter §7.22.3.5
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. [...]
realloc() resizes the memory and returns a pointer to the new memory. The old memory is to be free()d, considering realloc() is successful.
So,
You need to collect and check the return value of realloc() and test it against NULL to ensure success. Then, reassign it to *str.
NOTE: Please do not use a form like p = realloc(p, newsize); because, then, if realloc() fails, you'll end up losing the actual pointer, too.
If realloc() is successful, you must not free() the old pointer. Calling free() on already free()-d memory invokes undefined behavior.
After that, as rightly mentioned in the other answer by dbush, the usage
{ *(str)[l-1] = '\0'; flag = 0; }
is also wrong. Your required string is represented by *str, not str. As per the operator precedence, The Array subscripting operator ([]) has higher precedence over the dereference (*) operator, so essentially your code looks like
{ * ((str)[l-1]) = '\0'; flag = 0; }
Which is not what you want. So, to honor the operator precedence, you should modify it like
{ (*str)[l-1] = '\0'; flag = 0; }
That said, you should also check for the return value of fgets() to ensure the success before you make use of the destination buffer. As malloc() returns unitialized memory, and in case fgets() fails, you'll end up reading from unitialized memory which will again cause UB.
For your most recent update, you've got your parenthesis in the wrong place.
This:
if (l && ((*str)[l-1] == '\n')) { *(str)[l-1] = '\0'; flag = 0; }
Should be:
if (l && ((*str)[l-1] == '\n')) { (*str)[l-1] = '\0'; flag = 0; }
^---- here
Ok, so I'm a person who usually writes Java/C++, and I've just started getting into writing C. I'm currently writing a lexical analyser, and I can't stand how strings work in C, since I can't perform string arithmetic. So here's my question:
char* buffer = "";
char* line = "hello, world";
int i;
for (i = 0; i < strlen(line); i++) {
buffer += line[i];
}
How can I do that in C? Since the code above isn't valid C, how can I do something like that?
Basically I'm looping though a string line, and I'm trying to append each character to the buffer string.
string literals are immutable in C. Modifying one causes Undefined Behavior.
If you use a char array (your buffer) big enough to hold your characters, you can still modify its content :
#include <stdio.h>
int main(void) {
char * line = "hello, world";
char buffer[32]; // ok, this array is big enough for our operation
int i;
for (i = 0; i < strlen(line) + 1; i++)
{
buffer[i] = line[i];
}
printf("buffer : %s", buffer);
return 0;
}
First off the buffer needs to have or exceed the length of the data being copied to it.
char a[length], b[] = "string";
Then the characters are copied to the buffer.
int i = 0;
while (i < length && b[i] != '\0') { a[i] = b[i]; i++; }
a[i] = '\0';
You can reverse the order if you need to, just start i at the smallest length value among the two strings, and decrement the value instead of increment. You can also use the heap, as others have suggested, ordinate towards an arbitrary or changing value of length. Furthermore, you can change up the snippet with pointers (and to give you a better idea of what is happening):
int i = 0;
char *j = a, *k = b;
while (j - a < length && *k) { *(j++) = *(k++); }
*j = '\0';
Make sure to look up memcpy; and don't forget null terminators (oops).
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
strcpy( buffer, line );
Though in C string literals have types of non-const arrays it is better to declare pointers initialized by string literals with qualifier const:
const char *line = "hello, world";
String literals in C/C++ are immutable.
If you want to append characters then the code can look the following way (each character of line is appended to buffer in a loop)
#include <string.h>
//...
char *line = "hello, world";
char *buffer = ( char * ) malloc( strlen( line ) + 1 );
buffer[0] = '\0';
char *p = Buffer;
for ( size_t i = 0; i < strlen( line ); i++ )
{
*p++ = line[i];
*p = '\0';
}
The general approach is that you find the pointer to the terminating zero substitute it for the target character advance the pointer and appenf the new terminating zero. The source buffer shall be large enough to accomodate one more character.
If you want to append a single character to a string allocated on the heap, here's one way to do it:
size_t length = strlen(buffer);
char *newbuffer = realloc(buffer, length + 2);
if (newbuffer) { // realloc succeeded
buffer = newbuffer;
buffer[length] = newcharacter;
buffer[length + 1] = '\0';
}
else { // realloc failed
// TODO handle error...
free(buffer); // for example
}
However, this is inefficient to do repeatedly in a loop, because you'll be repeatedly calling strlen() on (essentially) the same string, and reallocating the buffer to fit one more character each time.
If you want to be smarter about your reallocations, keep track of the buffer's current allocated capacity separately from the length of the string within it — if you know C++, think of the difference between a std::string object's "size" and its "capacity" — and when it's necessary to reallocate, multiply the buffer's size by a scaling factor (e.g. double it) instead of adding 1, so that the number of reallocations is O(log n) instead of O(n).
This is the sort of thing that a good string class would do in C++. In C, you'll probably want to move this buffer-management stuff into its own module.
The simplest solution, lacking any context, is to do:
char buffer[ strlen(line) + 1 ];
strcpy(buffer, line);
You may be used to using pointers for everything in Java (since non-primitive types in Java are actually more like shared pointers than anything else). However you don't necessarily have to do this in C and it can be a pain if you do.
Maybe a good idea given your background would be to use a counted string object in C, where the string object owns its data. Write struct my_string { char *data; size_t length; } . Write functions for creating, destroying, duplicating, and any other operation you need such as appending a character, or checking the length. (Separate interface from implementation!) A useful addition to this would be to make it allocate 1 more byte than length, so that you can have a function which null-terminates and allows it to be passed to a function that expects a read-only C-style string.
The only real pitfall here is to remember to call a function when you are doing a copy operation, instead of allowing structure assignment to happen. (You can use structure assignment for a move operation of course!)
The asprintf function is very useful for building strings, and is available on GNU-based systems (Linux), or most *BSD based systems. You can do things like:
char *buffer;
if (asprintf(&buffer, "%s: adding some stuff %d - %s", str1, number, str2) < 0) {
fprintf(stderr, "Oops -- out of memory\n");
exit(1); }
printf("created the string \"%s\"\n", buffer);
free(buffer); /* done with it */
Appending is best done with snprintf
Include the stdio.h header
#include <stdio.h>
then
char* buffer;
char line[] = "hello, world";
// Initialise the pointer to an empty string
snprintf(buffer, 1, "%s", "");
for (i = 0; i < strlen(line); ++i) {
snprintf(buffer, sizeof line[i], "%s%s", buffer, line[i]);
}
As you have started the code you have there is different from the question you are asking.
You could have split the line with strtok though.
But I hope my answer clarifies it.
Can anyone explain to me why this isn't working?
#include <stdio.h>
#include <stdlib.h>
char *getline(int lim)
{
char c;
int i;
char *line;
line = malloc(sizeof(char) * lim);
i = 0;
while((c = getchar()) != '\n' && c != EOF && i < lim-1)
{
*line = c;
line++;
i++;
}
*line = '\0';
printf("%s", line);
return line;
}
I'm not worried about the return value right now - just the reason as to why printf("%s", line) isn't working.
Thanks!
EDIT: fixed to line = malloc(sizeof(char) * lim); but it is still not working.
Solution: the address of *line was being incremented throughout the function. When it was passed to printf(), *line pointed to '\0' because that's where its adress was incremented to. Using a temprorary pointer that stored the original address allocated by malloc() to *line and then passing that pointer into printf(), allowed for the function to walk up the pointer.
Because you are only allocating enough space for a single character in this line:
line = malloc(sizeof(char));
And that is getting filled with the \0 before your printf statement.
I'm guessing you want to change this line to:
/* Allocate enough room for 'lim' - 1 characters and a trailing \0 */
line = malloc(sizeof(char) * lim);
Or even better:
char *line, *tmp;
tmp = line = malloc(sizeof(char) * lim);
And then use tmp in all of your pointer math, this way line will still point to the start of your string.
And I know it's early in your development, but you'll want to make sure you free() the memory that you malloc().
Here is a working version of your function including my suggested changes:
#include <stdio.h>
#include <stdlib.h>
char *getline(int lim)
{
char c;
int i;
char *line, *tmp;
tmp = line = malloc(sizeof(char) * lim);
i = 0;
/* NOTE: 'i' is completely redundant as you can use 'tmp',
* 'line,' and 'lim' to determine if you are going to
* overflow your buffer */
while((c = getchar()) != '\n' && c != EOF && i < lim-1)
{
*tmp = c;
tmp++;
i++;
}
*tmp = '\0';
printf("%s", line);
return line;
}
It looks like you're printing a zero-length string.
*line = '\0';
printf("%s", line);
I presume that you want to store what line was originally (as returned from malloc) and print that.
Everyone has covered these points already, but here is the whole thing all put together:
Edit: Improved the code a little
#include <stdio.h>
#include <stdlib.h>
char *getline(int lim)
{
char *result = malloc(sizeof(char) * lim); // allocate result buffer
int i = 0;
char c;
char *line = result;
while((c = getchar()) != '\n' && c != EOF && i < lim-1)
{
*line = c;
line++;
i++;
}
*line = '\0';
printf("%s", result); // print the result
return result; // return the result buffer (remember to free() it later)
}
You seem to have allocated enough room for only one character. Did you mean the following instead:
line = malloc(lim * sizeof(char));
Also, you don't want to change line after reading each character. Use the following block for your while-loop instead:
*(line + i) = c;
i++;
And finally, to null-terminate the string, use:
*(line + i) = '\0';
Updated-it was a simple typo mistake,but you didn't have to vote me down on it
instead of
char *line= malloc(sizeof(char));
try
int space= //number of how many characters you need on the line
char *line= malloc(sizeof(char)*space);
sorry I meant
char *line= malloc( sizeof(char)*lim)
You're also overwriting memory you don't own. You're malloc'ing one character, setting *line to c, and then incrementing line and repeating.
You need to understand the concept of pointer, and how it's different from a buffer.
In your code, you treat "line" as pointer and buffer at the same time.
You are Making mistake on two points (but you can say same mistake or two ,its upto you). first your pointer should be incremented like
*(line+i) = c;
Due to this reason when you set the Null char at the end of loop you are actually saying the compiler to point the pointer to only this poistion.Hence the pointer is only pointing at Null String not the whole string. As it was constantly being moved in each step of the loop.
So when you tried to print that the pointer has nothing to print. So if you change your statement inside the loop for pointer and assign the value to an express address rather then actually moving the pointer then you problem will be solved.
Note. If you change that line then you also need to modify your Null terminator assignment like this;
*(line+limit) = '\0';