I have the following structs. A tDiscountsShop contains, let's say 5 tDiscount structs.
#define MAXDISCOUNTS 50
typedef enum {FALSE, TRUE} bool;
typedef struct {
int dni;
float discount;
bool changed;
} tDiscount;
typedef struct {
tDiscount discounts[MAXDISCOUNTS];
int numDiscounts;
} tDiscountsShop;
I would like to sort by dni using qsort. I'm trying using the following code:
int compare(const void *s1, const void *s2)
{
tDiscount *e1 = (tDiscount *)s1;
tDiscount *e2 = (tDiscount *)s2;
return e1->dni - e2->dni;
}
qsort (discountsShop->discounts, discountsShop->numDiscounts, sizeof(discountsShop->discounts), compare);
I'll appreciate if I could have an explanation of what I'm doing wrong and how I could solve this issue. Thanks in advance.
This
sizeof(discountsShop->discounts)
gives you the size of the whole array.
What you need/want is the size of one element.
To get this do
sizeof(*discountsShop->discounts)
or
sizeof(discountsShop->discounts[0])
From qsort()'s documentation:
void qsort(void *base, size_t nel, size_t width,
int (*compar)(const void *, const void *));
[...]
The size of each object, in bytes, is specified by the width argument.
You are passing the size of the whole array instead of the size of each element.
sizeof(discountsShop->discounts)
should be
sizeof(discountsShop->discounts[0])
Related
I have an error when I compile my C program.
I have this code :
#include <stdio.h>
#include <stdlib.h>
#define SET__SIZE 10
#define SET__BOUND ((void*) NULL)
struct set {
void *s[SET__SIZE];
int cursor;
int (*cmp)(const void*, const void*);
void * (*copy)(const void*);
void (*del)(void *);
};
int find(const void *s[], void *c, int (*cmp)(const void*, const void*))
{
int i = 0;
while (s[i]!=SET__BOUND && cmp(s[i],c)<0)
i++;
return i;
}
int set__find(const struct set *se, void *c)
{
return (se->cmp(se->s[find(se->s,c,se->cmp)],c)==0);
}
For some reason, gcc is raising a warning for the find call in set__find saying :
note: expected ‘const void **’ but argument is of type ‘void * const*’
I can't understand why he thinks the argument is a constant pointer (if I understood the error right)
As I tried, the note goes away, when I change the code to
struct set {
void *s[SET__SIZE];
int cursor;
int (*cmp)(const void*, const void*);
void * (*copy)(const void*);
void (*del)(void *);
};
int find(void * const s[], void *c, int (*cmp)(const void*, const void*))
{
int i = 0;
while (s[i]!=SET__BOUND && cmp(s[i],c)<0)
i++;
return i;
}
int set__find(const struct set *se, void *c)
{
return (se->cmp(se->s[find(se->s,c,se->cmp)],c)==0);
}
Explanation
In the answer... No point of writting it again.
why he thinks the argument is a constant pointer
struct set {
void *s[SET__SIZE];
The structure set contains the array s.
const struct set *se
se points to a constant structure set. Because the structure is constant, the memory for the structure is constant. The elements of the array s can't be modified, they are in constant memory.
find(se->s,
Arrays decay to the pointer to the first element. So imagine it's TYPE s[SET__SIZE] where TYPE is a void*. TYPE s[SET__SIZE] decays to a pointer TYPE *. But, it's constant, so it's const TYPE s[SET__SIZE]. So it decays to a const TYPE *. You can't modify it, it's a constant array. TYPE is a void* - you can dereference the element and modify it then, but you can't modify the pointer value itself.
TL;DR you want int find(void * const s[] as in the other answer.
I am trying to pass pointer to an array of structures to another function
#include<stdio.h>
#include<string.h>
typedef struct CovidData{
char region[7];
char towns[12];
char race[12];
int yearlyIncome;
int members;
int testedMembers;
int testedPositive;
} CovidData;
void RandomDataGenerator(CovidData *data[] ,int count)
{
for(int i=0;i<count;i++){
memcpy(data[i]->region,"david",sizeof("david"));
memcpy(data[i]->towns,"david",sizeof("david"));
memcpy(data[i]->race,"david",sizeof("david"));
data[i]->yearlyIncome=1000;
data[i]->members=99;
data[i]->testedMembers=88;
data[i]->testedPositive=656;
}
}
int main() {
struct CovidData data[100];
RandomDataGenerator(&data,2);
for(int i=0;i<5;i++){
printf("%s",data[i].region);
}
}
But it throws an error while compiling in terminal with gcc
incompatible pointer types passing 'CovidData (*)[100]' to
parameter of type 'CovidData
CovidData *data[] is grouped as CovidData *(data[]), so it declares an array of pointers to CovidData. For a pointer to an array, you would use CovidData (*data)[].
However, we rarely pass a pointer to an array. Usually, it is sufficient and convenient merely to pass a pointer to the first element. Thus, you would declare the parameter as CovidData *data and pass it as RandomDataGenerator(data, 2).
If you did declare the parameter as a pointer to an array, you would not use it with data[i]->region. You would need *data to get the array before applying the subscript, and again you would need parentheses for correct grouping: (*data)[i]->region.
Change your function to void RandomDataGenerator(CovidData *data ,int count); and pass only the pointer to the first array element, like this RandomDataGenerator(data,2);
#include<stdio.h>
#include<string.h>
typedef struct CovidData{
char region[7];
char towns[12];
char race[12];
int yearlyIncome;
int members;
int testedMembers;
int testedPositive;
} CovidData;
void RandomDataGenerator(CovidData *data ,int count)
{
for(int i=0;i<count;i++){
memcpy(data[i].region,"david",sizeof("david"));
memcpy(data[i].towns,"david",sizeof("david"));
memcpy(data[i].race,"david",sizeof("david"));
(data+i)->yearlyIncome=1000;
data[i].members=99;
data[i].testedMembers=88;
data[i].testedPositive=656;
}
}
int main() {
struct CovidData data[100];
RandomDataGenerator(data,2);
for(int i=0;i<5;i++){
printf("%s\n",data[i].region);
}
}
I want to use sizeof operator after passing an array to a function but C language is considering it as pointer not an array.
Can we implement a functionality to solve this purpose ?
void foo(char array[])
{
printf("sizeof array = %u\n", sizeof(array)); /* I know array is pointer here */
}
int main(void)
{
int array[5];
foo(array);
}
Not possible and using an array subscript [] can be confusing to people not knowing that it will behave as a pointer. The approach that I take is to pass length as another argument or create a struct with pointer and length:
struct {
char * buffer;
size_t length;
} buffer_t;
I don't like the array in the struct or typedef approach as it restricts the data to a fixed size.
You can't ask the compiler for that. It is not polite to do what you ask it to.
Instead you can add another parameter to your function definition to recieve the size of the array.
void foo(char array[], size_t array_size)
{
printf("sizeof array = %zu\n", array_size);
}
int main(void)
{
int array[5];
foo(array, sizeof(array)) ;
}
Note: to print size_t use %zu in printf.
Note: to print number of elements, use: sizeof(array) / sizeof(*array)
You can use a dedicated typedef, or as said in a remark an embedding struct :
#include <stdio.h>
typedef char A5[5];
typedef struct S {
char a[5];
} S;
void foo(A5 a) /* of course can be "void foo(char a[])" but less clear */
{
printf("%zu %zu\n", sizeof(A5), sizeof(a));
}
void bar(S s)
{
printf("%zu\n", sizeof(s.a));
}
int main()
{
A5 a;
S s;
foo(a);
bar(s);
}
Of course in case of the typedef you need to use typeof on it rather than on the var being a char*
But all of that is a kind of hack, C is C ...
This is the data type that I declared:
struct Element{
char name[21], symbol[4];
double atomicMass;
int valence;
};
typedef struct Element myElements;
myElements data[20];
If I just want to pass the name members of
data[20]
to qsort(), how to do that?
Not sure if this would be the correct way to pass to the function:
qsort(data->name, 20, sizeof(myElements), compare);
You don't pass just member. Right way is to write a helper compare function which compares 2 elements by their name and use it.
static int
cmpElement(const void *p1, const void *p2)
{
return strcmp(((const Element *) p1)->name, ((const Element *) p2)->name);
}
qsort(data, 20, sizeof data[0], cmpElement);
I'm trying to make a generic stack in plain C and I have some problems with pointers and no idea where is the problem.
Here's the structure and functions, where I have problems:
typedef struct{
void *elems;
int elemSize;
int logLength;
int allocLength;
} genStack;
void GenStackPush(genStack *s, const void *elemAddr);
void GenStackPop(genStack *s, void *elemAddr);
That's the implementation:
void GenStackPush(genStack *s, const void *elemAddr)
{
s->elems[s->logLength] = elemAddr;
s->logLength++;
}
void GenStackPop(genStack *s, void *elemAddr)
{
s->logLength--;
elemAddr = s->elems[s->logLength];
}
The usage should look like this:
int val;
genStack IntegerStack;
for (val = 0; val < 6; val++)
GenStackPush(&IntegerStack, &val);
GenStackPop(&IntegerStack, &val);
printf("Popped: %d\n",val);
And here are the problems I get:
genstacklib.c: In function ‘GenStackPush’:
genstacklib.c:60:10: warning: dereferencing ‘void *’ pointer [enabled by default]
genstacklib.c:60:2: error: invalid use of void expression
genstacklib.c: In function ‘GenStackPop’:
genstacklib.c:72:23: warning: dereferencing ‘void *’ pointer [enabled by default]
genstacklib.c:72:13: error: void value not ignored as it ought to be
I have tried already several ways to fix the code, but none of them worked.
Thanks.
==========================================================================
So, guys, thanks for help! Now it compiles, but I have changed an API, which was given by our Professor. There was also the problem with 'const' qualifier, so I deleted them. Not my code looks like this:
genstacklib.h:
#ifndef GENSTACKLIB_H
#define GENSTACKLIB_H
#define GenStackInitialAlocationSize 4
typedef struct{
void** elems;
int elemSize;
int logLength;
int allocLength;
}genStack;
void GenStackNew(genStack *s,int elemSize);
void GenStackDispose(genStack *s);
int GenStackEmpty(const genStack *s);
void GenStackPush(genStack *s, void *elemAddr);
void GenStackPop(genStack *s, void *elemAddr);
#endif
genstacklib.c:
#include <stdlib.h>
#include <stdio.h>
#include "genstacklib.h"
void GenStackNew(genStack *s,int elemSize)
{
void** newElems;
/* Allocate a new array to hold the contents. */
newElems = (void**) malloc(elemSize * GenStackInitialAlocationSize);
if (newElems == NULL)
{
fprintf(stderr, "Error with allocating the stack.\n");
exit(1); /* Exit, returning error code. */
}
s->elems = newElems;
s->allocLength = GenStackInitialAlocationSize;
s->logLength = 0; /*is empty*/
}
void GenStackDispose(genStack *s)
{
s->allocLength = 0;
free(s->elems);
}
int GenStackEmpty(const genStack *s)
{
return s->logLength == 0;
}
void GenStackPush(genStack *s, void *elemAddr)
{
s->elems[s->logLength] = elemAddr;
s->logLength++;
}
void GenStackPop(genStack *s, void *elemAddr)
{
s->logLength--;
elemAddr = s->elems[s->logLength];
}
If you have any ideas to improve it or something to say about it, I would hear with pleasure. :D
you are trying to dereference void pointer without typecasting to some other type which causing the problem.
elems has the wrong type. If you declare it as void* the compiler does not know how big the things it points to are. So it can't do pointer arithmetic or array subscripting on it or even dereference what it points to.
Conceptually elems is an array of the things that you put on the stack. What do you put on the stack? Pointers - declared as void*. So elems should be an array of void* objects. You can declare it like this
typedef struct{
void *elems[STACK_SIZE];
int elemSize;
int logLength;
int allocLength;
} genStack;
which will reserve space in the struct for the array (making the struct itself very big), or you can declare it as a pointer to void* i.e. void**
typedef struct{
void **elems;
int elemSize;
int logLength;
int allocLength;
} genStack;
If you go for this option, you then have to manually allocate the memory
genStack* genStackAlloc()
{
genStack* ret = calloc(1, sizeof *ret);
ret->elemns = calloc(STACK_SIZE, sizeof(void*));
// rest of the initialisation
return ret;
}
And of course, you'll have to manually free the memory when you dispose of the stack.
elems is declared as a pointer to a void, where I think you want it to be a pointer to a void*.
The problem is s->elems[s->logLength] :
First, the member variable void *elems use to store the element address (void *), array of the element (void ), so the type of the elems should be (void *), and you should allocate the memory to store the address.
You can allocate memory by the following ways:
void * elems[MAX_STACK_SIZE];
OR
void ** elems
s->elems = (void**)malloc(MAX_STACK_SIZE*sizeof(void*)); // and allocate it before use it.