I have a struct and want to sort an array of the struct using merge sort.
I need to pass the sorting parameter to the merge function and access the struct members.
Can this be done in C?
example:
struct movie_imdb_data {
char color[15];
char director_name[100];
int num_critic_for_reviews;
int duration; /// in min not date and time
int director_facebook_likes;
int actor_3_facebook_likes;
char actor_2_name[100];
int actor_1_facebook_likes;
int gross;
};
in the main function, I have:
if (argc > 2) {
column_sort = argv[2];
}
now I want to call merge_sort(<array of struct>, <column_sort *>)
can I access the member in the array as array[0]->column_sort to compare?
now I want to call merge sort and pass the
I want to pass the array and the sorting parameter (that I need the array sorted by) can I use a variable in place of a members name ie..
arr[1].column_sort
instead of
arr[1].color
Sounds like you want to take a command line argument specifying the name of the field to sort on and then sort on that field.
For that, try code like:
#include <stdlib.h>
#include <string.h>
/* compare function for field 3: "num_critic_for_reviews" */
int compField3(const void *a, const void *b)
{
struct movie_imdb_data* aStruct = (struct movie_imdb_data*)a;
struct movie_imdb_data* bStruct = (struct movie_imdb_data*)b;
return (aStruct->num_critic_for_reviews < bStruct->num_critic_for_reviews)?
-1: (aStruct->num_critic_for_reviews > bStruct->num_critic_for_reviews)?
+1: 0;
}
/* also define other compare functions for each field */
int main()
{
const char* columnName = argv[2];
struct movie_imdb_data* parray;
parray = your-array;
int (*comp)(const void *, const void *, void *) = 0;
/* map the column name to compare function for that column */
if (strcmp(columnName, "num_critic_for_reviews") == 0)
{
comp = compField3;
}
/* map other names to compare function for column */
else if (...) {...}
else { exit(1); /* if not recognized column name */ }
qsort(parray, numElementsOfArray, sizeof(struct movie_imdb_data), comp);
...
}
Hope this helps!
There are different approaches to your problem:
you can write separate functions to compare structures on specific fields. In main, you would select the appropriate comparison function by testing the name of the field (or possibly generic names that are not field names). You would then pass this comparison function to mergesort (or qsort...).
if all members have the same type, you could determine the offset of the field from the beginning of the structure with macros offsetof(type, member). There is no generic way to compute these offsets, you need to write a series of tests or use a table. The comparison function would use casts to access the members:
size_t member_offset = offsetof(struct movie_imdb_data, duration);
int comp_int_member(const void *a1, const void *a2) {
const int *p1 = (const int *)((const unsigned char*)a1 + member_offset);
const int *p2 = (const int *)((const unsigned char*)a2 + member_offset);
return (*p1 > *p2) - (*p1 < *p2);
}
The downside of this latter approach is it can only handle fields with a given type.
I'm attempting to sort a directory listing based on the most recent st_mtime, and having trouble figuring out how exactly to sort the struct array I'm allocating. How can I sort the struct according to the most recent date?... here's my current approach:
static int cmpstringp(const void *p1, const void *p2){
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
struct directoryStat dStat{
char name[50];
char time[50];
};
int main(){
i = 0;
while ((ep = readdir(dp))){
stat(ep->d_name, &fileStat);
strcpy(dStat[i].name, ep->d_name);
strcpy(dStat[i].time, ctime(&fileStat.st_mtime));
i++;
}
}
qsort(dStat, i, sizeof(char), cmpstringp);
Sorting time (or datetime)-values based on their string representations might not be a good idea, because this (lexicographical) order based on a probably localization-dependent string representation of a date/time-value might then not correspond to the chronological order (cf., for example, David C. Rankin's comment). I'd store and compare the raw time_t-values instead.
See the following code, which demonstrates both, sorting by a string value and sorting by a time_t-value. Hope it helps!
typedef struct directoryStat {
char name[50];
char timeStr[50];
time_t timeVal;
}dStat;
int cmpDStatTimeStr(const void *p1, const void *p2){
return strcmp( ((dStat*)p1)->timeStr, ((dStat*)p2)->timeStr);
}
int cmpDStatTimeVal(const void *p1, const void *p2){
return ((dStat*)p1)->timeVal > ((dStat*)p2)->timeVal;
}
int main(){
dStat stat[2] = { { "the former", "2017/5/2", 14500000 }, { "the latter","2017/5/12", 14500001 }};
// sort based on string value (lexigraphical comparison):
qsort(stat, 2, sizeof(dStat), cmpDStatTimeStr);
// sort based on time value directly:
qsort(stat, 2, sizeof(dStat), cmpDStatTimeVal);
return 0;
}
I am playing with a custom threads api, i want to spawn a new thread with two pointers to message channels and an integer.
spawn_thread(Proctype Procedure, int argc, char *argv[]);
I am using 'channels' as an shared area between threads.
i have three variables:
`Chan *return, Chan *receive and an int n'
Could i simply cast all of the values i want to store in *argv[], to chars?
The reason i haven't done this is the width of the char is only 8 bits so a max value of 255, which isn't enough to hold pointer or large values.
One common approach to storing heterogeneous data in a C array is with tagged unions:
typedef enum {chDataReturn, chDataReceive, chDataInt} ChannelDataType;
struct ChannelData {
ChannelDataType type;
union {
ChanReturn ret;
ChanReceive rcv;
int num;
} data;
};
Now your spawn_thread prototype would look as follows:
spawn_thread(Proctype Procedure, int argc, struct ChannelData *argv);
Users would put the data into the array of argvs, "tagging" each item with its type, like this:
struct ChannelData *data = malloc(3*sizeof(struct ChannelData));
data[0].type = chDataReturn;
data[0].data.ret = myReturn;
data[1].type = chDataReceive;
data[1].data.ret = myReceive;
data[2].type = chDataInt;
data[3].data.num = 12345;
spawn_thread(myProc, 3, data);
// Wait for the thread to finish, and then...
...
free(data);
char * argv[] passes a char ** which you can just as easily cast to a void ** essentially allowing you to pass an array of void * to whatever you like.
char * argv[] = {&return, &receive, &n};
spawn_thread(Procedure, 3, argv);
Procedure(int argc, char *argv[]) {
char return = *argv[0];
char receive = *argv[1];
int return = *(int *)argv[2];
// ...
}
I am trying to pass multiple parameters when running a compiled C code
code would be like this
void main(char argc,char *argv[]){
printf("%s",argv[1]) //filename
FILE *file = fopen(argv[1], "r")
printf("%s",argv[2]) //function to be called
char* func_name = argv[2];
printf("%s",argv[3]) //how many times the function is called
int repeat = argv[3];
for(int i=0;i<repeat;i++){
func_name(file) //calls some function and passes the file to it
}
}
i would compile like this
gcc cprog.c -o cprog
run like -
./cprog textfile.txt function1 4
how do i do this ? any help would be appreciated !
First off:
You are missing some semicolons, so your code won't even compile.
argv[] are strings, so you'll have to convert them to integers if you want to use them as such.
C does not store function names in the binary, so you have to create some kind of calling table.
Below find a working example. I creates a struct that maps a name to a function, implement that function and go look for it. It's quite buggy (no input validation is done), but gives you a proof of concept on how to possibly implement this.
#include <stdlib.h>
#include <stdio.h>
struct fcn_entry {
char *name;
void (*fcn)(char *);
};
void fcn1(char *fn) {
printf("fcn1: %s\n", fn);
}
void fcn2(char *fn) {
printf("fcn2: %s\n", fn);
}
void main(char argc,char *argv[]){
// name-to-function table
struct fcn_entry entries[] = {
{ "fcn1", fcn1 },
{ "fcn2", fcn2 },
{ NULL, NULL }
};
void (*fcn_to_call)(char *);
int i = 0;
printf("%s",argv[1]); //filename
printf("%s",argv[2]); //function to be called
char* func_name = argv[2];
i = 0;
while(entries[i].name != NULL) {
if (strcmp(entries[i].name, func_name) == 0) {
fcn_to_call = entries[i].fcn;
break;
} else {
fcn_to_call = NULL;
}
i++;
}
printf("%s",argv[3]); //how many times the function is called
int repeat = atoi(argv[3]);
for(i=0;i<repeat;i++){
fcn_to_call(argv[1]);
}
}
There are a lots of error here.
int repeat = argv[3]; //You must convert char* to int before assignment.
func_name(file) //func_name is a char* not a function. C does not support reflection so there is no way to call function like this.
To be able to call a function that you have as a string, you have know which name is paired to which function.
If all functions take the same arguments, you can have an array of structures with name and function pointer, and then match the name with the correct entry in the table.
Otherwise, if the arguments are different you have to have a chain of strcmp calls to call the correct function.
struct MemoryTag1;
typedef struct MemoryTag1{
char a[8]= {'+','0','2','6','.','5','EA','\r'}; // setpoint temperature value
char b[8]= {'-','0','2','4','.','5','EB','\r'};
char c[6]= {'+','0','2','0','EC','\r'};
}Memory1;
// This is a message structure which I want to transfer over the serial interface (RS232) and later convert into integer value. please guide me in this.
Your syntax is a bit off - try this:
// declare Memory1 struct type to hold data
typedef struct MemoryTag1 {
char a[9]; // setpoint temperature value
char b[9];
char c[7];
} Memory1;
// allocate and initialise a Memory1 struct
Memory1 m = { {'+','0','2','6','.','5','E','A','\r'},
{'-','0','2','4','.','5','E','B','\r'},
{'+','0','2','0','E','C','\r'} };
Really, to be honest, I'd prefer more information. But it doesn't really matter. It only affects the method of output. If you were running this on an arduino, for instance, you could output to the serial ports as easily as:
Serial.begin(9600);
Serial.write('a');
etc, etc
As others have mentioned, there are situations in which you'd be better off using null-terminated strings. If however, you had a particular reason to do so, then I suppose you could;
#include <stdio.h>
typedef struct memoryTag1_t
{
char a[9]; // setpoint temperature value
char b[9];
char c[7];
} *pMemoryTag1_t;
typedef struct memoryTag2_t
{
char a[10]; // setpoint temperature value
char b[10];
char c[8];
} *pMemoryTag2_t;
void displayField1(char *field, int len)
{
for (int i=0; i<len; i++)
{
if (i!=0) printf(",");
printf("%c", field[i]);
}printf("\n");
}
void displayField2(char *field)
{
bool firstDone = false;
while (*field)
{
if (firstDone)
printf(",");
else
firstDone = true;
printf("%c", *field++);
}
printf("\n");
}
int main()
{
memoryTag1_t myMem1 =
{
{'+','0','2','6','.','5','E','A','\r'},
{'-','0','2','4','.','5','E','B','\r'},
{'+','0','2','0','E','C','\r'}
};
memoryTag2_t myMem2 =
{
"+026.5EA\r",
"-024.5EB\r",
"+020EC\r"
};
displayField1(myMem1.a, sizeof(myMem1.a));
displayField1(myMem1.b, sizeof(myMem1.b));
displayField1(myMem1.c, sizeof(myMem1.c));
displayField2(myMem2.a);
displayField2(myMem2.b);
displayField2(myMem2.c);
}
Output:
(Don't forget there's a \r printed 'after' the last comma in each line)
+,0,2,6,.,5,E,A,
-,0,2,4,.,5,E,B,
+,0,2,0,E,C,
+,0,2,6,.,5,E,A,
-,0,2,4,.,5,E,B,
+,0,2,0,E,C,
you can not declare struct in C in this way:
it should be
typedef struct MemoryTag1{
char a[9];
char b[9];
char c[7];
}Memory1;
you can set value in the declaration of an object of this structure:
Memory1 test = {
{'+','0','2','6','.','5','E','A','\r'},
{'-','0','2','4','.','5','E','B','\r'},
{'+','0','2','0','E','C','\r'}
};
If you use this bloc in each initiation of a Memory1 object so you can use macro to make it easier:
#define INIT_MEMORYTAG1 {\
{'+','0','2','6','.','5','E','A','\r'},\
{'-','0','2','4','.','5','E','B','\r'},\
{'+','0','2','0','EC','\r'}\
}
and then in your declaration of a Memory1 object:
Memory1 test = INIT_MEMORYTAG1;
BTW: You can not put 'EA', 'EB', 'EC' like a 1 charachter you have to separate them to:
'E','A', 'E','B', 'E','C' and so you have to update your char array sizes in the struct definition
That is really not C syntax.
You can't have initializers in declarations of types, that doesn't have any meaning.
You need to do it like this, to build the message:
typedef struct {
char a[10];
char b[10];
char c[8];
} Memory1;
int main(void)
{
Memory1 m1;
strcpy(m1.a, "+026.5EA\r");
strcpy(m1.b, "-024.5EB\r");
strcpy(m1.c, "+020EC\r");
return 0;
}
Note that the above will build proper C strings in the fields of the message, i.e. there will be 0-characters acting as terminators. The sizes were too small, so I changed that.
It's trivial to ignore the terminator characters if you need to send this over some format that doesn't allow them; send each field separately.
Converting one of the fields back into integers could be done using sscanf(), for instance.