tags:

views:

129

answers:

8

I have twenty or so integers which I want to be able to refer to by name when they're being set, but I would like to also be able refer to them by number like they were in an array, so I can print them out one by one using a for loop. Any ideas how to code this in C? Here's what I'm talking about in pseudo code:

/* a data structure to keep a count of each make of car I own */
my_cars;

/* set the counts */
my_cars.saabs = 2;
my_cars.hondas = 3;
my_cars.porsches = 0;

/* print the counts */
for(all i in my_cars) {
    print my_cars[i];
}

Is this asking too much of a low level language like C?

+1  A: 

You need two data structures. An array to hold the numbers, and a map from the name to the index in the array. In C++ you'd use one of the map classes in the standard library. I don't know what's available in C but I'm sure there are map implementations available.

Jim Garrison
None in standard C, but POSIX has http://www.opengroup.org/onlinepubs/9699919799/basedefs/search.h.html
ephemient
+6  A: 
struct car {
    const char *name;
    int count;
} my_cars[] = {{"Saab", 2}, {"Honda", 3}, {"Porsche", 0}};
int i;
for (i = 0; i < sizeof(my_cars) / sizeof(my_cars[0]); i++)
    printf("%s: %d\n", my_cars[i].name, my_cars[i].count);
ephemient
+1. And, Is this valid C, there is no typedef? I'm not a C expert, just asking.
toto
Yes, this is valid C. Slap a `main(){}` around it and you can run it yourself.
ephemient
+6  A: 

To do that you should use an array instead of standalone data fields

#include <stdio.h>

typedef enum CarType {
  CART_SAAB,
  CART_HONDA,
  CART_PORSHE,

  CART_COUNT_
} CarType;

typedef struct MyCars {
  unsigned ncars[CART_COUNT_];
} MyCars;

int main(void)
{
  MyCars my_cars = { 0 } ;
  unsigned i;

  my_cars.ncars[CART_SAAB] = 2;
  my_cars.ncars[CART_HONDA] = 3;

  for (i = 0; i < CART_COUNT_; ++i)
    printf("%u\n", my_cars.ncars[i]);

  return 0;
}
AndreyT
You're the first person I see who uses unsigned alone, without int next to it.
toto
@jen9883: The output of this program will look rather... uninformative, so to say. Just a bunch of numbers. It would look a lot better if it would also print the brand hames next to the numbers. I hope you'll be able to figure out yourself how you can add the names by using the same technique.
AndreyT
@toto: It's perfectly legal to write `signed i` and `unsigned i` -- if unspecified, it's an `int`. And haven't you ever seen `[un]signed (char/short/long/...)`? That's not next to an `int` ;-)
ephemient
+3  A: 

enum Makes { SAAB, HONDA, PORSCHE, INVALID };
int my_cars[INVALID];
my_cars[SAAB] = 2;
my_cars[HONDAS] = 3;
my_cars[PORSCHE] = 0;

Stephen Newell
+5  A: 

C can do anything any other language can do. This does look like homework and I bet you are expected to make something with a key. Remember, your instructor wants you to use the data structures he or she is trying to teach you. He doesn't really want the problem solved in any random way, he wants it solved applying the topics you have been discussing.

So think about a data structure containing both strings and counts, one that can be searched, and provide functions to do that. What you are likely to get here are nice, professional, simple solutions to the problem. And that's not really what your instructor wants...

DigitalRoss
A: 

There are maybe a couple of options.

It is possible to have the same space in memory defined (and used) in two different ways. In other words, you could have a struct with the named members and reference it either as the struct or as an array depending on how you intended to address it.

Alternatively, you could do an enumerated typedef that names the locations in the array, e.g.

typedef enum {
  SABS = 0,
  HONDAS,
  PORSCHES
} cars;

This would then allow you to refer to offsets in the array by name, e.g.

mycars[SABS] = 5;
xyzzycoder
A: 

Umm...based on what you've pseudo coded up there you could probably use a union. The answers others are giving seem oriented around allowing a mapping between names and numbers. If thats what you're looking for (as in, being able to print the names) then their answers will be better. However it sounds like to me you're simply looking for clarity in the code to allow you to reference things by name or number, in this case a union would be ideal I think. This is exactly the type of thing a low level language like C lets you do.

union my_cars {
    struct names {
        int saab;
        int ford;
        ...
    }

    int[NUM_MAKES] nums;
}

You will have to be careful to ensure NUM_MAKES is in sync with the number of makes you define. Then you can do something like:

my_cars.names.saab = 20;
my_cars.nums[0] = 30;

And modify the same element.

FYI, my C is a little rusty so there may be syntax errors there, feel free to correct.

EDIT:

Ahh, I read some of the other answers using ENUMs or DEFINEs and those might actually be simpler/easier than this one...but unions are still worth knowing.

Daniel Brotherston
That is a truly evil use of union.
Greg Hewgill
:(...Perhaps it is, but its a reasonable demonstration of the concepts (which I think are worth knowing) and does solve the problem posed.
Daniel Brotherston
From the point of view of C language, none of this is legal and none of this is guaranteed to work.
AndreyT
I'm curious...I can see that it won't work if you don't do it properly (C is a gun, don't shoot yourself in your foot), but how is it not legal C?
Daniel Brotherston
Firstly, this is illegal from the purely pedantic point of view: C explicitly prohibits writing one member of union and then reading another. At any given moment a union has only one "active" member, and you are only allowed to read that one active member, not the others. Once you write something to another member, that another members becomes "active". And so on. Unions exist to save memory by sharing it between different objects *at different times*. Using union to reinterpret one object as another type is illegal.
AndreyT
Secondly, (if we ignore the "firstly" for a second) members of a struct in C are not guaranteed to reside in memory in compact way. The compiler is free to insert absolutly any amount of "padding" between struct members for any reason. Elements of an array, on the other hand, are required to be stored compactly. This means that there's absolutely no guarantee that the memory locations of array elements will coincide with the memory locations of struct fields, as you seem to expect. In general, they won't.
AndreyT
The requirement I described in "firstly" part is actually not as strict as I made it look. But what you plan do in your code is explicitly illegal.
AndreyT
+1  A: 

The low-level C way to do this would be to wrap the cars structure into a union:

// define a structure for the cars.
typedef struct 
{
    int saabs;
    int hondas;
    int porsches;
} cars;

// wrap it into a union:
typedef union 
{
  cars byname;
  int  byid[3]; // Note: Number and type must match with the cars structure. 
} cars2;

int main (int argc, char **arg)
{
  cars2 my_cars;
  int i;

  // fill out by name:
  my_cars.byname.saabs = 1;
  my_cars.byname.hondas = 5;
  my_cars.byname.porsches = 3;

  // print by index:
  for (i=0; i<3; i++)
    printf ("%d\n", my_cars.byid[i]);
}
Nils Pipenbrinck