Im trying to make a shell in linux. I need to save every command the user typed and when the user wants to see the history Ill show him the last 10 commands. Im using a char* historial[10]; but when the user type the second command it is saving in historial[0] and historial[1] the same thing. So if I type 10 different commands, it will save the last command 10 times. I dont know what Im doing wrong.
PS: my condition in the do while is not working neither.
int ejecutarCom(char* comando)
{ //executes the command
pid_t pid;
pid = fork();
int aux = 0;
if (pid < 0)
{
perror("Error");
return -1;
}
else if (pid == 0)
{
aux = execlp(comando, comando, NULL);
}
else
{
wait(NULL);
}
return aux;
}
char* historial[10]; //history vector
int cantidad = 0, tamano = 10; //quantity & size
void guardarEnHistorial(char* c)
{ //saves every command
if (cantidad < tamano)
{
historial[cantidad] = c;
}
if (cantidad == tamano)
{
cantidad = 0;
historial[cantidad] = c;
}
cantidad++;
}
void verHistorial()
{ //shows history
for (int i = 9; i >= 0; i--)
{
if (historial[0] == NULL)
break;
else if (historial[i] == NULL)
{
printf(" ");
}
else
printf("%i: %s\n", i, historial[i]);
}
}
int main()
{
char* comando = (char*) malloc(1024);
int x = 1;
do
{
printf("%s ", prompt);
fflush(stdout);
fgets(comando, sizeof(comando), stdin);
comando[strcspn(comando, "\n")] = '\0';
printf("%s\n", comando);
if (strcmp(comando, "hist") == 0)
{
verHistorial();
}
else
{
x = ejecutarCom(comando);
guardarEnHistorial(comando);
printf("%i\n", x);
}
} while (x != -1);
Modify your main function like below. Every location of your history array is pointing to same memory location hence you are getting the same information in every location. You have to allocate memory for each command like below:-
int main(){
char* comando=(char*) malloc(1024);
int x=1;
do{
printf("%s ",prompt);
fflush (stdout);
fgets(comando,sizeof(comando),stdin);
comando[strcspn(comando, "\n")] = '\0';
printf("%s\n",comando);
if(strcmp(comando,"hist")==0){
verHistorial();}
else{
x=ejecutarCom(comando);
guardarEnHistorial(comando);
comando=(char*) malloc(1024);// You need to allocate memory to store
//each history records. Also consider whether you really need 1024 bytes of memory make it small
printf("%i\n",x);
}}while(x!=-1);
You are storing a pointer to the command buffer, not the command itself. As the address of the command buffer doesn't change, but its contents does, every history entry will point to the same command buffer and thus will show the same command:
char* historial[10]; // history vector has only pointers
//...
historial[cantidad] = c; // stores a pointer
The following allocates and frees memory as appropriate:
void guardarEnHistorial(char* c)
{ //saves every command
if (cantidad == tamano) cantidad = 0;
if (historial[cantidad]) free(historial[cantidad]);
historial[cantidad]= malloc(strlen(c)+1);
strcpy(historial[cantidad], c);
cantidad++;
}
I dont know what Im doing wrong.
You are observing this behavior because the char pointers of historial array are pointing to same memory location comando. Check this statement of your program:
guardarEnHistorial(comando);
and in the guardarEnHistorial () function, you are doing:
historial[cantidad] = c;
Any changes you make to comando will reflect to all the members of historial array in which you have saved the command.
You can use strdup() to resolve this issue. Replace this:
guardarEnHistorial(comando);
with this:
guardarEnHistorial(strdup(comando));
^^^^
The strdup() function returns a pointer to a new string which is a duplicate of the string passed to it. strdup() allocates a memory for the new string using malloc and returns its pointer. Make sure to free it using free() once you are done with it. Like when inserting into historial array, you are rotating the insertion if the condition cantidad == tamano meets. Here, you need to free the element first and then insert the new element into the array otherwise there is a memory leaks in your program.
Related
So I am having two problems with this piece of code. The first problem when I'm following break points through it and I'm trying to copy the array to the struct fields, it is saying Error reading character string. And the second problem I am having is with the output "printMovieInfo function it is not printing out my list and it is causing an "unexpected breakpoint"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#pragma warning(disable : 4996)
#pragma warning(disable : 6387)
typedef struct {
char* g;
char* t;
}MovieInfo;
#define kTenItemsEntered 10
#define kCharacterString 30
//prototypes
void eliminateEndOfLine(char* buffer);
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]);
void printMovieInfo(MovieInfo list[]);
int main() {
MovieInfo newMovieInfo[kTenItemsEntered];
char title[kCharacterString];
char genre[kCharacterString];
printf("Enter 10 Title of Movie and 10 Genres\n");
for (int i = 1; i <= kTenItemsEntered; i++) {
printf("%d:\n", i);
printf("Title: ");
fgets(title, kCharacterString,stdin);
eliminateEndOfLine(title);
printf("Genre: ");
fgets(genre, kCharacterString, stdin);
eliminateEndOfLine(genre);
getMovieInfo(newMovieInfo, title, genre);
}
printMovieInfo(newMovieInfo);
return 0;
}
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]) {
ptr->t = (char*)malloc(strlen(title) + 1);
if (ptr->t == NULL) {
printf("Error allocating the title in the struct\n");
}
ptr->g = (char*)malloc(strlen(genre) + 1);
if (ptr->g == NULL) {
printf("Error allocating the genre in the struct\n");
}
strcpy(ptr->t, title);
strcpy(ptr->g, genre);
}
void printMovieInfo(MovieInfo list[]) {
printf("%-35s %-35s\n", "Title", "Genre");
for (int i = 0; i < kTenItemsEntered; i++) {
printf("%-35s %-35s\n", list[i].t, list[i].g);
}
}
//This is the end of new line function from examples
void eliminateEndOfLine(char* buffer)
{
char* target = strchr(buffer, '\n');
if (target != NULL)
{
*target = '\0';
}
}
Output
The output does not print
I'm guessing you're actually compiling in C++, otherwise this wouldn't compile as you need to use struct MovieInfo, not MovieInfo in C. You can fix that by doing:
typedef struct MovieInfo {
char* g;
char* t;
} MovieInfo;
In main your newMovieInfo is an array of MovieInfo structs. When you call getMovieInfo(newMovieInfo, title, genre);, you're actually passing a pointer to the first element of the array to the function (the array 'decays' into a pointer to the first element). What you should do instead is pass a pointer to the element you actually want to fill in. Array indices start at 0, not 1, so you need to fix your loop as well:
// previously: for (int i = 1; i <= kTenItemsEntered; i++) {
for (int i = 0; i < kTenItemsEntered; i++) {
...
// previously: getMovieInfo(newMovieInfo, title, genre);
getMovieInfo(&newMovieInfo[i], title, genre);
}
Then in getMovieInfo, since you already have a valid pointer to the struct, you don't need to allocate space for a new one:
void getMovieInfo(MovieInfo* ptr, char title[], char genre[]) {
// removed:
// ptr = (MovieInfo*)malloc(sizeof(MovieInfo));
// if (ptr == NULL) {
// printf("Error allocating the struct\n");
// }
ptr->t = (char*)malloc(strlen(title) + 1);
if (ptr->t == NULL) {
printf("Error allocating the title in the struct\n");
}
ptr->g = (char*)malloc(strlen(genre) + 1);
if (ptr->g == NULL) {
printf("Error allocating the genre in the struct\n");
}
strcpy(ptr->t, title);
strcpy(ptr->g, genre);
}
Finally in printMovieInfo, your current code is printing the first element's data multiple times. You can fix that by indexing into the array to get the element you want:
void printMovieInfo(MovieInfo list[]) {
printf("%-35s %-35s\n", "Title", "Genre");
for (int i = 0; i < kTenItemsEntered; i++) {
// previously: printf("%-35s %-35s\n",list->t,list->g);
printf("%-35s %-35s\n",list[i].t,list[i].g);
}
}
Each time you call getMovieInfo, you're storing the data in a local pointer to allocated memory. That pointer gets lost when the function returns, so newMovieInfo is never populated with anything.
The pointer being passed to the function points to an already existing instance of MovieInfo, so there's no need to allocate memory for it (though you still need to allocate memory for the strings).
Even after this fix, you're only ever updating the first element of the newMovieInfo. You need to pass a pointer to the array element in question when you call getMovieInfo:
getMovieInfo(newMovieInfo + i - 1, title, genre);
Note the -1 here because you're looping from 1 to kTenItemsEntered and array indices in C start at 0. If you changed the loop to go from 0 to kTenItemsEntered-1, you can remove the -1 from the line above.
Your printing function is also only printing the first element of the array, so change the line in the loop body to:
printf("%-35s %-35s\n",list[i].t,list[i].g);
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Below is the c code to kill a window in a versatile way.
When I call it in this way, it shows the message and then got stuck.
killWindowsVersatile.exe key ci ever 2 1000 "Super"
...closing window with argument:key,ci,ever,2,1000,Super,
name_key = 2;
cs_ci = 2;
ever_once = 1;
delay for 2 seconds start...
remaining 2 seconds ...
remaining 1 seconds ...
remaining 0 seconds ...
entering ever mode
Below is the code of the file:
It kills window either by name or a key word, and can delay for seconds, and can be case sensitive or insensitive. It can also loop the command by itself in a interval time of spacing.
There may be something wrong in the to_lowercase function.
#include <stdio.h>
#include <stdlib.h>
#define WINVER 0x0600
#define _WIN32_IE 0x0500
#include <windows.h>
#include <stdint.h>
#include <ctype.h>
int name_key,cs_ci,ever_once,delay;
//the order of function is important! you cannot call something before defined !
const char * to_lowercase(char* str){
int len = strlen(str);
char * newStr[len];
for(int i = 0; str[i]; i++){
newStr[i] = tolower(str[i]);
}
return newStr;
}
BOOL CALLBACK WindowFoundCB(HWND hwnd, char* param){
char *key = (char*) param;
char title[256]; // This way, you have allocated 256 bytes for the Window name.
GetWindowText(hwnd, title, 256);
HWND me = GetConsoleWindow();
if (IsWindowVisible(hwnd) && hwnd != me){
if(cs_ci==1){
char *p = strstr(title,key);
if (p!= NULL){
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
printf("==== close a window by key [%s]: <%s>\n",key,title);
}
}else if(cs_ci==2){
char title_lower[256];
char key_lower[256];
strcpy(title_lower,to_lowercase(title));
strcpy(key_lower,to_lowercase(key));
//char *title_lower = to_lowercase(title);
//char *key_lower = to_lowercase(key);
char *p = strstr(title_lower,key_lower);
if (p!= NULL){
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
printf("==== close a window by key [%s]: <%s>\n",key,title);
}
}
}
return TRUE;
}
int dealwithonce(int name_key,int cs_ci,char *argv[],int argc){
for(int i = 6; i < argc; i++){
if(name_key == 1){
HWND hwnd = FindWindow(NULL, argv[i]);
if(hwnd){
char strIte[256]; // This way, you have allocated 256 bytes for the Window name.
GetWindowText(hwnd, strIte, 256);
if( (cs_ci==2) || ((cs_ci==1)&& !strcmp(strIte,argv[i])) ){
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
printf("==== close a window by name [%s]\n",argv[i]);
}
}
}else if(name_key == 2){
char* aStr = argv[i];
EnumWindows(WindowFoundCB,aStr);
}
}
}
int main(int argc, char *argv[])
{
// argumrnt check
printf("...closing window with argument:");
for(int i = 1; i < argc; i++){
printf("%s,", argv[i]);
}
printf("\n");
// argument parse
if(argc < 7){
printf("%s\n","you must input 5 arguments at least!");
printf("%s\n","arg1: [name] or [key]");
printf("%s\n","arg2: [cs] for case-sensitive or [ci] for case-insensitive");
printf("%s\n","arg3: [ever] for forever or [once] for once");
printf("%s\n","arg4: the seconds for delay before start., 0,1,2...");
printf("%s\n","arg5: the milliseconds of Cycle for ever mode., 1,2,500,1000,...");
printf("%s\n","arg6+: the assembly of any strings");
return 0;
};
if(!strcmp(argv[1],"name")){
printf("name_key = 1;\n");
name_key = 1;
}else if(!strcmp(argv[1],"key")){
printf("name_key = 2;\n");
name_key = 2;
}
if (!strcmp(argv[2],"cs")){
printf("cs_ci = 1;\n");
cs_ci = 1;
}else if(!strcmp(argv[2],"ci")){
printf("cs_ci = 2;\n");
cs_ci = 2;
}
if (!strcmp(argv[3],"ever")){
printf("ever_once = 1;\n");
ever_once = 1;
}else if(!strcmp(argv[3],"once")){
printf("ever_once = 2;\n");
ever_once = 2;
}
// delay
delay = atoi(argv[4]);
if(delay > 0){
printf("delay for %d seconds start...\n",delay);
for(int i=delay;i>=0;i--){
printf("remaining %d seconds ...\n",i);
Sleep(1000);
}
}
//cycle
int interval = atoi(argv[5]);
// ever or not
if(ever_once == 1){
printf("entering ever mode\n");
while(1){
dealwithonce(name_key,cs_ci,argv,argc);
Sleep(interval);
}
}else if(ever_once == 2){
printf("entering once mode\n");
dealwithonce(name_key,cs_ci,argv,argc);
}
return 0;
}
strcmp returns a value that represents how argument 1 compares to argument 2 alphabetically. If they are equal, it returns 0 which is numerically equivalent to the false boolean. Thus you need to modify your if statements like this:
if(!strcmp(argv[1],name)){
printf("name_key = 1;");
name_key = 1;
}else if(!strcmp(argv[1],key)){
printf("name_key = 2;");
name_key = 2;
}
if (!strcmp(argv[2],cs)){
printf("cs_ci = 1;");
cs_ci = 1;
}else if(!strcmp(argv[2],ci)){
printf("cs_ci = 2;");
cs_ci = 2;
}
if (!strcmp(argv[3],ever)){
printf("ever_once = 1;");
ever_once = 1;
}else if(!strcmp(argv[3],once)){
printf("ever_once = 2;");
ever_once = 2;
}
This way if the two strings are equivalent alphabetically, then strcmp will return 0 which will be negated to assume the value of true and the program execution will enter the if block. If the two strings are NOT equal, then strcmp will return either a positive or a negative value which is equivalent to the boolean true. Negating this will assume the value of false and the program execution will not enter the if block.
Hope this helps!
EDIT:
To help you understand why your to_lowercase function might not be working properly, you have to understand two things about arrays:
Firstly, the array contents are not passed to the function. The C language (and thus all compilers/interpreters) only passes the single value of any variable to a function when it is called. In this case, the array only contains the memory address of the first element of the array. That address is passed to the function instead of the array as a whole.
Secondly, an array declaration is simply a pointer to the first location of a contiguous set of locations in memory. When you reference the first element of an array you are accessing the first memory location pointed to by the pointer. When you reference the second, third, or even fourth element of the array, you are accessing the locations in memory that are exactly 2 or 3 or even 4 memory locations away from the first element.
So what can we do with this? What happens when we change the values pointed to by the pointer? Remember, when you modify an array, (without intervention of some memory allocation tricks) the memory locations pointed to by the pointer do not change. Only the values in those memory locations change. This is why we cannot copy arrays by simply typing: array1 = array2. All this is doing is copying the pointer in array2 to array1. Any modifications you make to array2 will have a side effect of also being made in array1 because they both now point to the same locations in memory!
So if you think you have an error in your to_lowercase function, try this modification to it. I cannot test your complete code because my GCC compiler is for Linux, but it should work for you:
void to_lowercase(char *str)
{
for(int i = 0; str[i]; i++)
{
str[i] = tolower(str[i]);
}
}
HOW DOES THIS WORK?
When we pass an array to a function, we are passing the value of the array pointer. Thus the function only receives a location in memory. By utilizing this pointer, we can advance through memory to the other locations pointed to by that pointer (via str[i]) and modify them freely without affecting the value of the pointer itself.
Hope this helps.
You're correct - your to_lowercase function is doing multiple things wrong:
The way this function is currently written newStr is defining an array of char *, not an array of char.
You're then returning newStr, which is an automatic variable defined on the stack. As soon as the function returns newStr is subject to being overwritten by any other subroutine calls which are made.
You could choose to define to_lowercase as
const char * to_lowercase(char* str){
char *newStr = malloc(strlen(str)+1);
for(int i = 0; str[i]; i++)
newStr[i] = tolower(str[i]);
return newStr;
}
but this means that you'd have to be careful to free the memory allocated in to_lowercase each time you use it.
A better idea might be to write to_lowercase in such a manner that it changes the string it's passed to lower case in-place:
char *to_lowercase(char* str){
for(char *p = str; *p; p++)
*p = tolower(*p);
return str;
}
This overwrites the original content of str so you'll need to be sure to account for that in your code.
// Struct for Country Data
typedef struct
{
char name[50]; // Country name
char code[3]; // Country code
int population; // Country Population
double lifeExp; // Country Life expectancy
} CountryData;
// Struct for Dir File
typedef struct
{
char code[3];
int offSet;
} DirData;
// Function Declarations
void fillCountryStructs(CountryData ** dataPtr, int nLines, int fd);
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines,int fd2);
void sortStructs(DirData **director, int nLines);
int verifyString(char *s1, char *s2);
// Main Function
// - This function starts the program, get the number of lines as a
// parameter, fills the structs and writes the data to the Country
// File and the Directory file.
int main(int argc, char *argv[]) // Always remember to pass an argument while executing
{
// Some variables
int nLines; // The number of lines
char *pEnd; // For String functions
FILE *Fin,*Fout; // File pointers
int fd;
int fd2;
nLines = strtod(argv[1], &pEnd);
CountryData **countryDataPtr; // Array of structs
CountryData **tempStruct;
DirData **director;
// Allocate memory for the struct pointers
countryDataPtr = calloc(nLines, sizeof(CountryData*));
director = calloc(nLines, sizeof(DirData*));
// File Stream for "AllCountries.dat"
if((fd = open("AllCountries.dat", O_RDWR)) ==-1)
err_sys("File not found...\n");
// File Stream for "RandomStruct.bin"
if ((fd2 = open("RandomStruct.bin", O_RDWR)) == -1)
err_sys("Failed to open binary\n");
// Filling the Country stucts
fillCountryStructs(countryDataPtr, nLines, fd);
close (fd);
//fclose(Fin); // Closing the file "AllCountries.dat"
// Writing Binary File
write(fd2, (countryDataPtr[0]->name[0]), sizeof(CountryData));
close (fd2);
//fclose(Fout);
printf("RandomStruct.bin written Sucessfully\n");
// Filling the Directory File
// File Stream for "RandomStructDir.dir"
if ((fd2 = open("RandomStructDir.dir",O_RDWR|O_TRUNC)) != -1)
err_sys("Failed to open binary\n");
fillDirectoryStructs(countryDataPtr, director, nLines, fd2);
sortStructs(director, nLines); // Sorting the structs
// Write the number of lines in the FIRST LINE
// of the Directory File
write(fd2, nLines, sizeof(nLines));
// Writing Directory File after the number of lines was written
write(fd2,(director[0]->code[0]), sizeof(DirData));
close (fd2);
//fclose(Fout);
printf("RandomStructDir.dir written Sucessfully\n\n");
exit(0);
}
// Filling the Country structs
// - This function extracts the data from the file using strtok
// and fills all the structs with their corresponding values.
void fillCountryStructs(CountryData **dataPtr, int nLines, int fd)
{
int curLine = 0; // Current line
int index = 0; // The index
char buf[BUFSIZE]; // The Buffer with the size of BUFSIZE
char *tok; // Token
char *pEnd; // For the String functions
char ch = 'a'; // The temp character
int temPop;
double temLifeExp;
int num=0;
for(curLine = 0; curLine < nLines; curLine++)
{
// Reading each line
dataPtr[curLine] = (CountryData *)calloc(1, sizeof(CountryData));
index = 0;
do
{
read(fd, &ch, 1);
buf[index++] = ch;
}
while(ch != '\n');
// Strtoking...
tok = strtok(buf, ",\n");
index = 1;
while(tok != NULL)
{
tok = strtok(NULL, ",\n");
// Get the Country Code
if(index == 1)
{
strcpy(dataPtr[curLine]->code, tok); // Copying code to the struct
}
// Get the Country Name
if(index == 2)
{
strcpy(dataPtr[curLine]->name, tok); // Copying name to the struct
}
// Get the Country Population
if(index == 7)
{
temPop = (int)strtol(tok, &pEnd, 10);
dataPtr[curLine]->population = temPop; // Copying population to the struct
}
// Get the Country Life expectancy
if(index == 8)
{
num=countchar(tok);
printf ("The number of characters entered is %d\n", num);
printf ("The character entered is %s\n",tok);
temLifeExp = strtod(tok, &pEnd);
dataPtr[curLine]->lifeExp = temLifeExp; // Copying life expectancy to the struct
}
index++;
}
}
}
int countchar (char list[])
{
int i, count = 0;
for (i = 0; list[i] != '\0'; i++)
count++;
return (count);
}
// Filling the Directory Structs
// - This function fills the directory with the offset
void fillDirectoryStructs(CountryData **dataPtr, DirData **director, int nLines, int fd2)
{
int i = 0;
for(i = 0; i < nLines; i++)
{
strcpy(director[i]->code, dataPtr[i]->code); //It crashes in this Line
director[i]->offSet = sizeof(CountryData) * (i);
}
}
// Sorting the Dir Structs
// - This function sorts the Directory Structs.
void sortStructs(DirData **director, int nLines)
{
int maxNumber;
int i;
DirData **temp;
temp = calloc(1, sizeof(DirData));
// Sorting the array of pointers!
for(maxNumber = nLines - 1; maxNumber > 0; maxNumber--)
{
for(i = 0; i < maxNumber; i++)
{
if((verifyString(director[i]->code, director[i+1]->code)) == 1)
{
temp[0] = director[i];
director[i] = director[i+1];
director[i+1] = temp[0];
}
}
}
}
// Veryfying the strings
// - This function compares two strings and return a specific value
// accordingly.
int verifyString(char *s1, char *s2)
{
int i;
if(strcmp(s1,s2) == 0)
return(0); // They are equal
for(i = 0; s1[i] != 0; i++)
{
if(s1[i] > s2[i])
return(1); // s1 is greater
else if(s1[i] < s2[i])
return(2); // s2 is greater
}
return (2); // s2 is greater
}
So I get segmentation fault and I have no Idea why? maybe is something about the pointers. I specified where it crashes (void fillDirectoryStructs) that method the first line.
When I compile I get :
Countries.c: In function 'main':
Countries.c:68: warning: passing argument 2 of 'write' makes pointer from integer without a cast
Countries.c:84: warning: passing argument 2 of 'write' makes pointer from integer without a cast
Countries.c:86: warning: passing argument 2 of 'write' makes pointer from integer without a cast
Countries.c:232:2: warning: no newline at end of file
I don't know a lot about pointers but I have to use system calls, so I can't use any of the FILE * functions (fwrite(), etc) that is why I'm using plain write() and read().
When I run it I get segmentation fault when It gets to that point I just specified.
for test purposes I'm trying to print this
printf("test: %s\n", countryDataPtr[0]->code[0]);
instead of writing and it crashes there, why? what am I doing wrong? shouldn't that get the code of that first country in my struct? thanks
Well, you need to listen to your compiler and take its warnings seriously.
This:
write(fd2, nLines, sizeof(nLines));
is wrong, and would explain the warning. The variable nLines has type int, but if you look at the [documentation for write()] you can see that the 2nd argument has type void *.
So it will interpret your integer value as a pointer, and start reading memory which you have no right to be reading.
You need:
write(fd2, &nLines, sizeof nLines);
Note that sizeof is not a function, it only needs parenthesis when the argument is a type name (since it then needs a cast expression to the type in question, and casts are writen as a type name enclosed in parenthesis).
Also, you need to be prepared for the reality that I/O can fail. The write() function has a return value which you should be checking.
There are a number of other problems with your code, in addition to the serious one unwind pointed out.
This:
CountryData **countryDataPtr; // Array of structs
is not an Array of structs. Once allocated, it could be an array of pointers to structs.
This:
write(fd2, (countryDataPtr[0]->name[0]), sizeof(CountryData));
does not write one CountryData instance (much less a whole array of them). It takes the integer value of the first character of the first element's name, and treats it as a pointer just like you do with nLines.
If you want to write the first element, it would look like this:
write(fd2, countryDataPtr[0], sizeof(CountryData));
and if you wanted to write all the elements, you'd either need a loop, or a contiguous array of structs you can write in one go.
I've got two different arrays that I'm using. With one, I'm getting the exact results that I want, the other, not so much. I'm filing the arrays with by reading from a text file similar to this:
2597
283
4
723
21
82
426
The first five lines would be the customer IDs. There is always 5 lines but they don't always have a value. The next line is the number of vendors, then followed by the vendor ids.
void use_arrays()
{
int
i,
customer_count,
*customer_ids,
vendor_count,
*vendor_ids;
customer_ids = malloc(sizeof(int));
vendor_ids = malloc(sizeof(int));
fill_arrays(&customer_count, customer_ids, &vendor_count, vendor_ids);
for (i = 0; i < customer_count; i++)
{
printf("Customer[%d]: %d\n", i, customer_ids[i]);
}
for (i = 0; i < vendor_count; i++)
{
printf("Vendor[%d]: %d\n", i, vendor_ids[i]);
}
free(customer_ids);
free(vendor_ids);
}
void fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int *vendor_ids)
{
int
i,
*temp,
customer_id,
vendor_id,
num_cust = 0;
FILE
*inp_file;
char
*endptr = NULL,
buffer[500];
inp_file = fopen(g_filename, "r");
for (i = 0; i < 5; i++) /* Can't be more than 5 customers */
{
fgets(buffer, sizeof(buffer), inp_file);
customer_id = strtol(buffer, &endptr, 0);
if (customer_id != 0)
{
customer_ids[i] = customer_id;
temp = realloc(customer_ids, (i+2)*sizeof(int));
if (temp != NULL)
{
customer_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
num_cust++;
}
}
*customer_count = num_cust;
/* Next is number of vendor ids*/
fgets(buffer, sizeof(buffer), inp_file);
*vendor_count = strtol(buffer, &endptr, 0);
temp = realloc(vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
vendor_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
for (i = 0; i < *vendor_count; i++)
{
fgets(buffer, sizeof(buffer), inp_file);
vendor_id = strtol(buffer, &endptr, 0);
if (vendor_id != 0)
{
vendor_ids[i] = vendor_id;
}
}
fclose(inp_file);
}
Once the arrays print out, customer_ids is showing the correct numbers but vendor_ids is printing out random numbers from memory. To be more frustrating, it prints the vendors correctly from inside fill_arrays.
If you want to modify vendor_ids the way you do in fill_arrays, then you have to pass it in as a pointer to a pointer:
fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int **vendor_ids)
Call it like this:
fill_arrays(&customer_count, customer_ids, &vendor_count, &vendor_ids);
Then you can realloc like so:
temp = realloc(*vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
*vendor_ids = temp;
}
Also, at the end of your function:
vendor_ids[i] = vendor_id;
will have to change to
(*vendor_ids)[i] = vendor_id;
You will also have to make the same changes to customer_ids. The fact that customer_ids was working while vendor_ids wasn't was probably due to your use of realloc. If realloc decides that the memory block has to be reallocated in a new location, you'll run into these problems but if reallocates the memory in the same location, your pointer that you passed in is still pointing there. Since you never know if realloc is going to make that descision or not, both customer_ids and vendor_ids should be passed in as pointers to pointers.
You seem a bit confused how to return memory from a function.
if you have a function that looks like this
void foo(int a);
you can not change a inside of foo, foo only gets a copy of a.
void foo(int *a);
otoh gives foo the address of a, i.e. "i know where you live" it lets you change what a points to. if a points to an array then you can change the contents of that array, if a points to a single integer you can change that.
void foo(int **a);
lets you change not only what a points to, but also where it points. so if you want a to point to somewhere else you can. this is what you need in your function, if you do a malloc/calloc/realloc in your function to return the result you need this
I want to pass the contents of an array to another method and have that method print out the entire array - how would i do this?
Currently:
I'm returning an array from a function.
char* search_value(struct PDB *llist)
{
int realID = -7;
int x = 0;
int task = 0;
char *received;
char theMessage[100];
theMessage[0] = '\0';
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%d", &task);
while(llist->data1 != NULL)
{
if(task == llist->taskID)
{
realID = llist->taskID;
strcpy(theMessage, llist->data1);
break;
}
}
return theMessage;
}
i'm getting the return value:
void getMessage(const int GET_MESSAGE)
{
char * received = NULL;
int x = 0;
received = search_value(llist);
printf("%s", received);
}
I want to somehow print the entire value (rather than just the first value to which the pointer is pointing at - how would i do this?
A few corrections and it should work:
// - struct contents shouldn't be changed by the function, make its pointer const.
// - pass a pointer to an allocated array as parameter
char* search_value(const struct PDB *llist, char* theMessage)
{
int realID = -7;
int x = 0;
int task = 0;
char *received;
theMessage[0] = '\0';
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%d", &task);
while(llist->data1 != NULL)
{
if(task == llist->taskID)
{
realID = llist->taskID;
strcpy(theMessage, llist->data1);
break;
}
}
return theMessage;
}
void getMessage(const int GET_MESSAGE)
{
char received[100]; // allocate the array outside the function
int x = 0;
search_value(llist, received); // pass a pointer to the first element
printf("%s", received);
}
You have an issue with variable scope here: theMessage is local to the function search_value, so you're returning a pointer to an array which no longer exists once the function completes.
Instead you should use malloc() to allocate the space for theMessage and then subsequently free() it later on outside of the function when you're finished with it — however this can often lead to memory leaks if you're not diligent about cleaning up after yourself.
You can allocate the memory like so:
char * message = malloc(100);
One alternative would be to allocate the buffer in getMessage() and pass a pointer to the buffer into search_value which could then write into it:
void getMessage(const int GET_MESSAGE)
{
char received[100];
int x = 0;
search_value(llist, received);
printf("%s", received);
}
void search_value(struct PDB *llist, char * buffer)
{
// write to buffer
}
Another option is to declare a char * pointer inside getMessage(), pass a pointer to a pointer into search_value() and again use malloc() to allocate space for the buffer.
Finally, this is a minor style criticism, but you'd do well to learn to stick to one convention for naming your functions, search_value and getMessage are not consistent names, and this will irk many a coder that you work with.
You have several problems with your code. I'm guessing that you want to search a list for some value, then return that value.
The first problem is that you do not actually iterate over the list, but only check the same item over and over again. The other problem is that you return a pointer to a local variable. This is undefined behavior, because as soon as the function returns the memory the pointer points to can be used for something else.
I suggest you change your code as follows:
char *search_value(struct PDB *llist, char *theMessage, size_t theMessageMaxLength)
{
int realID = -7;
int task = 0;
printf("Your choice: `Search'\n");
printf("Enter the value you want to find: ");
scanf("%d", &task);
while(llist != NULL && llist->data1 != NULL)
{
if(task == llist->taskID)
{
realID = llist->taskID;
strncpy(theMessage, llist->data1, theMessageMaxLength);
theMessage[theMessageMaxLength] = '\0';
break;
}
llist = llist->next; /* Assuming the field is named "next" */
}
return theMessage;
}
void getMessage(const int GET_MESSAGE)
{
char *received = NULL;
char theMessage[100];
/* Subtract 1 from the size, for the terminating '\0' */
received = search_value(llist, theMessage, sizeof(theMessage) - 1);
printf("%s", received);
}
the array you are returning is local to that function. Either the calle function shall provide the array in which it expects the values or use static array.