Can a string be used as array index in C?
Ex:
String Corresponding value
"ONE" 1
"TWO" 2
"FIVE" 5
"TEN" 10
When a string in the above list is passed to the function, the function must return the corresponding value indicated above. Can this be achieved by declaring a constant array with string as index
int *x;
x["ONE"] = 1;
x["TWO"] = 2;
x["FIVE"] = 5;
x["TEN"] = 5;
return x["string received by the function"];
The above logic does not work as expected; is there a workaround to implement the above logic in order to have a string-indexed array?
It might compile, but it won't work.
It's not entirely clear what you're trying to achieve. I think you want an associative array, in which case you should find a library implementation of one.
If you're looking for something more like an enumerated type, and you can rely on C89, look at something like:
enum cardsuit {
CLUBS,
DIAMONDS,
HEARTS,
SPADES
};
If you can't rely on C89, then you should try some typedef trickery.
There are other excellent answers to what you should do, so I thought I'd explain what you are doing and why it's compiling and not working.
In C, array reference is done by having an array or pointer and an integer of some sort. (in x[1], x is the array and 1 is the integer). As long as you're using some integral type, it'll work as you expect.
Suppose you have something that's not an integer. In that case, the C implementation will see if it can convert it to the appropriate type, so you wind up with array and integer. It's cases like this where you get into trouble (and slightly more sophisticated versions of this in C++ have confused more experienced people than you).
In C, a literal string like "one" is of type const char *, meaning pointer to characters you can't change. The actual value is the memory address of where the string actually resides in memory. Normally, you'd pay no attention to this pointer value, and look at the string value, but there's a gotcha here.
In C, any data pointer can be converted to some sort of integer, and will be automatically. Therefore, you've got a string like "one", and its value is whatever number that represents the memory address. Use it where C expects some sort of integer, and it'll get converted to some integral value or other.
Therefore, this is what's happening with x["ONE"]. The C system has to put the string "ONE" somewhere in memory, and it doesn't matter where. It's likely to be somewhere with a fairly large memory address, quite possibly in the billions. When it sees x["ONE"], it tries to convert that value to an integer, and uses it as a subscript. Therefore, you're trying to access the array x far, far beyond its bounds, and that's causing the problem. Either you're trying to use memory you're not allowed to, and the system just stops you, or you're mucking with a chunk of memory you should be leaving alone, and it's likely to fail in some mysterious way later.
You can easily build lookup tables with the function bsearch() provided by stdlib.h. A working example is this:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define count(ARRAY) (sizeof(ARRAY)/sizeof(*ARRAY))
struct item
{
const char * name;
int value;
};
static _Bool sorted;
static struct item items[] =
{
{ "one", 1 },
{ "two", 2 },
{ "three", 3 },
{ "ten", 10 }
};
static int compare(const void * p1, const void * p2)
{
return strcmp(*((const char **)p1), *((const char **)p2));
}
int get(const char * name)
{
if(!sorted)
{
qsort(items, count(items), sizeof(*items), compare);
sorted = 1;
}
struct item * item = bsearch(&name, items, count(items), sizeof(*items),
compare);
return item ? item->value : 0;
}
int main(int argc, char ** argv)
{
int i;
for(i = 1; i < argc; ++i)
printf("%i\n", get(argv[i]));
return 0;
}
You will need to write a function that maps strings to integers, or alternatively use enumerations throughout (and then perhaps a function that maps enumerated values to strings).
In general, it's nicer to do the latter: to pass integers, so that the implementation isn't dependent on the details of strings that might be used in the representation. For example, think about how you would manage localization (translation) if ever you need to make those strings palatable to somebody speaking a different language.
What you are looking for is probably the equivalent of an associative array which can't be provided with the same syntactic sugar in C unfortunately without some silly results.
However, what you can provide is a hashmap if your data conforms to key -> value pairs. What you will need is an appropiate hash function.
There's a decent simple example of a hashtable here:
http://www.cl.cam.ac.uk/~cwc22/hashtable/
As already indicated, you need an associative array or hash map or equivalent. One possible source for such code is Hanson's "C Interfaces and Implementations" (code at Google Code - double check licencing terms etc before using it.)
This is an old thread, but I thought this might still be useful for anyone out there looking for an implementation. It doesn't take too much code; I did mine in ~100 lines of without any extra library like Hank Gay suggested. I called it a dictionary since it parallels (sort of) the python datatype. Here is the code:
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
typedef struct hollow_list hollow_list;
struct hollow_list{
unsigned int size;
void *value;
bool *written;
hollow_list *children;
};
//Creates a hollow list and allocates all of the needed memory
hollow_list hollow_list_create(unsigned int size){
hollow_list output;
output = (hollow_list) {.size = size, .value = (void *) 0, .written = calloc(size, sizeof(bool)), .children = calloc(size, sizeof(hollow_list))};
return output;
}
//Frees all memory of associated with a hollow list and its children
void hollow_list_free(hollow_list *l, bool free_values){
int i;
for(i = 0; i < l->size; i++){
hollow_list_free(l->children + i, free_values);
}
if(free_values){
free(l->value);
}
free(l);
}
//Reads from the hollow list and returns a pointer to the item's data
void *hollow_list_read(hollow_list *l, unsigned int index){
if(index == 0){
return l->value;
}
unsigned int bit_checker;
bit_checker = 1<<(l->size - 1);
int i;
for(i = 0; i < l->size; i++){
if(bit_checker & index){
if(l->written[i] == true){
return hollow_list_read(l->children + i, bit_checker ^ index);
} else {
return (void *) 0;
}
}
bit_checker >>= 1;
}
}
//Writes to the hollow list, allocating memory only as it needs
void hollow_list_write(hollow_list *l, unsigned int index, void *value){
if(index == 0){
l->value = value;
} else {
unsigned int bit_checker;
bit_checker = 1<<(l->size - 1);
int i;
for(i = 0; i < l->size; i++){
if(bit_checker & index){
if(!l->written[i]){
l->children[i] = hollow_list_create(l->size - i - 1);
l->written[i] = true;
}
hollow_list_write(l->children + i, bit_checker ^ index, value);
break;
}
bit_checker >>= 1;
}
}
}
typedef struct dictionary dictionary;
struct dictionary{
void *value;
hollow_list *child;
};
dictionary dictionary_create(){
dictionary output;
output.child = malloc(sizeof(hollow_list));
*output.child = hollow_list_create(8);
output.value = (void *) 0;
return output;
}
void dictionary_write(dictionary *dict, char *index, unsigned int strlen, void *value){
void *hollow_list_value;
dictionary *new_dict;
int i;
for(i = 0; i < strlen; i++){
hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
if(hollow_list_value == (void *) 0){
new_dict = malloc(sizeof(dictionary));
*new_dict = dictionary_create();
hollow_list_write(dict->child, (int) index[i], new_dict);
dict = new_dict;
} else {
dict = (dictionary *) hollow_list_value;
}
}
dict->value = value;
}
void *dictionary_read(dictionary *dict, char *index, unsigned int strlen){
void *hollow_list_value;
dictionary *new_dict;
int i;
for(i = 0; i < strlen; i++){
hollow_list_value = hollow_list_read(dict->child, (int) index[i]);
if(hollow_list_value == (void *) 0){
return hollow_list_value;
} else {
dict = (dictionary *) hollow_list_value;
}
}
return dict->value;
}
int main(){
char index0[] = "hello, this is a test";
char index1[] = "hello, this is also a test";
char index2[] = "hello world";
char index3[] = "hi there!";
char index4[] = "this is something";
char index5[] = "hi there";
int item0 = 0;
int item1 = 1;
int item2 = 2;
int item3 = 3;
int item4 = 4;
dictionary d;
d = dictionary_create();
dictionary_write(&d, index0, 21, &item0);
dictionary_write(&d, index1, 26, &item1);
dictionary_write(&d, index2, 11, &item2);
dictionary_write(&d, index3, 13, &item3);
dictionary_write(&d, index4, 17, &item4);
printf("%d\n", *((int *) dictionary_read(&d, index0, 21)));
printf("%d\n", *((int *) dictionary_read(&d, index1, 26)));
printf("%d\n", *((int *) dictionary_read(&d, index2, 11)));
printf("%d\n", *((int *) dictionary_read(&d, index3, 13)));
printf("%d\n", *((int *) dictionary_read(&d, index4, 17)));
printf("%d\n", ((int) dictionary_read(&d, index5, 8)));
}
Unfortunately you can't replicate the list[x] syntax, but this is the best alternative I have come up with.
In "plain C" you can mimic using a string as an index, but not QUITE in the way you seem to be wanting. However, doing so is seldom useful and mostly an excellent way of making your code unreadable. What you seem to be wanting is to be able to use string keys into a dictionary (or "hash table", if you prefer) and there are no built-in data structure for that in C. The exact design would depend on what you want (and, indeed, if this is part of homework, you may not even need to use a full-fledged hash-table implementation but could probably get away with less performant static coding).
An example using a string (OK, a char array) in the "index position) of an a[b] construct:
int main (void)
{
char *str = "This is a test string";
int x;
for (x=0; x < 12; x += 3)
putchar(x[str]);
printf("\n");
return 0;
}
The above is, as far as I can tell, legal C, with a well-defined output (the string "Tss ssi"). It relies on the fact that a[b] is defined to be the same as *(a+b).
Related
I read on some ways to dynamically create and use a 2D array, and I've settled on this way:
file:
5 4
+---+
|xxx|
|xxx|
+---+
main.c:
char** loadArray() {
FILE *in = fopen("file", "r");
int w, h;
fscanf(in, "%d %d\n", &w &h);
char (*buf)[w] = malloc(sizeof(char[h][w]));
for (int i = 0; i < h; i++) {
fscanf(in, "%s\n", buf[i]);
}
fclose();
return buf;
}
int main() {
char** array = loadArray();
for (int i = 0; i < 4; i++) { // magic number, only because I know the size
printf("%s\n", array[i]);
}
return 0;
}
While this does compile, it gives a warning: incompatible pointer types returning 'char (*)[w]' from a function with result type 'char **', and segfaults if I try to run it.
Several questions (mainly for C, but C++ specific answers are welcome for future reference, when I get there):
What is the correct return type for a multidimensional array? The warning isn't quite helpful I think, since it offers a variable term, which I obviously don't have until I read the file.
At this point, I'm just trying to get returning 2D arrays to work, but when I do and move on, I'm going to need the dimensions of the array for proper usage later on. My first idea would be to return structs instead, where I can save the dimension and the array itself. However, after further thought, the variable size makes me think that I wouldn't be able to have a single struct template to use as the function's return type, and I would have to find some other way to get the size along with the array. Are ideas?
Thank you for your time.
Pointer to pointer and pointer to array are quite different things. You can't return the array dimension as such in the return type so you'd have to use an incomplete array:
char (*(loadArray()))[] {
...
}
Evidently this is not very readable so you'd better pass through a typedef:
typedef char line[];
line* loadArray() {
...
}
You'd also have to get your hands on the dimensions w and h, so you'd have to pass pointers to the values as arguments. But that would be another question.
What is the correct return type for a multidimensional array?
There is no "correct" type, it all depends on how you want to represent the data and what architecture you want to design.
The warning isn't quite helpful I think, since it offers a variable term
The warning is very helpful, because it indicates an error in the code - you are casting a pointer to an array to a pointer to a pointer. Ignoring that warning ultimately leads to printf("%s\n", array[i]); which is undefined behavior.
Are ideas?
You may return a pointer to a dynamically allocated array of pointers to dynamically allocated array of values.
char **loadArray(void) {
size_t w, h;
// Initialize w and h.
char **arr;
arr = malloc(w * sizeof(*arr));
if (!arr) return NULL;
for (size_t i = 0; i < w; ++i) {
arr[i] = malloc(h * sizeof(*arr[i]));
if (!arr[i]) {
for (size_t j = 0; j < i; ++j) {
free(arr[j]);
}
frer(arr);
return NULL;
}
}
// Fill arr from file. Handle errors. Handle deallocation.
return arr;
}
which is the usual way to program in C and one would expect char ** to act that way. I can thing of an example from POSIX , where scandir function takes a pointer to a double pointer struct dirent ***namelist argument which is assigned to a such dynamically allocated 2d array.
But let's take a bit over-engineered (or not) design (in mostly C-ish pseudocode):
// represents the thing
struct thething_s {
char *string;
};
int thething_print(struct thething_s *t, FILE *f) {
return fprintf(f, ....);
}
void thething_free(struct thething_s *t) {
free(t->string);
}
int thething_load_from_file(struct thething_s *t, FILE *f, size_t len) {
t->string = malloc(len);
if (!t->string) return -1;
if (scanf(....) != 1) return -2;
return 0;
}
// represents an array of things
struct thingsarr_s {
struct thethings_s *things;
size_t w;
size_t h;
};
int thigsarr_load_from_file(struct thingsarr_s *t, FILE *f) {
// Initialize t->w and t->h.
t->things = malloc(t->w * sizeof(*t->things));
if (!arr) return -1;
for (size_t i = 0; i < w; ++i) {
if (thething_load_from_file(&t->things[i], f, t->h) != 0) {
for (size_t j = 0; j < i; ++j) {
thethings_free(t->things[j]);
}
free(t->things);
return -1000 - i;
}
}
return 0;
}
const struct thething_s *thingsarr_get_thing(const struct thingsarr_s *t, size_t idx)
{
assert(idx < t->len);
return t->things[idx];
}
struct thething_s *thingsarr_get_thing_nonconst(struct thingsarr_s *t, size_t idx) {
return (struct thething_s *)thingsarr_get_thing(t, idx);
}
void thingsarr_free(struct thingsarr_s *t) {
for (.,.) {
thething_free(&t->things[i]);
}
free(t->things);
}
Which is "best" strongly depends on the context, specific needs and specific application.
The pointer types char ** and char (*)[w] are very different. There is a common misconception that arrays and pointers are the same thing in C, but that is not the case.
You want the return type of the function to be char (*)[w], but that return type is impossible to declare because w is an unknown value. Also, the caller doesn't know the width and height of the returned array, because that information is internal to the loadArray function.
Setting the return type of the loadArray function to void* allows it to just return a pointer to the array without knowing the width or height. The width and height values can be sent to the caller using pointer arguments so that the caller can reconstruct the array type.
The following modifications of the original C code implements the above change. (C++ does not support variable length array types as far as I know, so this is C only.)
#include <stdio.h>
#include <stdlib.h>
void* loadArray(int *pw, int *ph) {
FILE *in = fopen("file", "r");
int w, h;
fscanf(in, "%d %d\n", &w, &h);
*pw = w;
*ph = h;
char (*buf)[w] = malloc(sizeof(char[h][w]));
for (int i = 0; i < h; i++) {
fscanf(in, "%s\n", buf[i]);
}
fclose(in);
return buf;
}
int main(void) {
int w, h;
void* larray = loadArray(&w, &h);
char(*array)[w] = larray;
for (int i = 0; i < h; i++) {
printf("%s\n", array[i]);
}
return 0;
}
I was just creating a testing function in which i have to pass boolean in void * so that i can parse it in other function and use it.
but i am stuck and not able to know that how should i memcpy the boolean in void *.
but when i am parsing it in another fucntion i am always getting the value true.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct {
int a;
uint8_t var_data[];
} s;
void parse(s * dummy)
{
void *var_data = dummy->var_data;
uint8_t *len;
char type[128];
bool *leaf;
for(int i = 0; i < dummy->a; i++)
{
len = (uint8_t *)var_data;
var_data += 1;
memcpy(type, var_data, *len);
type[*len] = '\0';
var_data += *len;
leaf = (bool *)var_data;
var_data += 1;
printf("%s\n", type);
printf("leaf: %s\n\n", leaf ? "true" : "false");
}
}
int main()
{
// Write C code here
char val[] = "dummy value";
uint8_t len = strlen(val);
bool v = false;
int b = 2;
int sz = sizeof(s) + b * (sizeof(bool) + len + 1);
s * dummy = (s *) malloc(sz);
dummy->a = b;
void *var = dummy->var_data;
for(int i = 0; i < dummy->a; i++){
memcpy(var, &len, 1);
var += 1;
memcpy(var, val, len);
var += len;
memcpy(var, &v, sizeof(bool));
var += sizeof(bool);
}
parse(dummy);
return 0;
}
can body help me with this problem.
var_data is uninitialized. You should allocate var_data with malloc, and copy the data in leaf into it:
void *var_data = malloc(sizeof(bool));
bool leaf = false;
memcpy(var_data, &leaf, sizeof(bool));
And you can cast it to bool * like this:
bool *leaf;
leaf = (bool *) var_data;
In addition, you increment var_data pointer. So var_data points to a different memory location now.
You didn't dereference leaf in this line:
printf("leaf: %s\n\n", leaf ? "true" : "false");
Since leaf is a non-zero pointer, it will always evaluate to true in C. You want to print *leaf instead:
printf("leaf: %s\n\n", *leaf ? "true" : "false");
Some other misc remarks:
void* arithmetic (i.e. var_data += 1) is illegal in C, although gcc will not complain. Use a char* because this is the type that's supposed to be used for serialization.
As mentioned in other answers, using pointers like you are doing right now can lead to subtle errors. If your pointer is pointing to an address and you want to dereference it (read the value stored there), it's better to do this sooner than risk this location being changed by some other code in the meantime.
So, just copy the data from the char* array into the target struct (or a primitive like uint8_t) and then advance the pointer.
The only way you are technically allowed to cast pointers in C is to cast them from a specific pointer (like something*) to a char*, in order to inspect their contents. You can also implicitly cast from and to void*, but only if you are not aliasing the pointer (trying to modify the underlying type). Any casting in the other direction is a violation of strict aliasing, so you should try to use memcpy instead. It may look uglier, but compiler will optimize it anyway (see for yourself) and you'll be safe(r) from the horrors of aliasing.
One nice habit to have it to try to utilize const-correctness wherever you can, it helps the compiler warn you if you're doing something wrong. If your function is parsing the array, the parameter should be const char*.
Finally, if your goal is to serialize and deserialize structs, perhaps you should look into protocol buffers or some similar serialization framework. It is fast, efficient, portable and, best of all, already written.
So, something like:
typedef struct {
int len;
char * var_data;
} example;
// note the const keyword - this means this function is
// not going to change the struct, only read it
void parse(const example * dummy)
{
// again, pointer to const char
const char * var_data = dummy->var_data;
// move all variables to the innermost scope
for (int i = 0; i < dummy->len; i++)
{
uint8_t len = 0;
memcpy(&len, var_data, sizeof(len));
var_data++;
...
}
}
So, my first question here, please be patient with me:
My task is to sort an array of structs (name, surname and another struct for the birthday, which consists of the year, month, day). I have to sort by birthdate and by using qsort.
My problem is, I looked up everything about qsort but i am not quite sure if my implementation is correct since I am new to C. I can create the executable program but it is not giving my any result only Segmentation Fault.
Here is my Code:
#include <stdio.h>
#include <stdlib.h>
typedef int (*compfn) (const void*, const void*);
typedef struct {
unsigned year, month, day;
} date_t;
typedef struct {
char name[32];
char surname[32];
date_t birthday;
}person_t;
typedef struct {
unsigned n;
unsigned cap;
person_t *arr;
} persons_t;
int compare(person_t *a, person_t *b){
if(a->birthday.year!=b->birthday.year){
return a->birthday.year-b->birthday.year;
}else{
if(a->birthday.month!=b->birthday.month){
return a->birthday.month-b->birthday.month;
}else{
return a->birthday.day-b->birthday.day;
}
}
}
int main(int argc, char* argv[])
{
if (argc <= 1) {
fprintf(stderr, "syntax: %s <inputfile>\n", argv[0]);
return 1;
}
FILE* f = fopen(argv[1], "rt");
if (f == NULL) {
fprintf(stderr, "cannot open file %s\n", argv[1]);
return 1;
}
persons_t persons;
persons.n = 0;
persons.cap = 0;
persons.arr = NULL;
person_t p;
while (fscanf(f, "%s %s %4u-%2u-%2u", p.name, p.surname,
&p.birthday.year, &p.birthday.month, &p.birthday.day) == 5) {
if (persons.n == persons.cap) {
persons.cap = persons.cap == 0 ? 1 : 2 * persons.cap;
persons.arr = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
}
persons.arr[persons.n++] = p;
}
int nitems = persons.cap*sizeof(persons.arr[0]);
int size = sizeof(persons.arr[0]);
qsort(persons.arr, nitems, size, (compfn)compare);
for (unsigned i = 0; i < persons.n; i++) {
person_t *p = persons.arr + i;
printf("%s %s %4u-%2u-%2u\n",
p->name, p->surname,
p->birthday.year, p->birthday.month, p->birthday.day);
}
fclose(f);
return 0;
}
I hope someone can help me,
Thanks in advance ;)
As far as _t-suffixed identifiers go, according to the C standard they're reserved for the implementation (e.g. your compiler, and/or your standard library). It's very possible that your implementation already has a date_t type, and your code might be causing some kind of mischief. If you wish to avoid subtly and dangerously clashing identifiers wreaking all sorts of havoc, it's probably best to avoid them. Not to worry, you could always use '_s' to denote a struct type instead!
Whenever you're declaring a variable that represents an index within an array, use size_t as the type!
int compare(person_t *a, person_t *b){
...
qsort(persons.arr, nitems, size, (compfn)compare);
According to the qsort manual, the argument given as the comparator function should be an int (*compar)(const void *, const void *), and that's what you've given since you've cast to (compfn). As far as qsort is aware that function accepts two const void * arguments, which might differ in representation to person_t * arguments. This could certainly cause segfaults. Don't lie about the type of compare. Change it to look more like:
int compare(const void *x, const void *y) {
const person_s *a = x, *b = y;
/* ... */
}
... and you won't need the cast or the typedef.
Next, onto return values for that function. I have used implementations where-by lexically illogical return values cause segmentation faults. For example, if a <= b and b <= c, then a <= c, but your code doesn't guarantee this. In fact, using your code it is possible that a <= b, b <= c and a > c. I recommend making sure your code guarantees correspondence between the return value and lexical order. You can do so by returning 1 for greater than, 0 for equal to or -1 for less than.
#define lexical_order(x,y) ((x > y) - (x < y))
int compare(const void *x, const void *b){
const person_s *a = x, *b = y;
return a->birthday.year != b->birthday.year ? lexical_order(a->birthday.year, b->birthday.year)
: a->birthday.month != b->birthday.month ? lexical_order(a->birthday.month, b->birthday.month)
: lexical_order(a->birthday.day, b->birthday.day);
}
I'm sure you're aware that you should be checking the return value of realloc... For example:
void *temp = realloc(persons.arr, persons.cap * sizeof(persons.arr[0]));
if (temp == NULL) { /* If we don't check return value prior *
* to assigning to persons.arr, we *
* might leak some memory... */
puts("Error in realloc");
free(persons.arr);
exit(-1);
}
persons.arr = temp;
Finally, and most importantly (this is probably your error), are you sure about this?
int nitems = persons.cap*sizeof(persons.arr[0]);
If you mean to pass this as the number of items to qsort (which is usual), then I think that should be:
size_t nitems = persons.n;
P.S. In case you missed it the second time, you should probably audit your code to make sure you're using size_t to store array indexes only.
P.P.S. Don't forget to free(persons); at the end of your program, so you don't end up with reports of memory leaks when you use valgrind...
P.P.P.S. valgrind is awesome!
So you are allocating our array by doubling its size whenever needed, using persons.cap, but you are not filling all its elements, are you?
From your code, the actual number of persons is nitems = persons.n, not persons.cap. What if you retry your code with nitems=persons.n?
If you have unfilled elements in your array, it means the strings inside them are arbitrary (i.e person.name), so probably not null-terminated, and the crash will occur when you try to display them.
So I have implemented a generic stack in Plain C. It should copy different type of data, inclusive structures. And by structures I have the problem.
So here's the structure of the stack:
/*
* Definite genStack as a structure.
* Pointer elems points to the objects lying on the stack
* The variable elemSize spiecifies the size of an element
* The variable logLength specifies the number of actually
* lying on the stack objects
* The variable allocLenght specifies the allocated size
*/
typedef struct{
void* elems;
int elemSize;
int logLength;
int allocLength;
}genStack;
Push and pop functions:
void GenStackPush(genStack *s, const void *elemAddr)
{
/* if stack is full - allocates more memory */
if (GenStackFull(s))
{
GenStackAlloc(s, s->elemSize);
}
memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
s->logLength++;
}
void GenStackPop(genStack *s, void *elemAddr)
{
if(GenStackEmpty(s))
{
fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
} else
{
s->logLength--;
memcpy((void*) elemAddr, (s->elems)+(s->logLength), sizeof(s->elems[s->logLength]));
}
}
Simple structures test:
gentest.h:
#ifndef GENTEST1_H
#define GENTEST1_H
typedef struct {
char* name;
int age;
char gender;
}person;
#endif
gentest.c:
#include <stdio.h>
#include <stdlib.h>
#include "gentest1.h"
#include "genstacklib.h"
int main(int argc, char* argv[])
{
genStack StructStack;
person testPerson[5];
person* newPerson;
person* test;
int i;
newPerson = (void*) malloc (sizeof(person));
testPerson[0].name = "Alex";
testPerson[0].age = 21;
testPerson[0].gender = 'm';
testPerson[1].name = "Vanja";
testPerson[1].age = 20;
testPerson[1].gender = 'm';
testPerson[2].name = "sjrgsde";
testPerson[2].age = 11;
testPerson[2].gender = 'w';
testPerson[3].name = "wergsggsd";
testPerson[3].age = 99;
testPerson[3].gender = 'y';
testPerson[4].name = "adaasxx";
testPerson[4].age = 13;
testPerson[4].gender = 'g';
GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
for (i = 0; i < 5; i++) {
newPerson = &testPerson[i];
GenStackPush(&StructStack, newPerson);
printf("Pushed: %s, %d, %c\n", newPerson->name, newPerson->age, newPerson->gender);
}
test = (void*) malloc (sizeof(person));
test->name = "test";
test->age = 0;
test->gender = 't';
while(!GenStackEmpty(&StructStack))
{
GenStackPop(&StructStack, test);
printf("Popped: %s, %d, %c\n", test->name, test->age, test->gender);
}
GenStackDispose(&StructStack);
return 0;
}
And here's the output I get:
./gentest1
elemSize = 16 GenStackInitialAlocationSize = 4
sizeof(person) = 16
Pushed: Alex, 21, m
Pushed: Vanja, 20, m
Pushed: sjrgsde, 11, w
Pushed: wergsggsd, 99, y
New size of alloc = 8
Pushed: adaasxx, 13, g
Popped: adaasxx, 0, t
Popped: wergsggsd, 0, t
Popped: sjrgsde, 0, t
Popped: Vanja, 0, t
Popped: Alex, 0, t
As you can see, I can receive names, but no age or gender. I've tried a lot of options, but still getting Segmentation Fault or the output from above. For moment, the output above is the finest output I get, but still not what I want.
The question is - how can I get the output I need?
Thanks in advance.
To avoid some questions:
sizeof(person) = s->elemSize
It is defined by creating the stack:
genstacklib.c:
void GenStackNew(genStack *s, int elemSize)
{
void* newElems;
/* Allocate a new array to hold the contents. */
newElems = (void*) malloc(elemSize * GenStackInitialAlocationSize);
printf("elemSize = %d\tGenStackInitialAlocationSize = %d\n",
elemSize, GenStackInitialAlocationSize);
if (newElems == NULL)
{
fprintf(stderr, "Error with allocating the stack.\n");
exit(1); /* Exit, returning error code. */
}
s->elems = newElems;
s->elemSize = elemSize;
s->allocLength = GenStackInitialAlocationSize;
s->logLength = 0; /*is empty*/
}
gentest.c:
GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
your push function is copying sizeof(*elemAddr) and that is a void *, so it has the size of a pointer not the inteded size of a person struct. So you are probably copying only the first 4 bytes
As stated above the push is copying the wrong size of data. It should be elemSize.
The memcpy is also overwriting its own data. Something like this should work.
memcpy((char*) (s->elems)+(s->logLength)*elemSize, elemAddr, elemSize);
s->logLength++;
You're not using elemSize in all the relevant places...
void GenStackPush(genStack *s, const void *elemAddr)
{
...
memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
^^^^^^^^^^^^^^^^^
This is very wrong; the type of the expression *elemAddr is void, which is a constraint violation (sizeof may not be called on an expression of incomplete type, and void is an incomplete type). You will want to turn up the warning level on your compiler. I wrote a test program to compute sizeof on expressions of type void * and void, and I get a warning with gcc -pedantic. If I drop the -pedantic I don't get a warning, but the result I get for sizeof (void) is 1, which I'm pretty certain is not the size of a person. Why aren't you using s->elemSize here?
Secondly, why are you casting s->elems to char *?
EDIT
If I may offer some advice, I've womped up a few generic containers in the past, and here are the lessons I've come away with:
First, delegate all type-aware operations (allocation, deallocation, copy, compare, display, etc.) to separate functions, which are called via function pointers passed as parameters to the generic container's functions; i.e., a push would be defined like
GenStackPush(genStack *stack, const void *data, void *(*copy)(const void *))
{
stack->elems[++stack->logLength] = copy(data);
}
...
void *myIntCopyFunc(const void *data)
{
const int *inputData = (const int *) data;
int *copy = malloc(sizeof *copy);
if (copy)
*copy = *inputData;
return copy;
}
...
GenStackPush(&myIntStack, &intVal, myIntCopyFunc);
One issue you have with your person type is that you're not doing a deep copy of the name member; you're just copying a pointer value to the stack. In this case it's not a big deal since you're working with string literals, but if you were using, say, a local char [], you'd have problems. By writing a separate copy function for each type, you can deal with those sorts of issues, instead of trying to do a one-size-fits-all allocation in the container function itself.
Secondly, don't call your generic container functions directly; put a type-aware interface between you and the container (basically, the poor man's version of function overloading):
void pushInt(GenStack *stack, int intVal)
{
GenStackPush(stack, &intVal, myIntCopyFunc);
}
...
genStack myIntStack;
...
pushInt(&myIntStack, 5);
This gives you two benefits; first, it allows you to pass literal values as parameters (which you can't do with parameters of type void *). Secondly, it gives you a way to enforce type safety on your container. You can't accidentally push a value of the wrong type this way.
Is this a lot of extra work? Oh my yes. There's a lot of magic that has to happen under the hood for generic container types to work properly. If you're trying to replicate the same kind of functionality that you get with the C++ std::stack container type, you're going to be writing a lot of code.
Is it possible to have an array of multiple types by using malloc?
EDIT:
Currently I have:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define int(x) *((int *) x)
int main() {
void *a[10];
a[0] = malloc(sizeof(int));
int(a[0]) = 4;
char *b = "yola.";
a[1] = malloc(strlen(b)*sizeof(char));
a[1] = b;
printf("%d\n", int(a[0]));
printf("%s\n", a[1]);
}
But it's messy. Other ways?
EDIT: Cleaned it up a bit.
You can't have an array of different types, exactly. But you can achieve a similar effect (for some purposes at least) in a number of different ways.
If you just want a few values of different types packaged together, but the number and types of values don't change, you just need a struct and can access them by name:
struct s_item {
int number;
char str[100];
} item;
item.number = 5;
strcpy(item.str,"String less than 100 chars");
If you know what types you might use, you can create a union, or a struct containing a union so you can tag it with the type. You can then create an array of those. The type member lets you check to see what you stored in each array element later.
enum ElementType { et_str, et_int, et_dbl };
struct Element {
ElementType type;
union {
char *str;
int i;
double d;
}
};
struct Element *arr = malloc(sizeof(struct Element) * 3);
arr[0].type = et_str;
arr[0].str = strdup("String value"); /* remember to free arr[0].str */
arr[1].type = et_int;
arr[1].i = 5;
arr[2].type = et_dbl;
arr[2].d = 27.3;
/* access the values.. */
for (int i = 0; i < 3; i++) {
switch(arr[i].type) {
case et_str: printf("String: %s\n",arr[i].str); break;
case et_int: printf("Integer: %d\n",arr[i].i); break;
case et_dbl: printf("Double: %f\n",arr[i].d); break;
}
}
/* The strings are dynamically allocated, so free the strings */
for (int i = 0; i < 3; i++)
if (arr[0].type == et_str) free(arr[0].str);
/* free the malloc'ed array */
free(arr);
/* etc., etc. */
This approach may waste space because:
Each element has an extra value to keep track of the type of data it holds
The struct may have extra padding between its members
The types in the union may be different sizes, in which case the union will be as large as the largest type
If you have another way of knowing what type you've stored in each element, you can use just the bare union without the struct wrapping it. This is a little more compact, but each element will still be at least as large as the largest type in the union.
You can also create an array of void * values. If you do this, you'll have to allocate the items somehow and assign their addresses to the array elements. Then you'll need to cast them to the appropriate pointer type to access the items. C doesn't provide any runtime type information, so there's no way to find out what type of data each element points at from the pointer itself -- you must keep track of that on your own. This approach is a lot more compact than the others when the types you're storing are large and their sizes vary a lot, since each is allocated separately from the array and can be given only the space needed for that type. For simple types, you don't really gain anything over using a union.
void **arr = malloc(3 * sizeof(void *));
arr[0] = strdup("Some string"); /* is a pointer already */
arr[1] = malloc(sizeof(int));
*((int *)(arr[1])) = 5;
arr[2] = malloc(sizeof(double));
*((double *)(arr[2])) = 27.3;
/* access the values.. */
printf( "String: %s\n", (char *)(arr[0]) );
printf( "Integer: %d\n", *((int *)(arr[1])) );
printf( "Double: %f\n", *((double *)(arr[2])) );
/* ALL values were dynamically allocated, so we free every one */
for (int i = 0; i < 3; i++)
free(arr[i]);
/* free the malloc'ed array */
free(arr);
If you need to keep track of the type in the array, you can also use a struct to store the type along with the pointer, similar to the earlier example with the union. This, again, is only really useful when the types being stored are large and vary a lot in size.
enum ElementType { et_str, et_int, et_dbl };
struct Element {
ElementType type;
void *data;
};
struct Element *arr = malloc(sizeof(struct Element) * 3);
arr[0].type = et_str;
arr[0].data = strdup("String value");
arr[1].type = et_int;
arr[1].data = malloc(sizeof(int));
*((int *)(arr[1].data)) = 5;
arr[2].type = et_dbl;
arr[2].data = malloc(sizeof(double));
*((double *)(arr[2].data)) = 27.3;
/* access the values.. */
for (int i = 0; i < 3; i++) {
switch(arr[i].type) {
case et_str: printf( "String: %s\n", (char *)(arr[0].data) ); break;
case et_int: printf( "Integer: %d\n", *((int *)(arr[1].data)) ); break;
case et_dbl: printf( "Double: %f\n", *((double *)(arr[2].data)) ); break;
}
}
/* again, ALL data was dynamically allocated, so free each item's data */
for (int i = 0; i < 3; i++)
free(arr[i].data);
/* then free the malloc'ed array */
free(arr);
You can easily have an array of pointers that point to different types. Of course for it to be very useful, you'd need to have some way of recording or determining what type is currently referenced by each element.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// This implicitly allocates memory to store 10 pointers
void *a[10];
// The first element will be a pointer to an int
// Allocate the memory it points to, then assign it a value.
a[0] = malloc(sizeof(int));
*( (int *)a[0] ) = 4;
// The second element will be a pointer to char; for simplicity,
// I'm hardcoding the length of the string + 1 for the null byte.
a[1] = malloc( 6*sizeof(char) );
strncpy( a[1], "hello", 5 );
printf( "%d\n", *( (int *)a[0] ) );
printf( "%s\n", a[1] );
}
No, all the elements have to be of the same type. You might get away with an array of structures.
struct mixed {
enum {
INTEGER,
STRING,
} type;
union {
int num;
char *str;
} value;
};
struct mixed v[10];
v[0].type = INTEGER;
v[0].value.num = 10;
I myself would never do such a thing (seems messy). But your array-of-void* approach is similar: you have to store the information on the type somewhere.
I'm not sure what you want to achieve but there are two possibilities:
1 - You don't actually want an array but a struct:
struct {
int number;
char *string;
} a;
In this case you can access the number as a.number and the string as a.string.
2 - You want an array of variant type. In C, you can use unions (preferably tagged) for variant types:
struct Variant {
int type;
union {
int number;
char *string;
}
}
Then you can encode your type with 0 for number and 1 for string. Using an enum instead of integer for the type would be a better way of course.
It's because you're trying to store a value into a slot which is expecting a pointer. Try the following (error checking omitted for brevity)
int* pIntTemp = malloc(sizeof(int));
*pIntTemp = 4;
a[0] = pIntTemp;
The bigest issue is getting the C compiler to treat each element of the array differently.
Might I suggest a hybrid approach.
Set aside several pointers, each with their appropriate structure definitions.
When you decide which kind of element you want, use that pointer to malloc and setup, then later use.
Then copy the value of that pointer into the array of pointers.
Later, when you want to use that element, copy the array element into it's aproprate pointer to make the compiler happy.
Please keep in mind, this is only an example, it has some short commings like difficulty of sorting or inserting a node in the middle, but...
For example:
struct this_type {
char mod_kind[20];
int this_int;
};
struct that_type {
char mod_kind[20];
char that_string[20];
};
void *list_o_pointers[10];
struct this_type *p_this;
struct that_type *p_that;
p_this = malloc(sizeof(struct this_type));
list_o_pointers[0] = p_this;
strcpy(p_this->mod_kind, "this kind"); // or whatever you want to use to differentate different types
p_that = malloc(sizeof(struct that_type));
list_o_pointers[0] = p_that;
strcpy(p_that->mod_kind, "that kind");
// later
p_this = list_o_pointers[0];
p_that = list_o_pointers[0];
if (strstr(p_this->mod_kind, "this kind")) { /* do this stuff */ }
if (strstr(p_that->mod_kind, "that kind")) { /* do that stuff */}
it solves the ulgyness of stuff like having to cast *((double *)(arr[2].data)) = and also helps with readability.
This may break down if you have many different node structures.
It is a bit brute force, but (IMHO) it's a little easier on the brain. The array is a simple array and each node is simple. The nodes have no need for a "next" pointer like a linked list has.
Mark.