Pointers and strings, failing on EOF - c

Code rewritten to be more clear
void indexe(char * line, unsigned ref) {
unsigned i = 0;
char word[128]; //(1)
//char * word; //(2)
while(*line) {
if(isalpha(*line))
word[i++] = *line; //(1)
//*word++ = *line; //(2)
*line++;
}
}
int main(int argc, const char * argv[]) {
char line[128];
FILE * f = fopen(argv[1], "r");
unsigned x = 0;
while (fgets(line, 128, f)){
indexe(line, ++x);
}
fclose(f);
return 0;
}
Hello, I have tried the two above combinations:
word[] -> word[i++]
*word -> *word++
The whole thing works flawlessly, except when reaching EOF, in which case the pointers syntax fails with a segmentation fault, but not the array syntax.
I am a C total beginner, could someone explain in beginner terms what happens here, and maybe suggest a solution to fix the pointer syntax? (But mostly explain, please)

This version, as posted, is fine:
void indexe(char * line, unsigned ref) {
unsigned i = 0;
char word[128]; //(1)
//char * word; //(2)
while(*line) {
if(isalpha(*line))
word[i++] = *line; //(1)
//*word++ = *line; //(2)
*line++;
}
}
However, if you recomment the code to use the lines marked //(2) instead, you have this:
char * word; //(2
*word++ = *line; //(2)
Which is simply a case of writing to a pointer you haven't initialised with allocated memory. This isn't allowed. You need to keep it as an array, or use something like malloc to reserve storage. If you want to write the function without using an array at all, the code would be:
char *word = malloc(128); // reserve 128 bytes
if (word == NULL) { // these checks are important
fprintf(stderr, "Cannot allocate memory!");
exit(EXIT_FAILURE);
}
...other stuff...
free(word);
Note also that:
*line++;
increments line, but dereferences it (before it's incremented) for no reason.

The pointer syntax fails because you have defined a pointer char * word; but you have not set it to point to any data - the memory you are pointing to can be anywhere. So, when you execute the following statement:
*word++ = *line;
You are storing the value pointed to by line in the value pointed to by word. Unfortunately, you do not know where word is pointing. As #teppic pointed out, you're writing to a pointer that has not been initialized to allocated memory.
You could malloc that memory as #teppic previously pointed out. You could also do the following:
char reserve[128];
char * word = reserve; // could also have used &reserve[0]
Hope that helps!

Related

How to copy char array to a double pointer in C

Im writing a fairly simple program to read a file line by line and store it into an array of lines, my program compiles fine but it crashes everytime I run it.
This is my code:
#include <stdio.h>
#include <string.h>
#define LINESIZE 512
typedef struct {
char **data;
size_t nused;
} lines_t;
lines_t readlines(FILE *fp);
int main(int argc,char* argv[]) {
FILE *fp;
(void)argc;
if((fp = fopen(argv[1],"r+")) == 0) {
perror("fopen");
}
readlines(fp);
return 0;
}
lines_t readlines(FILE *fp) {
lines_t line_data;
char line[LINESIZE];
char temp[20];
int num_lines = 0;
(*line_data.data) = (char *)malloc(LINESIZE);
while(fgets(line,LINESIZE,fp)) {
sscanf(line,"%s\n",temp);
strcpy(line_data.data[num_lines], temp); /* Program crashes here */
num_lines++;
}
return line_data;
}
The line where I try to copy my array is giving me trouble, So my question is, How do I copy my character array temp into the char **data inside struct lines_t if I am not doing it right?
You are dereferencing an invalid pointer here:
(*line_data.data) = (char *)malloc(15);
line_data.data is a char **. You are trying to deference it but it is not yet set to any meaningful value. You need to allocate memory for line_data.data before you allocate memory for *line_data.data.
(char *)malloc(15) is particularly suspicious also. Where does 15 come from and what are you actually allocating memory for? Casting the result of malloc is generally considered bad practice, and in your case, rightly so, because malloc is declared in stdlib.h and you aren't including that header. If you want to allocate enough space to hold 15 char *, then use malloc(15 * sizeof(char *)) or alternatively, malloc(15 * sizeof(*line_data.data)) (here it is safe to use *line_data.data even if it doesn't point to anything, because sizeof does not evaluate its operand).
You can try malloc line_data.data before strcpy.
lines_t readlines(FILE *fp) {
lines_t line_data;
line_data.data = malloc(LINESIZE);
*line_data.data = malloc(LINESIZE);
return line_data;
}
You first need to allocate memory.
You need to allocate both for the array of strings and string array. Here you need to have an upper limit while calling a malloc.
line_data = malloc(10* sizeof(char*)); // for example here the upper limit is 10
while(fgets(line,LINESIZE,fp)) {
sscanf(line,"%s\n",temp);
line_data.data[num_lines] = malloc(sizeof(char) * (strlen(temp)+1));
strcpy(line_data.data[num_lines], temp); /* Program crashes here */
num_lines++;
}

Issue with assignment from incompatible pointer type

Hey so im trying to attempt to read in a file, store it in a hash and then copy it. However i get the incompatible pointer type
struct hash_struct {
int id;
char name[BUFFER_SIZE]; /* key (string WITHIN the structure */
UT_hash_handle hh; /* makes this structure hashable */
};
int main(int argc, char *argv[] )
{
char *lines[80];
FILE* fp = fopen("file.txt","r");
if(fgets(*lines, BUFFER_SIZE, fp) != NULL)
{
puts(*lines);
// do something
}
fclose(fp);
const char **n;
char *names[1024];
strcpy(*names, *lines);
struct hash_struct *s, *tmp, *users = NULL;
int i=0;
for (n = names; *n != NULL; n++)
{
s = (struct hash_struct*)malloc(sizeof(struct hash_struct));
strncpy(s->name, *n,10);
s->id = i++;
HASH_ADD_STR( users, name, s );
}
HASH_FIND_STR( users, "joe", s);
if (s) printf("joe's id is %d\n", s->id);
printf("Hash has %d entries\n",HASH_COUNT(users));
/* free the hash table contents */
HASH_ITER(hh, users, s, tmp) {
HASH_DEL(users, s);
free(s);
}
return 0;
}
The code works when i initialize const char **n, *names = {array elements here};
But it doesnt work with the code i have. Please help.
lines is declared to be an array of char pointers, but doesn't allocate any space for the strings they point to. In your working version, the compiler took care of allocating space for each string.
Plus, you can't use strcpy to copy an array of 80 pointers to an array of 1024 pointers.
Instead, each line you read in needs space to be allocated for it to be read into; then the addresses of each of those can be assigned to an element of names. In fact, as #BLUEPIXY suggests, line should be an array of 80 chars, not an array of 80 pointers-to-chars. Or you could just malloc the space for each new line, and put the address of that line into names.

Parsing a string with multi-char delimiter and got junk data along with the result in the second call in c

I have writen a code to split the string with multiple char delimiter.
It is working fine for first time of calling to this function
but i calling it second time it retuns the correct word with some unwanted symbol.
I think this problem occurs because of not clearing the buffer.I have tried a lot but cant solve this. please help me to solve this problem.
char **split(char *phrase, char *delimiter) {
int i = 0;
char **arraylist= malloc(10 *sizeof(char *));
char *loc1=NULL;
char *loc=NULL;
loc1 = phrase;
while (loc1 != NULL) {
loc = strstr(loc1, delimiter);
if (loc == NULL) {
arraylist[i]=malloc(sizeof(loc1));
arraylist[i]=loc1;
break;
}
char *buf = malloc(sizeof(char) * 256); // memory for 256 char
int length = strlen(delimiter);
strncpy(buf, loc1, loc-loc1);
arraylist[i]=malloc(sizeof(buf));
arraylist[i]=buf;
i++;
loc = loc+length;
loc1 = loc;
}
return arraylist;
}
called this function first time
char **splitdetails = split("100000000<delimit>0<delimit>hellooo" , "<delimit>");
It gives
splitdetails[0]=100000000
splitdetails[1]=0
splitdetails[2]=hellooo
but i called this second time
char **splitdetails = split("20000000<delimit>10<delimit>testing" , "<delimit>");
splitdetails[0]=20000000��������������������������
splitdetails[1]=10����
splitdetails[2]=testing
Update:-
thanks to #fatelerror. i have change my code as
char** split(char *phrase, char *delimiter) {
int i = 0;
char **arraylist = malloc(10 *sizeof(char *));
char *loc1=NULL;
char *loc=NULL;
loc1 = phrase;
while (loc1 != NULL) {
loc = strstr(loc1, delimiter);
if (loc == NULL) {
arraylist[i]=malloc(strlen(loc1) + 1);
strcpy(arraylist[i], loc1);
break;
}
char *buf = malloc(sizeof(char) * 256); // memory for 256 char
int length = strlen(delimiter);
strncpy(buf, loc1, loc-loc1);
buf[loc - loc1] = '\0';
arraylist[i]=malloc(strlen(buf));
strcpy(arraylist[i], buf);
i++;
loc = loc+length;
loc1 = loc;
}
}
In the caller function, i used it as
char *id
char **splitdetails = split("20000000<delimit>10<delimit>testing" , "<delimit>");
id = splitdetails[0];
//some works done with id
//free the split details with this code.
for(int i=0;i<3;i++) {
free(domaindetails[i]);
}free(domaindetails);
domaindetails=NULL;
then i called the same for the second as,
char **splitdetails1= split("10000000<delimit>1000<delimit>testing1" , "<delimit>");
it makes error and i can't free the function.
thanks in advance.
Your problem boils down to three basic things:
sizeof is not strlen()
Assignment doesn't copy strings in C.
strncpy() doesn't always nul-terminate strings.
So, when you say something like:
arraylist[i]=malloc(sizeof(loc1));
arraylist[i]=loc1;
thisdoes not copy the string. The first one allocates the size of loc1, which is a char *. In other words, you allocated the size of a pointer. You want to allocate storage to store the string, i.e. using strlen():
arraylist[i]=malloc(strlen(loc1) + 1);
Note the + 1 as well, because you also need room for the nul-terminator. Then, to copy the string you want to use strcpy():
strcpy(arraylist[i], loc1);
The way you had it was just assigning a pointer to your old string (and in the process leaing the memory you had just allocated). It's also common to use strdup() which combines both of these steps, i.e.
arraylist[i] = strdup(loc1);
This is convenient but strdup() is not part of the official C library. You need to assess the portability needs of your code before you consider using it.
Additionally, with strncpy(), you should be aware that it does not always nul-terminate:
strncpy(buf, loc1, loc-loc1);
This copies less bytes than were in the original string and doesn't terminate buf. Thus, it's necessary to include a nul terminator yourself:
buf[loc - loc1] = '\0';
This is the root cause of what you are seeing with the garbage. Since you didn't nul terminate, C doesn't know where your string ends and so it keeps on reading whatever happens to be in memory.

Reversing a string in C using pointers?

Language: C
I am trying to program a C function which uses the header char *strrev2(const char *string) as part of interview preparation, the closest (working) solution is below, however I would like an implementation which does not include malloc... Is this possible? As it returns a character meaning if I use malloc, a free would have to be used within another function.
char *strrev2(const char *string){
int l=strlen(string);
char *r=malloc(l+1);
for(int j=0;j<l;j++){
r[j] = string[l-j-1];
}
r[l] = '\0';
return r;
}
[EDIT] I have already written implementations using a buffer and without the char. Thanks tho!
No - you need a malloc.
Other options are:
Modify the string in-place, but since you have a const char * and you aren't allowed to change the function signature, this is not possible here.
Add a parameter so that the user provides a buffer into which the result is written, but again this is not possible without changing the signature (or using globals, which is a really bad idea).
You may do it this way and let the caller responsible for freeing the memory. Or you can allow the caller to pass in an allocated char buffer, thus the allocation and the free are all done by caller:
void strrev2(const char *string, char* output)
{
// place the reversed string onto 'output' here
}
For caller:
char buffer[100];
char *input = "Hello World";
strrev2(input, buffer);
// the reversed string now in buffer
You could use a static char[1024]; (1024 is an example size), store all strings used in this buffer and return the memory address which contains each string. The following code snippet may contain bugs but will probably give you the idea.
#include <stdio.h>
#include <string.h>
char* strrev2(const char* str)
{
static char buffer[1024];
static int last_access; //Points to leftmost available byte;
//Check if buffer has enough place to store the new string
if( strlen(str) <= (1024 - last_access) )
{
char* return_address = &(buffer[last_access]);
int i;
//FixMe - Make me faster
for( i = 0; i < strlen(str) ; ++i )
{
buffer[last_access++] = str[strlen(str) - 1 - i];
}
buffer[last_access] = 0;
++last_access;
return return_address;
}else
{
return 0;
}
}
int main()
{
char* test1 = "This is a test String";
char* test2 = "George!";
puts(strrev2(test1));
puts(strrev2(test2));
return 0 ;
}
reverse string in place
char *reverse (char *str)
{
register char c, *begin, *end;
begin = end = str;
while (*end != '\0') end ++;
while (begin < --end)
{
c = *begin;
*begin++ = *end;
*end = c;
}
return str;
}

c splitting a char* into an char**

I'm reading in a line from a file (char by char, using fgetc()), where all fields(firstname, lastname, ...) are seperated by an ;. What I now want to do is create a char**, add all the chars to that and replace the ; by \0 so that I effectively get a list of all the fields.
Is that actually possibly? And when I create a char**, e.g. char ** buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
EDIT
Sry, meant to replace ; by \0, bot \n.
EDIT 2
This code should demonstrate what I intend to do (may clarify things a little):
int length = 80; // initial length char *buf = malloc(length); int num_chars = 0;
// handle malloc returning NULL
// suppose we already have a FILE pointer fp for (int ch = fgetc(fp); ch != EOF && ferror(fp) == 0; ch = fgetc(fp)) {
if (length == size) { // expand array if necessary
length += 80;
buf = realloc(buf, length);
// handle realloc failure
}
if (ch == ';') {
buf[num_chars] = '\0'; // replace delimiter by null-terminator
} else {
buf[num_chars] = ch; // else put char in buf
} }
// resize the buffer to chars read buf
= realloc(buf, num_chars);
// now comes the part where I'm quite unsure if it works / is possible
char **list = (char **)buf; // and now I should be able to handle it like a list of strings?
Yes, it's possible. Yes, you can treat it as a one dimensional array. Yes, the memory is contiguous.
But you probably want:
char ** fields = malloc(sizeof(char *) * numFields);
Then you can do:
// assuming `field` is a `char *`
fields[i++] = field;
It's not exactly possible as you describe it, but something similar is possible.
More specifically, the last line of your example char **list = (char **)buf; won't do what you believe. char **list means items pointed by *list will be of the type char*, but the content of *buf are chars. Hence it won't be possible to change one into another.
You should understand that a char ** is just a pointer, it can hold nothing by itself. But the char ** can be the start address of an array of char *, each char * pointing to a field.
You will have to allocate space for the char * array using a first malloc (or you also can use a static array of char * instead of a char**. You also will have to find space for every individual field, probably performing a malloc for each field and copying it from the initial buffer. If the initial buffer is untouched after reading, it would also be possible to use it to keep the field names, but if you just replace the initial ; by a \n, you'll be missing the trailing 0 string terminator, you can't replace inplace one char by two char.
Below is a simplified example of what can be done (removed malloc part as it does not add much to the example, and makes it uselessly complex, that's another story):
#include <stdio.h>
#include <string.h>
main(){
// let's define a buffer with space enough for 100 chars
// no need to perform dynamic allocation for a small example like this
char buf[100];
const char * data = "one;two;three;four;";
// now we want an array of pointer to fields,
// here again we use a static buffer for simplicity,
// instead of a char** with a malloc
char * fields[10];
int nbfields = 0;
int i = 0;
char * start_of_field;
// let's initialize buf with some data,
// a semi-colon terminated list of strings
strcpy(buf, data);
start_of_field = buf;
// seek end of each field and
// copy addresses of field to fields (array of char*)
for (i = 0; buf[i] != 0; i++){
if (buf[i] == ';'){
buf[i] = 0;
fields[nbfields] = start_of_field;
nbfields++;
start_of_field = buf+i+1;
}
}
// Now I can address fields individually
printf("total number of fields = %d\n"
"third field is = \"%s\"\n",
nbfields, fields[2]);
}
I now want to do is create a char**, add all the chars to that
You would allocate an array of char (char*) to store the characters, not an array of char* (char**).
And when I create a char*, e.g. char * buf = malloc(80) can I treat it like a one dimensional array? If the memory returned by malloc contiguous?
Yes, malloc always returns continguous blocks. Yes, you can treat it as single-dimensional an array of char* (with 80/sizeof char* elements). But you'll need to seperately allocate memory for each string you store in this array, or create another block for storing the string data, then use your first array to store pointers into that block.
(or something else; lots of ways to skin this cat)
It may be that Helper Method wants each of the "fields" written to a nul-terminated string. I cannot tell. You can do that with strtok(). However strtok() has problems - it destroys the original string "fed" to it.
#define SPLIT_ARRSZ 20 /* max possible number of fields */
char **
split( char *result[], char *w, const char *delim)
{
int i=0;
char *p=NULL;
for(i=0, result[0]=NULL, p=strtok(w, delim); p!=NULL; p=strtok(NULL, delim), i++ )
{
result[i]=p;
result[i+1]=NULL;
}
return result;
}
void
usage_for_split(void)
{
char *result[SPLIT_ARRSZ]={NULL};
char str[]="1;2;3;4;5;6;7;8;9;This is the last field\n";
char *w=strdup(str);
int i=0;
split(result, w, ";\n");
for(i=0; result[i]!=NULL; i++)
printf("Field #%d = '%s'\n", i, result[i]);
free(w);
}

Resources