Structs, strtok, segmentation fault - c

I'm trying to make a program with structs and files. The following is just a part of my code(it;s not the entire program).
What i'm trying to do is: ask the user to write his command. eg. delete John
eg. enter John James 5000 ipad purchase.
The problem is that I want to split the command in order to save its 'args' for a struct element. That's why i used strtok. BUT I'm facing another problem in who to 'put' these on the struct.
Also it's seems quite odd to me how to 'pass' the 'args' to the struct in a safe way,as I save all enters (from user) on a binary file which maybe be reopened and rewrited so I cant use :
strcpy(catalog[0]->short_name, args[1]);
Because it's time the short name will be saved in the first element of the struct. But if the file is written what happens then? The first element exists so if i write ..[0] i will write up on it?
What should I do? Thanx in advance for any help! :D
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 100
char command[1500];
struct catalogue
{
char short_name[50];
char surname[50];
signed int amount;
char description[1000];
}*catalog[MAX];
int main ( int argc, char *argv[] )
{
int i,n;
char choice[3];
printf(">sort1: Print savings sorted by surname\n");
printf(">sort2: Print savings sorted by amount\n");
printf(">search+name:Print savings of each name searched\n");
printf(">delete+full_name+amount: Erase saving\n");
printf(">enter+full_name+amount+description: Enter saving \n");
printf(">quit: Update + EXIT program.\n");
printf("Choose your selection:\n>");
gets(command); //it save the whole command
/*in choice it;s saved only the first 2 letters(needed for menu choice again)*/
strncpy(choice,command,2);
choice[2]='\0';
char** args = (char**)malloc(strlen(command)*sizeof(char*));
memset(args, 0, sizeof(char*)*strlen(command));
char* temp = strtok(command, " \t");
for (n = 0; temp != NULL; ++n)
{
args[n] = strdup(temp);
temp = strtok(NULL, " \t");
printf(" %s ",args[n]);
}
strcpy(catalog[0]->short_name, args[1]); //segmentation fault
strcpy(catalog[0]->surname,args[2]);
catalog[0]->amount=atoi(args[3]); //atoi doesn't work
strcpy(catalog[0]->description,args[4]);
}
As a result, after running the program i get a Segmentation Fault...
for the line:
strcpy(catalog[0]->short_name, args[1]);
Any help? Any ideas?

You have 2 errors:
Your catalog[MAX] array holds MAX pointers to your struct catalogue, but none of them are initialized. The way to fix this is to either not declare them to be pointers, or to malloc them as needed, like in catalog[0] = (struct catalogue *)malloc(sizeof(struct catalogue));
Your args variable is bad. First, I don't think you intend to create an array of strings whose length is the length of your command string. That means if you type "sort1" you will create args[5]. That's nonsensical, because the length of your command has nothing to do with how many arguments it should have.
But assuming you really want to do that, you are creating space for the array, but not for the strings within the array. You will get a segfault eventually anyway (though the one you are getting is due to #1 above) because of that. You need to allocate space for each element in args as you use it.
The code could look like this:
for (n = 0; temp != NULL; ++n)
{
args[n] = (char *)malloc((strlen(temp) + 1) * sizeof(char));
strcpy(args[n], temp);
// and so on
}

The for loop assigns one argument at a time (args[n] = ...), but then accesses several arguments on each pass: *args[1], args[2], etc., which are uninitialized on the first pass.
The warning is due to another bug. You can't just assign a pointer to an array like that. Use strcpy() instead.

your array for catalouge is an array of pointers, not an array of objects, but those pointers aren't initialized to anything, hence the seg fault
try:
struct catalogue
{
char short_name[50];
char surname[50];
signed int amount;
char description[1000];
}catalog[MAX];
strcpy(catalog[0].short_name, args[1]); //segmentation fault
strcpy(catalog[0].surname,args[2]);
catalog[0].amount=atoi(args[3]); //atoi doesn't work
strcpy(catalog[0].description,args[4]);

Lots of problems in this code.
First of all, you're confusing the number of arguments on the input line for the number of entries in the catalog. In one context you're using n to count the number of args, but in another you're using it to index the catalog array.
You're creating memory management headaches where you don't need to. The args variable is completely unnecessary, and you're allocating the memory for it incorrectly anyway. You're basically saying, "allocate a pointer to char for every character in command, which is probably not what you want.
Lose args completely; you don't need it.
I realize this is not your entire program, but it's not clear why you're creating catalog as an array of pointer to struct catalog as opposed to just a regular array.
I'm not sure what you think you're doing on the line
*catalog[n]->short_name=*args[1];
The type of the expression catalog[n]->short_name is char[50]. In this context the array type is implicitly converted ("decays") to a pointer type, char *. Thus the type of the whole expression *catalog[n]->short_name is * (char *), or just plain char, which is an integral type. You're essentially trying to assign the value of the first character of args[1] to the first character of catalog[n]->short_name.
None of this matters anyway, because catalog[n] hasn't been initialized to point anywhere meaningful; the segfault is coming from the attempt to access the short_name member, which implicitly dereferences catalog[n], which is pointing somewhere random.
Next, you cannot use the assignment operator = to assign string data; you must use strcpy() or strncpy() to do that.
Finally, NEVER NEVER NEVER NEVER NEVER use gets(). It will introduce a point of failure in your code. It has been officially deprecated in C99 and should no longer be used. Use fgets() instead:
if (fgets(command, sizeof command, stdin) != NULL)
{
char *newline = strchr(command, '\n');
if (newline != NULL)
*newline = 0;
}
Here's the way you need to parse out the command string and assign the fields to members of the struct:
curToken = strtok(command, '\t');
if (curToken)
strncpy(catalog[n]->short_name, curToken, sizeof catalog[n]->short_name);
curToken = strtok(NULL, '\t');
if (curToken)
strncpy(catalog[n]->surname, curToken, sizeof catalog[n]->surname);
curToken = strtok(NULL, '\t');
if (curToken)
{
char *chk;
catalog[n]->amount = (int) strtol(curToken, &chk, 10);
if (!isspace(*chk) && *chk != 0)
fprintf(stderr,
"Warning: expected integer value for amount, received %s instead\n",
curToken);
}
curToken = strtok(NULL, '\t');
if (curToken)
strncpy(catalog[n]->description, curToken, sizeof catalog[n]->description);
This code assumes that catalog is still being declared as an array of pointer and that each element has been initialized to point somewhere meaningful. Otherwise change the declaration from struct catalog {...} *catalog[MAX]; to struct catalog {...} catalog[MAX] and change -> to ..

Related

Spliting string and storing in array or pointer in C

I'm working in a little project, i have some names stored in a struct (example: Name1, Name2) and I need to split each one and store it in a array so I can call then each name separately (printf("%s", vet[1]) should print only "Name2").
This is my code:
int main(){
char temp[100];
LIGA *vetLiga;
int reference;
int quantiy;
separarEquipas(vetLiga, temp, reference, quantity);
}
int separarEquipas(LIGA *p, char vet[100], int vPesquisa, int n){
int i, nr, a;
char *ptr;
char *str;
for(i=0;i<n;i++){
if (p->id == vPesquisa){
nr = p->nrEquipas;
strcpy(str, p[i].eqLiga);
ptr = strtok(str, " ,");
while(ptr != NULL)
{
vet[a++] = ptr; //here I'm trying to store each word in a position of the array
ptr = strtok(NULL, " ,");
}
}
p++;
}
return nr;
}
The issue is inside the while where I try to store each token in the array but it keeps crashing the terminal. I tried in different ways like using strcpy and memcpy as other posts suggest but nothing :(.
Some errors that i got while trying to find a solution:
[Warning] assignment makes integer from pointer without a cast;
[Warning] passing argument 1 of 'strcpy' makes pointer from integer without a cast.
Hope you can help me,
Thank you!
You didn't post the full code, so from what I can see vetLiga that in separarEquipas becomes p is uninitialised.
Another issue is that you try to use str in strcpy without allocating memory for it. You need to do that
char *str = malloc( max_number_of_characters_in_str );
Then here:
vet[a++] = ptr; //here I'm trying to store each word in a position of the array
You are doing exactly what you said in the comment. However you can't store a word into the space for a single character. vet needs to be a 2D array or if you want even an array of pointers to char.
If you want further help include the whole program.
In main, vetLiga is never assigned a value, but maybe you abbreviated the code.
In separarEquipas you have the following:
char *str;
strcpy(str, p[i].eqLiga)
So you are copying a string to a random location in memory.

How do I return a string from a function in C?

I'm working on the following homework problem:
Given the first name and last name as parameters, write the code of
the function createFBlink(). The functions returns a facebook link
which serves as an alternate email of the facebook user. The variable
holding the facebook link should contain the minimum number of bytes
required to store the string representing the facebook link. If there
is no first name or last name, the function returns NULL.
For example, if firstname = tzuyu and lastname = chou, the
facebook link is chou.tzuyu#facebook.com.
(See the original problem statement here.)
I've been trying to return a string from createFBlink into main. I've tried multiple methods such as turning char into static but I keep getting errors that I don't understand because I don't have a lot of experience with C.
I've had the best luck with using malloc, but I've come across a problem wherein if ever there are parameters to the function I'm sending from main, I end up with a crash after the input. Here's my code so far:
#include <string.h>
#include <conio.h.>
#include <stdio.h>
#include <stdlib.h>
char *createFBlink(char *firstname , char *lastname) ;
int main(void)
{
char firstname[24] , lastname[24], fblink[24] ;
printf("Enter first name: ");
scanf("%s", firstname);
firstname[strlen(firstname)] = '\0';
printf("\n Enter last name: ");
scanf("%s", lastname);
lastname[strlen(lastname)] = '\0';
*fblink = createFBlink(firstname, lastname);
if(*firstname == '\0'){
printf("no facebook link generated");
}else{
printf("%s", *fblink);
}
getch();
return 0;
}
char * createFBlink(char *firstname , char *lastname)
{
int check1 = strlen(firstname) , check2 = strlen(lastname), num = check1+check2;
char link = (char*) malloc(sizeof(char) * num);
if(check1 == 0 || check2 == 0){
*firstname = '\0' ;
}else{
strcat(*lastname, ".");
strcat(*lastname, firstname);
strcat(*lastname, "#facebook.com");
strcpy(link , *lastname);
return link;
}
}
*link = (char *) malloc(24);
This is incorrect, it should be
link = (char *) malloc(24);
*link (the same as link[0]) is the first character of the string pointed by link, that assignment is just overwriting the character, not changing the pointer.
The following is also incorrect:
*fblink = createFBlink(firstname, lastname);
This:
strcat(*lastname, ...);
is incorrect in the same way. You are getting the first character of the string pointed by lastname, converting it to a pointer and passing this (obviously invalid) pointer to strcat. This is the most likely reason of the crash.
Also, 24 characters may not be enough to hold the concatenated string.
Try to read a book about working with pointers in C, trying to understand them via trial-and-error is probably not the most effective way.
When working with strings, you need to understand the types you are using.
This is a fixed area in memory, of fixed size.
char buffer [24];
This is a dynamically allocated buffer that must be freed
char* szBuffer = malloc(24);
free(szBuffer)
Instead of doing that correctly, your createFBlink does malloc twice, and free zero times.
If you return a malloc'ed buffer from a function, you still must free it.
char * result = createFBlink(stuff);
free(result);
Since the types are different, you would need to use another function to move the string data from one to the other.
char * result = createFBlink(stuff);
strcpy(fblink, result, sizeof(fblink));
free(result);
And then you have additional risk from writing outside the allocated space. Let's try a made-up name with some common names.
firstname "Richard"
lastname "Hernandez"
returned string "Hernandez.Richard#facebook.com"
Oh look, 31 characters in a 24 character string. We just overwrite something, somewhere on your computer. Literally anything could happen, now.
So you have all kinds of risk. You have to match malloc with free. You have to keep track of the size. All of this is considered to be VERY BAD c++ style. The std::string class is highly recommended for this. You want your string class to take care of all the resource management, so you can't mess it up while you are using it.
std::string CreateFacebookLink (const std::string &firstname, const std::string &lastname){
return firstname + "." + lastname + "#facebook.com";
}
std::string strFacebookLink (CreateFacebookLink("Richard", "Hernandez"));

(C Programming) Making a char ** array like argv

In a program I am writing I made a Tokenize struct that says:
TokenizerT *Tokenize(TokenizerT *str) {
TokenizerT *tok;
*tok->array = malloc(sizeof(TokenizerT));
char * arr = malloc(sizeof(50));
const char *s = str->input_strng;
int i = 0;
char *ds = malloc(strlen(s) + 1);
strcpy(ds, s);
*tok->array[i] = strtok(ds, " ");
while(*tok->array[i]) {
*tok->array[++i] = strtok(NULL, " ");
}
free(ds);
return tok;
}
where TokenizeT is defined as:
struct TokenizerT_ {
char * input_strng;
int count;
char **array[];
};
So what I am trying to do is create smaller tokens out of a large token that I already created. I had issues returning an array so I made array part of the TokenizerT struct so I can access it by doing tok->array. I am getting no errors when I build the program, but when I try to print the tokens I get issues.
TokenizerT *ans;
TokenizerT *a = Tokenize(tkstr);
char ** ab = a->array;
ans = TKCreate(ab[0]);
printf("%s", ans->input_strng);
TKCreate works because I use it to print argv but when i try to print ab it does not work. I figured it would be like argv so work as well. If someone can help me it would be greatl appreciated. Thank you.
Creating the Tokenizer
I'm going to go out on a limb, and guess that the intent of:
TokenizerT *tok;
*tok->array = malloc(sizeof(TokenizerT));
char * arr = malloc(sizeof(50));
was to dynamically allocate a single TokenizerT with the capacity to contain 49 strings and a NULL endmarker. arr is not used anywhere in the code, and tok is never given a value; it seems to make more sense if the values are each shifted one statement up, and corrected:
// Note: I use 'sizeof *tok' instead of naming the type because that's
// my style; it allows me to easily change the type of the variable
// being assigned to. I leave out the parentheses because
// that makes sure that I don't provide a type.
// Not everyone likes this convention, but it has worked pretty
// well for me over the years. If you prefer, you could just as
// well use sizeof(TokenizerT).
TokenizerT *tok = malloc(sizeof *tok);
// (See the third section of the answer for why this is not *tok->array)
tok->array = malloc(50 * sizeof *tok->array);
(tok->array is not a great name. I would have used tok->argv since you are apparently trying to produce an argument vector, and that's the conventional name for one. In that case, tok->count would probably be tok->argc, but I don't know what your intention for that member is since you never use it.)
Filling in the argument vector
strtok will overwrite (some) bytes in the character string it is given, so it is entirely correct to create a copy (here ds), and your code to do so is correct. But note that all of the pointers returned by strtok are pointers to character in the copy. So when you call free(ds), you free the storage occupied by all of those tokens, which means that your new freshly-created TokenizerT, which you are just about to return to an unsuspecting caller, is full of dangling pointers. So that will never do; you need to avoid freeing those strings until the argument vector is no longer needed.
But that leads to another problem: how will the string be freed? You don't save the value of ds, and it is possible that the first token returned by strtok does not start at the beginning of ds. (That will happen if the first character in the string is a space character.) And if you don't have a pointer to the very beginning of the allocated storage, you cannot free the storage.
The TokenizerT struct
char is a character (usually a byte). char* is a pointer to a character, which is usually (but not necessarily) a pointer to the beginning of a NUL-terminated string. char** is a pointer to a character pointer, which is usually (but not necessarily) the first character pointer in an array of character pointers.
So what is char** array[]? (Note the trailing []). "Obviously", it's an array of unspecified length of char**. Because the length of the array is not specified, it is an "incomplete type". Using an incomplete array type as the last element in a struct is allowed by modern C, but it requires you to know what you're doing. If you use sizeof(TokenizerT), you'll end up with the size of the struct without the incomplete type; that is, as though the size of the array had been 0 (although that's technically illegal).
At any rate, that wasn't what you wanted. What you wanted was a simple char**, which is the type of an argument vector. (It's not the same as char*[] but both of those pointers can be indexed by an integer i to return the ith string in the vector, so it's probably good enough.)
That's not all that's wrong with this code, but it's a good start at fixing it. Good luck.

C String parsing errors with strtok(),strcasecmp()

So I'm new to C and the whole string manipulation thing, but I can't seem to get strtok() to work. It seems everywhere everyone has the same template for strtok being:
char* tok = strtok(source,delim);
do
{
{code}
tok=strtok(NULL,delim);
}while(tok!=NULL);
So I try to do this with the delimiter being the space key, and it seems that strtok() no only reads NULL after the first run (the first entry into the while/do-while) no matter how big the string, but it also seems to wreck the source, turning the source string into the same thing as tok.
Here is a snippet of my code:
char* str;
scanf("%ms",&str);
char* copy = malloc(sizeof(str));
strcpy(copy,str);
char* tok = strtok(copy," ");
if(strcasecmp(tok,"insert"))
{
printf(str);
printf(copy);
printf(tok);
}
Then, here is some output for the input "insert a b c d e f g"
aaabbbcccdddeeefffggg
"Insert" seems to disappear completely, which I think is the fault of strcasecmp(). Also, I would like to note that I realize strcasecmp() seems to all-lower-case my source string, and I do not mind. Anyhoo, input "insert insert insert" yields absolutely nothing in output. It's as if those functions just eat up the word "insert" no matter how many times it is present. I may* end up just using some of the C functions that read the string char by char but I would like to avoid this if possible. Thanks a million guys, i appreciate the help.
With the second snippet of code you have five problems: The first is that your format for the scanf function is non-standard, what's the 'm' supposed to do? (See e.g. here for a good reference of the standard function.)
The second problem is that you use the address-of operator on a pointer, which means that you pass a pointer to a pointer to a char (e.g. char**) to the scanf function. As you know, the scanf function want its arguments as pointers, but since strings (either in pointer to character form, or array form) already are pointer you don't have to use the address-of operator for string arguments.
The third problem, once you fix the previous problem, is that the pointer str is uninitialized. You have to remember that uninitialized local variables are truly uninitialized, and their values are indeterminate. In reality, it means that their values will be seemingly random. So str will point to some "random" memory.
The fourth problem is with the malloc call, where you use the sizeof operator on a pointer. This will return the size of the pointer and not what it points to.
The fifth problem, is that when you do strtok on the pointer copy the contents of the memory pointed to by copy is uninitialized. You allocate memory for it (typically 4 or 8 bytes depending on you're on a 32 or 64 bit platform, see the fourth problem) but you never initialize it.
So, five problems in only four lines of code. That's pretty good! ;)
It looks like you're trying to print space delimited tokens following the word "insert" 3 times. Does this do what you want?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char str[BUFSIZ] = {0};
char *copy;
char *tok;
int i;
// safely read a string and chop off any trailing newline
if(fgets(str, sizeof(str), stdin)) {
int n = strlen(str);
if(n && str[n-1] == '\n')
str[n-1] = '\0';
}
// copy the string so we can trash it with strtok
copy = strdup(str);
// look for the first space-delimited token
tok = strtok(copy, " ");
// check that we found a token and that it is equal to "insert"
if(tok && strcasecmp(tok, "insert") == 0) {
// iterate over all remaining space-delimited tokens
while((tok = strtok(NULL, " "))) {
// print the token 3 times
for(i = 0; i < 3; i++) {
fputs(tok, stdout);
}
}
putchar('\n');
}
free(copy);
return 0;
}

Dealing with strings in C

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.

Resources