C - Unable to copy a string in a function - c

This function is to split string based on \n and see if the row number is selected. If the row number matched, this string should be copied and used by other function:
void selectDeparment(char* departments, int selectedNum, char* selectedDepartment){
char* copyOfDepartments = malloc(strlen(departments)+1);
strcpy(copyOfDepartments,departments);
char* sav1 = NULL;
char* token = strtok_s(copyOfDepartments,"\n",&sav1);
int counter = 0;
while(token != NULL){
if(counter == selectedNum){
selectedDepartment = malloc(strlen(token)+1);
strcpy(selectedDepartment,token);
}
++counter;
token = strtok_s(NULL, "\n", &sav1);
}
}
This function is called in main like:
char* selectedDepartment;
selectDeparment(recordsPtr[0], 1, selectedDepartment);
printf(selectedDepartment);
recordsPtr[0] contains four strings with \n at the end:
aDeparment
anotherDepartment
newDepartment
otherDepartment
In C, we are encouraged to use pointer to get a value from function instead of returning a string from a function. However, the prinft in main function gives random output

I believe there is some confusion in the way you are using pointers here. Let me clarify.
In the main function, the character pointer selectedDepartment holds a certain memory in the computer. But when a function call is made to void selectDeparment(char* departments, int selectedNum, char* selectedDepartment), a new copy of selectedDepartment is created. Henceforth any changes which are made to selectedDepartment are done only at the scope of the called function and does not impact the original pointer in the main function.
Thus one clear way to solve this problem will be to pass a pointer to the character pointer defined in the main function. This will then give the correct/expected results.
Here is the modified version of the function -
void selectDeparment(char* departments, int selectedNum, char** selectedDepartment){
char* copyOfDepartments = malloc(strlen(departments)+1);
strcpy(copyOfDepartments,departments);
char* sav1 = NULL;
char* token = strtok_s(copyOfDepartments,"\n",&sav1);
int counter = 0;
while(token != NULL){
if(counter == selectedNum){
(*selectedDepartment) = malloc(strlen(token)+1);
strcpy(*selectedDepartment,token);
}
++counter;
token = strtok_s(NULL, "\n", &sav1);
}
}
And this is how it is called from the main function -
int main() {
char* recordsPtr[] = {"aDeparment\nanotherDepartment\nnewDepartment\notherDepartment"};
char* selectedDepartment;
selectDeparment(recordsPtr[0], 1, &selectedDepartment);
printf(selectedDepartment);
}

I think you are getting confused with the "A Pointer To What?" you are supposed to return. In your selectDeparment() function, if I understand what is needed, is you simply need to return a pointer to the correct department within recordsPTR. You do not need to allocate or tokenize to do that. You already have the index for the department. So simply change the return-type to char * and return departments[selectedNum];.
For example, you can whittle-down your example to:
#include <stdio.h>
char *selectDeparment (char **departments, int selectedNum){
return departments[selectedNum];
}
int main (void) {
char *selectedDepartment = NULL;
char *recordsPTR[] = { "aDepartment\n",
"anotherDepartment\n",
"newDepartment\n",
"otherDepartment\n" };
selectedDepartment = selectDeparment (recordsPTR, 1);
fputs (selectedDepartment, stdout);
}
Note: the '*' generally goes with the variable name and not the type. Why? Because:
int* a, b, c;
certainly does NOT declare three-pointers to int,
int *a, b, c;
makes clear that you have declared a single-pointer to int and two integers.
Example Use/Output
Running the example above you would have:
$ ./bin/selectedDept
anotherDepartment
You will want to add array bounds protection to ensure the index passed does not attempt to read past the array bounds. That is left to you.
If You Must Use void
If you must use a void type function, then you can pass the Address Of the pointer to the function so the function receives the original address for the pointer in main(). You can then assign the correct department to the original pointer address so the change is visible back in main(). When you pass the Address Of the pointer, it will require one additional level of indirection, e.g.
#include <stdio.h>
void selectDeparment (char **departments, int selectedNum, char **selectedDeparment) {
*selectedDeparment = departments[selectedNum];
}
int main (void) {
char *selectedDepartment = NULL;
char *recordsPTR[] = { "aDepartment\n",
"anotherDepartment\n",
"newDepartment\n",
"otherDepartment\n" };
selectDeparment (recordsPTR, 1, &selectedDepartment);
fputs (selectedDepartment, stdout);
}
(same result, same comment on adding array bounds protection)
Look this over and let me know if I filled in the missing pieces correctly. If not, just drop a comment and I'm happy to help further.

Related

Using a struct vs a pointer to a struct

I was working on the following as an example to see the differences between passing an object directly and then passing a pointer to it:
#include "stdio.h"
// Car object
typedef struct Car {
char* name;
unsigned int price;
} Car;
void print_car(Car car) {
printf("<Car: %s, Price: $%d>", car.name, car.price);
};
void print_car2(Car *car) {
printf("<Car: %s, Price: $%d>", car->name, car->price);
};
int main(int argc, char* argv[]) {
Car chevy = {chevy.name = "Chevy", chevy.price = 45000};
print_car(chevy);
Car mazda = {chevy.name = "Mazda", chevy.price = 30000};
print_car2(&mazda);
return 1;
}
Other than the first approach being much more readable and easier to understand for me, what are the differences between the two? When would passing a pointer be the only option, and why do both work in the above case?
There are two reasons to use the second approach:
If you want to avoid copying the whole struct. If the struct is big, this can affect performance.
If you want to modify struct that you're passing.
In general (not only for structs) passing a variable to a function will make a copy of this variable so if you want to alter this variable you ll have to return the value of the altered copy but you may want to alter the variable and return something else, in this case you have no other choice of passing a pointer as argument exemple :
first exemple with passing a variable
int useless_func(int nb) /*nb is actually a copy of the variable passed as argument */
{
nb++; /* the copy was incremented, not the real variable */
return nb; /* the new value is returned */
}
int main()
{
int nb = 1;
useless_func(nb); /* here nb is still = 1 cause it wasn't altered by the function */
nb = useless_func(nb); /* here nb is = 2 cause it took the return value of the func */
}
now a second stupid exemple with pointer :
char *useless_func(int *nb) /* nb is now a pointer to the variable */
{
*nb++; /* the derefencement of the pointer (so the variable value) was incremented */
return strdup("This is a useless return"); /* we return some other stupid stuff */
}
int main()
{
int nb = 1;
char *str = useless_func(&nb); /* now nb is = 2 and str is an allocated useless string woohoo */
}
When a function is called, the arguments in a function can be passed by value or passed by reference.
void print_car(Car car)
In here you are directly passing an object to the function, that means it will be copied into the functions stack and destroyed after function call ends. This method should be avoided because copying is expensive and unnecessary. Also if your objects are quite big, this operation will take a lot of time
void print_car2(Car *car) {
In this situation you are passing a pointer to the object which is called pass by reference, that means you are working with the original object and changes you make will directly affect it. It's a lot faster because you are not moving your object, but you should be careful about alter of original data

char* value changing after use of fgets

so my first question would be. Does fgets overwrite other char* values?
Otherwise, I'm not really sure how I have messed up my mallocs. Below is the code where the value is changing. First line is where the variable is being created.
data[dataIndex++] = createVariable(varName, 1, value, -1, line, NULL);
The code where the variable is being created
Variable *createVariable(char *name, int type, int val, int len, int line, char *string)
{
Variable *var = malloc(sizeof(Variable));
var->name = name;
var->setting = type;
var->num = val;
var->length = len;
var->line = line;
var->string = string;
return var;
}
What data looks like and how it was created.
Variable **data;
data = malloc(4 * sizeof(Variable *));
Forgot to add this, but below is my fgets code
if (fgets(line, MAX_LINE_LENGTH, in) == NULL)
{
break;
}
The problem is this line in your createVariable function:
var->name = name;
What this does is copy the pointer given as the first argument to the name field in the var structure; it doesn't make a (separate) copy of the data that is pointed to! So, assuming you call createVariable many times with the same variable as the first argument, then every object created will have the same address in its name field, and any modifications you make to any of them (via fgets) will change all of them.
To get round this, you need to allocate new memory for the name field, each time you call the createVariable function, then copy the string data to it. The simplest way to do this is using the strdup function:
Variable *createVariable(char *name, int type, int val, int len, int line, char *string)
{
Variable *var = malloc(sizeof(Variable));
var->name = strdup(name);
//...
var->string = strdup(string);
//...
But note, you will now need to be sure to free that memory from each object when you (eventually) delete it. Something like this:
void deleteVariable(Variable** var)
{
free((*var)->name); // free the name memory
free((*var)->string); // free the string memory
free(*var); // free the actual structure
*var = NULL; // set the pointer to NULL - to prevent multiple frees
}
EDIT: Just re-read your question, and noticed that you are making the same mistake with the string field! The same fix needs to be applied to that!

read access violation variable

When i'm using char* pilih(char teks[]) in the int main() function, it's working. But when I used this function in the new function, it said read access violation. Please help me
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#define MAXARR 1000
char *hilangkan(char[]);
char *pilih(char[]);
char *loadFile(FILE *sumber);
int main(){
FILE *sumber;
sumber=fopen("kumpulan.txt","r");
char *teks=loadFile(sumber);
char *pilihan=pilih(teks);
printf("%s",pilihan);
printf("%s",hilangkan(pilihan));
}
char *hilangkan(char teks[]){
char *penghilangan;
strcpy(penghilangan,teks);
int y=strlen(penghilangan);
srand(time(NULL));
int c=48;
for(int i=0;i<y;i++){
int hilang=rand()%y+1;
penghilangan[hilang]='_';
}
return penghilangan;
}
char *loadFile(FILE *sumber){
char *teks;
if (sumber == NULL) {
printf("ERROR!!!");
system("pause");
return 0;
}
char h;
int count=0;
while (h = fgetc(sumber) != EOF) {
teks[count] = h;
count++;
}
fclose(sumber);
return teks;
}
char *pilih(char teks[]){
char *hasil;
srand(time(NULL));
int w = rand() % 47 + 1;
char cek[3];
itoa(w, cek, 10);
char *c=strstr(teks, cek);
int lokasi = c - teks + 1;
int pan = strlen(cek);
int i;
if (pan == 2)i = -1;
else i = 0;
while (teks[lokasi]!='\n') {
hasil[i] = teks[lokasi];
i++;
lokasi++;
}
hasil[i] = NULL;
return hasil;
}
On while(teks[lokasi]!='\n') it says read violation access. teks was 0x1110113
Links are highly discouraged and expectation from you is that, you should post the whole program in your question.
From the part of code that you have posted and the error information shared, it seems that the argument passed to pilih() function is not valid which in turn makes teks pointer pointing to some invalid memory. Actual cause of problem can only be given after looking at minimal, complete and verifiable program depicting the problematic behavior.
One confirm problem in your pilih() function is that you are returning a local variable hasil from it. The scope and life of local variable hasil is limited to pilih() function and it is no more valid once pilih() function exits.
Also, this statement
hasil[i] = NULL;
is not correct. Variable hasil is array of char and hasil[i] is a character at index i. You are assigning NULL to it which is not valid. I think you wanted to do this:
hasil[i] = '\0';
EDIT:
The full code has been posted in the question. Hence editing my answer and pointing out the problems in the OP's code.
There are several issues in your code. If you are using gcc compiler, try compiling your code with "-Wall -Wextra" options and check the warnings messages given by the compiler.
In the function loadFile(), look at this statement:
while (h = fgetc(sumber) != EOF) {
First of all, the return type of fgetc() function is int and not char [the fgetc() return type is int to accommodate for the special value EOF]. In the operator precedence table, the operator != comes before =, so you will not get the read character assigned to h but the result of fgetc(sumber) != EOF will be assigned to h. This should be
while ((h = fgetc(sumber)) != EOF) { // check the parenthesis added
^ ^
In this statement:
teks[count] = h;
You are accessing a pointer which is not initialized. Any variable which is not initialized has indeterminate value and same is true for pointers. They can point to any random indeterminate address. There is no default behavior. Only behavior is indeterminate and using indeterminate values results in Undefined behavior.
You should allocate memory to teks before using it, like this:
teks = malloc(100 * sizeof (char)); //this will allocate memory for 100 characters.
if (teks == NULL)
exit(EXIT_FAILURE);
Looks like you want to store the whole file content in teks. Make sure to allocate enough memory to teks and if you are not sure about the size of memory required then realloc is your friend.
Also, after while loop, you should add the terminating null character at the end of the content stored in teks, like this:
teks[count] = '\0';
The same error of accessing uninitialized pointer exists in hilangkan() and pilih() function. Check the variable penghilangan and hasil respectively.
strstr() can return NULL pointer. You should add check for it:
char *c=strstr(teks, cek);
if (c == NULL)
return NULL; // Or whatever the way you want to handle this case
// but you should not use c
You should check the parameter value before using it in every function. If it is pointer, check whether it is NULL or not. If it is not NULL then only use it.
Also, make sure to free the dynamically allocated memory once you are done with it.
I don't what exactly you are trying to do but somewhere I feel that there is lot of scope of improvements in your code.
Hope this help.

multithreading - using a thread to execute a function storing contents of a file in char **array?

Let's say I have a file named file1.txt with contents:
one
two
three
Here is my code with a segfault that occurs inside
#include <stdio.h>
#include <pthread.h>
struct example{
char **file;
char **array;
};
void *func(void *arg){
struct example *ex = arg;
char name[1025];
FILE *fp = fopen(ex->file[0], "r");
int k = 0;
while (fscanf(fp, "%1025s", name) > 0){
ex->array[k] = name; // segfault happens here?
k++;
}
return NULL;
}
int main(){
char *sarray[5] = { NULL };
struct example t;
t.file[0] = "file1.txt"
t.array = sarray;
pthread_t tid;
pthread_create(&tid, NULL, func, &t);
pthread_join(tid, NULL);
}
If I do a check:
for (int i = 0; i < 3; i++){
printf("t array: %s and sarray: %s\n", t.array[i], sarray[i]);
}
I want the following output:
t array: one and sarray: one
t array: two and sarray: two
t array: three and sarray: three
Essentially, I want the contents of file to be stored into t.array from the function func and as a result, sarray will have the same values as well. Can anyone help me? I've tried and tested the while loop in func in main and it works, but I want it to work in func.
I would recommend to change few things.
Let's focus first on the main function and the structure example.
You are using struct example t where the fields **file and **array are not allocated.
Luckily, the statement t.file[0]="file1.txt" is okay.
However, if you want to manage n files, the statement t.file[i], with 0 <= i < n, you will get a segfault when 0 < i.
Why ? Because your t.file is not allocated. Therefore the statement t.file[0] is in reality *(t.file + 0) = *(t.file) that can point to the const char[] "file1.txt".
Next, you are doing t.array = sarray;
Keep in mind that using the symbol '=' is not equivalent to an allocation or a copy.
Now, let's have a look on the func function.
Why are you using 'void *' parameter ? Actually you know the type of arg variable.
Same remark as in the main function, you are using the symbol '=' in the statement ex->array[k] = name;
Here, it means that the k-th element points to name.
There is no copy. As a consequence, all elements of ex->array will have the same value as name. Assuming no segfault, you should get 'three three three' and not 'one two three'.
Last but not least, if you have more than 5 lines in your file, you also get a segfault.
Therefore, you should allocate your memory and replace the symbol '=' by strncpy to copy the content of a buffer into antoher memory space.

Keeping the value of a String initialized into a function

I know my title isn't clear, It will be clearer with code + examples.
I want to initialize a char* ("motSecret" in the main, "mot" in my function) containing a word selected randomly into a file, doing this into a function. This array is made dynamic using memory allocation.
The variable in my function get well initialized, but when I print the value just after I exited the function, the value change and become something like "0#"
Here is the part concerned in the main :
int main()
{
FILE* dico =NULL;
char *motSecret, *motRes;
char lettre=' ';
int check=0, nbCoups=10, longueur=0, nbMots=0;
Bool erreur = TRUE;
srand(time(NULL));
nbMots = scanDico(dico);
getWord(dico, nbMots, motSecret);
printf("Mot : %s", motSecret);
The problem appears after the function getWord(). Here is the code of this function :
void getWord(FILE* dico, int nbLignes, char *mot)
{
int numMotChoisi=rand() % nbLignes, nbChar=0;
char charActuel=' ';
dico = fopen("dico.txt", "r");
rewind(dico);
if(dico != NULL)
{
while (numMotChoisi > 0)
{
charActuel = fgetc(dico);
if (charActuel == '\n')
numMotChoisi--;
}
charActuel = ' ';
while(charActuel != '\n')
{
charActuel = fgetc(dico);
nbChar++;
}
fseek(dico,-(nbChar)-1,SEEK_CUR);
mot = malloc(nbChar * sizeof(char));
if(mot == NULL)
{
printf("Probleme d'allocation memoire");
exit(0);
}
fgets(mot, SIZE, dico);
mot[strlen(mot) - 1] = '\0';
printf("Mot = %s ", mot);
}
fclose(dico);
}
The printf at the end of the function return a good value, and the printf just after the getWord() in the main show that the value changed in the function haven't been "saved"...
Other thing, that works fine without memory allocation.
I hope I'm clear enough. If I forgot to tell something or if you need more informations, please tell me.
C uses pass by value in function parameter passing.
You need a double pointer, something like void getWord(FILE* dico, int nbLignes, char **mot) if you want to allocate memory inside another function.
As a cascased effect, printf("Mot : %s", motSecret); is trying to access uninitialized memory, causing undefined behaviour.
Suggestions:
I see no reason to use FILE *dico as a parameter in getWord(). In can very well be a local.
instead of using double pointer, i would like to recommend returning the allocated pointer from getWord(), i.e., change void getWord() to char * getWord(), add return mot and use like motSecret = getWord(<params>)
char *motSecret;
motSecret is a local variable withing main() and it is not initilized.
By calling
getWord(dico, nbMots, motSecret);
You are passing some uninitialized pointer to a function getword().
Inside getword() you are assigning some memory to
char *mot;
and writing some data to this memory.
Now this memory is not known to motSecret You have to return this memory address to the uninitialized pointer in main()
char *motSecret = getWord(dico, nbMots);
Your getword() should be like,
char *getWord(dico, nbMots);
and inside this after performing everything do,
return mot;

Resources