As an exercise, I have build a simple program that, given a text file of N lowercase words and whitespaces, populates a ragged array char *en[N].
It works without great problems, apart for one: it populates the ragged array with only the last word of the input.
#include<stdio.h>
#include<ctype.h>
int main(int argc, char *argv[]){
int i = 0, j = 0;
char *en[100];
char temp[20];
FILE *p = fopen(argv[1], "r");
char single;
while((single = fgetc(p)) != EOF){
if(!isspace(single)) /* Temporary store a single word */
temp[i++] = single;
else{
temp[i] = '\0';
en[j++] = temp; /* Save stored word in ragged array */
i = 0;
}
}
printf("%s\n", en[0]); /* Return the same than en[1] and en[99] */
printf("%s\n", en[1]);
printf("%s\n", en[99]);
return 0;
}
I cannot understand why it goes down to the end of the input file. I am unable of detecting major issues that could suggest a wrong approach.
Edit:
The reasoning behind my approach was that an array of *char can be initialized with this form:
p[0] = "abc";
reasoning that I wrongly tried to translate in the error above, that #coderredoc brilliantly caught. As far as the dimensions of single words and inputs are concerned, I admit I did not put many attention in them. The exercise is centered on a different topic. In any case, thanks a lot your your valuable suggestions!
Your array of charcaters are all pointing to the same char array and then the content of the array at last changes to the last word. And you get only the last word.
A possible solution
en[j++] = temp;
to
en[j++] = strdup(temp);
Then you will achieve the behavior you want your program to have.
You just found out the awesomeness of pointers, congratulations!
Seriously, char *en[100] is an array of pointers. en[j++] = temp; assigns the pointer to the first value of temp to a pointer at en[j++]. And you do this over and over again. No surprise that you end up with an array of pointers, all of which point to the same array temp, which holds the contents of the last word.
What to learn from this: a pointer merely points to some memory, and no memory copying happens when you do en[j++] = temp;. You have to allocate the memory yourself and copy temp to that new memory yourself.
Related
I'm writing a program that should get its inputs from a text file by using input redirection in a function called GetInput. (The text file contains 10 words.) The code should then be able to print the contents of ListWord in the Print function.
This is what I have so far.
I keep on getting errors while trying to run this code. I tried to remove * before ListWord and the code works but it does not retain the word (string) that was stored in it. But removing * before ListWord does not make sense to me. What am I doing wrong?
void GetInput( char** ListWord)
{
int i=0;
char word[30]; //each word may contain 30 letters
*ListWord = malloc(sizeof(char*)*10); //there are 10 words that needs to be allocated
while(scanf("%s", word)==1) //Get Input from file redirection
{
*ListWord[i]= (char *)malloc(30+1);
printf("%s\n", word); //for checking
strcpy(*ListWord[i], word);
printf("%s\n", *ListWord[i]); //for checking
i++;
}
}
void Print(char *ListWord)
{
//print ListWord
int i;
for (i=0; i<10; i++)
{
printf("%s", ListWord[i]);
}
}
int main()
{
char * ListWord;
GetInput(&ListWord);
printf("%s\n", ListWord[0]);
Print(ListWord);
free(ListWord);
return 0;
}
(Note: This is a homework. Thank you and sorry if it's unclear)
Due to *operator precedence the expression *ListWord[i] doesn't do what you think it does. In fact you should be getting errors or warnings from the code you have.
The compiler thinks that *ListWord[i] means *(ListWord[i]), which is not right. You need to use (*ListWord)[i].
Unfortunately that's only the start of your problems. A bigger problem is that the pointer you pass to the function GetInput is not a pointer to what could become an array of strings, but a pointer to a single string.
For a dynamic allocated array of strings, you need a pointer to a pointer to begin with, and then emulate pass-by-reference on that, i.e. you need to become a three star programmer which is something you should avoid.
Instead of trying to pass in the array to be allocated as an argument, have the GetInput return the array instead. Something like
char **GetInput(void)
{
// Allocate ten pointers to char, each initialized to NULL
char **ListWords = calloc(10, sizeof(char *));
if (ListWords == NULL)
return NULL;
char word[31];
for (int i = 0; i < 10 && scanf("%30s", word) == 1; ++i)
{
ListWords[i] = strdup(word);
}
return ListWords;
}
The above code adds some security checks, so you will not go out of bounds of either the temporary array you read into, or the ListWords array. It also makes sure the ListWords array is initialized, so if you read less then 10 words, then the remaining pointers will be NULL.
Of course you need to change your main function accordingly, and also your Print function, because now it only takes a single string as argument, not an array of strings. You also of course need to free every single string in the array because freeing the array.
I'm taking 2 character arrays as parameters and trying to swap those. This is what I have so far, but I'm getting the error "array initializer must be an initializer list". Can someone explain why this is happening and how to fix it?
Here's my code so far for the function:
void swapStrings(char string1[], char string2[])
{
char tempString[] = string1;
}
In C you can't copy strings using the assignment operator, so you'll probably want something like this.
size_t length = strlen(string1) + 1;
char *tempString = malloc(length);
strncpy(tempString, string1, length);
free(tempString);
(Assuming both strings have enough space to be swapped as #abelenky said.)
Hope it helps!
The first concern is to make sure that the two strings both have enough space to be swapped.
You can see the problem with trying to swap "Hello" with "ThisIsAVeryLongStringThatWillNotFitInTheSpaceOfHello"; there just isn't enough space in the first buffer to hold the second buffer.
Assuming there is enough space, you just need to swap every character, one-by-one until the entire strings are swapped.
void swapStrings(char string1[], char string2[])
{
char temp;
do
{
temp = *string1;
*string1 = *string2;
*string2 = temp;
} while(*string1++ && *string2++);
// At this point, the shorter string is done swapping.
// But we need to finish swapping the longer string:
if (*string1)
{
while (*string2++ = *string1++) ;
}
if (*string2)
{
while (*string1++ = *string2++) ;
}
}
Note contrary to the answer by cdonts, no third buffer needs to be allocated or managed. The memory requirement is much smaller because there is only a single byte needed for swapping the strings.
I'm fairly new to C; been at it for 3 weeks in a class. I am having a bit of trouble with pointers, and am sure there is probably an easy fix. So basically, this program is supposed to read a word from an input file, store it in an array of pointers with memory allocation, print the word and the normalized form of the word (irrelevant process), and then reallocate the space so that the pointer array will grow as more words are inputted. However, I am having a bit of trouble getting the words to print and the array to reallocate (I currently have it set to a fixed size just to troubleshoot the whole printing aspect). Let me know if there is something wrong with my variable declarations, or if I am just making a stupid mistake please (I am sure it is the probably a combination of the two). Again, I'm very new to C, so I apologize if this is an easy question.
char * word_regular[100];
char * word_norm[100];
int main (int argc, char * argv[])
{
if (argc != 2){
printf("You have not entered a valid number of files.\n");
exit(1);
}
FILE * f_in = fopen(argv[1],"r");
int i = 0;
char word[512];
char norm_word[512];
while(fscanf(f_in, "%s", word) != EOF) {
if (is_valid_entry(word)) {
word_regular[i] = malloc(sizeof(char) * strlen(word) + 1);
strcpy(word_regular[i],word);
printf("%s\n",*word_regular[i]);
word_norm[i] = malloc(sizeof(char) * strlen(norm_word) + 1);
normalize(word, norm_word);
strcpy(word_norm[i],norm_word);
printf("%s\n", *word_norm[i]);
i++;
Some problems that are with your current code (ignoring the dynamic size need as opposed to fixed since you already said you are using that to debug),
printf("%s\n",*word_regular[i]);
%s takes a char * for printing, so it should be
printf("%s\n",word_regular[i]);
For the second printf, since norm_word itself is a char array,
you should simply use
printf("%s\n", &norm_word[i]);
If you want to print string starting from the ith index.
Update:
A quick tip is to pay attention whether you are copying the \0 with strings or not. Because your api calls, such as strlen would go beyond string crashing (or worst silently), unless it is null terminated.
The problem with your printf call is that you pass a char (*word_regular[i], *norm_word[i]) instead of char * (word_regular[i], word_norm[i]) when trying to print a string.
If you want to dynamically grow the array, you need to dynamically allocate it in the first place, so instead of declaring arrays of pointers:
char * word_regular[100];
char * word_norm[100];
You need to declare pointers to pointers:
char ** word_regular;
char ** word_norm;
Allocate an initial buffer for them (in a function, main for example):
word_regular = malloc(sizeof(char *) * INITIAL_AMOUNT);
Then reallocate them as needed.
word_regular = realloc(word_regular, sizeof(char *) * new_amount);
You will need to keep track of the amount of pointers in the arrays, and free them properly of course...
I'm trying to write a stream editor in C and I'm having a hard time dealing with strings. After reading in the lines of a File, I want to store them locally in an array of Strings. However, when I try to store the variable temp into the array of strings StoredEdits I get a segmentation fault (core dumped) error. Furthermore, if I uncomment the char* temp2 variable and save this into my array as a workaround, then the last value read in gets stored for every value in the array.
I assume this has to do with the fact that temp2 is a pointer. I've tried a million things like malloc'ing and free'ing this variable after each iteration, but nothing seems to work.
Any help would be greatly appreciated.
#define MAX_SIZE 100
typedef char String[MAX_SIZE];
int main(int argc, char* argv[]){
char** StoredEdits;
int index, numOfEdits;
FILE *EditFile;
char* temp;
//char* temp2;
StoredEdits = (char**)malloc(MAX_INPUT_SIZE*sizeof(String));
/*Check to see that edit file is passed in.*/
if(argc < 2){
printf("ERROR: Edit File not given\n");
return(EXIT_FAILURE);
}
printf("%s\n",argv[1]);
if( (EditFile = fopen(argv[1],"r")) != NULL ){
printf("file opened\n");
numOfEdits = 0;
while(fgets(temp, MAX_STRING_SIZE, EditFile) != NULL){
printf("%d %s",numOfEdits,temp);
//temp2 = temp;
StoredEdits[numOfEdits++] = temp;
//StoredEdits[numOfEdits++] = temp;
printf("Stored successfully\n");
}
..........
printf("%d\n",numOfEdits);
for(index=0;index<numOfEdits;index++){
printf("%d %s\n",index, StoredEdits[index]);
}
You need to initialize temp to point to valid storage.
temp = malloc(MAX_STRING_SIZE+1);
It looks like you may have intended to do something like this:
String temp;
using your macro. This would be better as a regular char array. And the common name for this is buffer.
char buffer[MAX_STRING_SIZE+1];
Then, you should store in your array, not temp itself, but a new string containing a copy of the contents. There is a POSIX function strdup that should be helpful here. Note, strdup is not part of the C standard, but it is available in most hosted implementations. Historically, it comes from the BSD branch.
StoredEdits[numOfEdits++] = strdup(temp);
Let me backpedal a little and say that if you're allocating new storage for temp inside the loop, then you should skip the strdup because, as Jim Balter says, this will leak memory. If you allocate temp outside of the loop, then it makes little difference whether you allocate it statically (by declaring a char []) or dynamically (with malloc).
By the way, this line will not buy you much:
typedef char String[MAX_SIZE];
For why, see the classic Kernighan (the K in K&R) essay Why Pascal is not my favorite Programming Language.
Also note, that my examples above do not check the pointer returned by malloc. malloc can fail. When malloc fails it will return a NULL pointer. If you try to store data through this pointer, Kaboom!
You're right about your problem being because of pointer semantics. You should use copy the contents of the string from temp.
char *cpy = malloc(1 + strlen(temp));
if (cpy)
strcpy(cpy, temp);
//else handle error
StoredEdits[numOfEdits++] = cpy;
Others answered the reason for the error.
But from the program, i see that you tried to allocate a character double array. then you store each line read from the file into the array.
StoredEdits = (char**)malloc(MAX_INPUT_SIZE*sizeof(String));
if my assumption is right, then you should pass the array into strcpy like the below.
strcpy(StoredEdits[numOfEdits],tmp);
when you have a file where each line varies in size, it is better to go array of pointers points to character array.
In my program I am getting a seg fault and I'm not sure the cause or how to find out the cause. Any help would be greatly appreciated!
In the code I am trying to read word by word, but I need to keep track of the line numbers. Then I am trying to create a linked list where the data is the word and line number.
(there are two files compiled together)
void main(int argc, char **argv){
file = fopen(argv[1],"r");
struct fileIndex *fIndex = NULL;
delimiters = " .,;:!-";/*strtok chars to seperate*/
int wCount = wordcount(file);/*number of words in file*/
char **str[wCount+1];/*where the lines are being stored*/
int j=0;
while(!feof(file)){/*inserting lines*/
fscanf(file, "%s", &str[j]);
j++;
}
char *token, *cp;
int i;
int len;
for(i = 0; str[i]; i++){/*checking to insert words*/
len = strlen(*str[i]);
cp = xerox(*str[i]);
token = strtok(cp, delimiters);
if(!present(fIndex, token)){
insert(fIndex, i+1,token);
}
while(token!=NULL){
token = strtok(NULL, delimiters);
if(!present(fIndex, token)){
insert(fIndex, i+1,token);
}
}
i++;
}
fclose(file);
}
int strcmpigncase(char *s1, char *s2){/*checks words*/
for(;*s1==*s2;s1++,s2++){
if(*s1=='\0')
return 0;
}
return tolower(*s2)-tolower(*s2);
}
present(struct fileIndex* fIndex, char *findIt){/*finds if word is in structure*/
struct fileIndex* current = fIndex;
while(current!=NULL){
current = current -> next;
if(strcmpigncase(current -> str, findIt)==0){
return current -> lineNum;
}
}
return 0;
}
void insert(struct fileIndex *head, int num, char *insert){/*inserts word into structure*/
struct fileIndex* node = malloc(sizeof(struct fileIndex));
node -> str = insert;
node -> lineNum = num;
node -> next = head;
head = node;
}
#define IN_WORD 1
#define OUT_WORD 0
int wordcount(FILE *input)/*number of words in file*/
{
FILE *open = input;
int cur; /* current character */
int lc=0; /* line count */
int state=OUT_WORD;
while ((cur=fgetc(open))!=EOF) {
if (cur=='\n')
lc++;
if (!isspace(cur) && state == OUT_WORD) {
state=IN_WORD;
}
else if (state==IN_WORD && isspace(cur)) {
state=OUT_WORD;
}
}
return lc;
}
char *xerox(char *s){
int i = strlen(s);
char *buffer = (char *)(malloc(i+1));
if(buffer == NULL)
return NULL;
char *t = buffer;
while(*s!='\0'){
*t=*s;
s++; t++;
}
*t = '\0';
return buffer;
}
This code has a fairly high rate of problems. I'll dissect just the first few lines to give an idea:
void main(int argc, char **argv){
main should return int, not void. Probably not causing your problem, but not right either.
file = fopen(argv[1],"r");
You really need to check the value of argc before trying to use argv[1]. Invoking the program without an argument may well lead to a problem. Depending on how you've invoked it, this could be the cause of your problem.
struct fileIndex *fIndex = NULL;
Unless you've included some headers you haven't shown, this shouldn't compile -- struct fileIndex doesn't seem to have been defined (nor does it seem to be defined anywhere I can see in the code you'e posted).
delimiters = " .,;:!-";/*strtok chars to seperate*/
int wCount = wordcount(file);/*number of words in file*/
This (wordcount) reads to the end of the file, but does not rewind the file afterward.
char **str[wCount+1];/*where the lines are being stored*/
From your description, you don't really have any need to store lines (plural) at all. What you probably want is to read one line, then tokenize it and insert the individual tokens (along with the line number) into your index, then read the next line. From what you've said, however, there's no real reason to store more than one raw line at a time though.
int j=0;
while(!feof(file)){/*inserting lines*/
As noted above, you've previously read to the end of the file, and never rewound the file. Therefore, nothing inside this loop should ever execute, because as soon as you get here, feof(file) should return true. When/if you take care of that, this loop won't work correctly -- in fact, a loop of the form while (!feof(file)) is essentially always wrong. Under the circumstances, you want to check the result of your fscanf, with something like:
while (1 == fscanf(file, "%1023s", line))
...so you exit the loop when attempting to read fails.
fscanf(file, "%s", &str[j]);
What you have here is basically equivalent to the notorious gets -- you've done nothing to limit the input to the size of the buffer. As shown above, you normally want to use %[some_number]s, where some_number is one smaller than the size of the buffer you're using (though, of course, to do that you do need a buffer, which you don't have either).
You've also done nothing to limit the number of lines to the amount of space you've allocated (but, as with the individual lines, you haven't allocated any). I almost hesitate to mention this, however, because (as mentioned above) from your description you don't seem to have any reason to store more than one line anyway.
Your code also leaks all the memory it allocates -- you have calls to malloc, but not a single call to free anywhere.
Actually, some of the advice above is (at last more or less) wrong. It's looking at how to fix an individual line of code, but in reality you probably want to structure the code a bit differently in general. Rather than read the file twice, once to count the words, then read it again to index the words, you probably want to read a line at a time (probably with fgets, then break the line into words, and count each word as you insert it into your index. Oh, and you almost certainly do not want to use a linked-list for your index either. A tree or a hash-table would make a great deal more sense for the job.
I also disagree with the suggestion(s) in the direction of using a debugger on this code. A debugger is not likely to lead toward significantly better code -- it may help you find a few of the localized problems, but is unlikely to lead toward a significantly better program. Instead, I'd suggest a pencil and a piece of paper as the tools you really need to use. I believe your current problems stem primarily for not having thought about the problem enough to really understand what steps are needed to accomplish the goal, and a debugger isn't likely to help much in finding an answer to that question.
If you don't have a good debugger handy, a good fallback is to simply add a few printf statements at steps through the code, so you can see how far it gets before crashing.
In this code:
char **str[wCount+1];/*where the lines are being stored*/
int j=0;
while(!feof(file)){/*inserting lines*/
fscanf(file, "%s", &str[j]);
j++;
}
str is an array of pointers to char *s. In your loop you are reading each piece of input into a slot in it. There are a couple of problems.
I think there's a miscount in the number of *s vs. &s (I don't usually program with that many levels of pointer indirection to avoid having to think so hard about them ;-). &str[j] is the address of that array element, but that array element is a pointer to a pointer; now you have a pointer to a pointer to a pointer. If you had instead char *str[wCount+1], and read into str[j], I think it might match up. (Also I don't use fscanf much, so perhaps someone can confirm how best to use it.)
More obviously, you're not actually allocating any memory for the string data. You're only allocating it for the array itself. You probably want to allocate a fixed amount for each one (you can do that in the loop before each fscanf call). Remember that you're fscanf could in practice read more than that fixed size, resulting in another memory error. Again, working around that requires an expert in fscanf usage.
Hope this helps for a start. If the printf suggestion finds a more specific point in the code where it fails, add that to the question.