Able to access struct member through character in c? - c

Basically I have a struct with several members, named a-z. I have a string where I want each letter to correspond to the correct struct member. Currently I use a switch statement with each case in order to access the correct letter member of the struct, but Im wondering if theres a better (cleaner/shorter code) way to do this, without having to put 26 case statements with nearly identical code inside? My code looks somewhat like this:
typedef struct node
{
struct node *a;
struct node *b;
struct node *c;
...
struct node *z;
}node;
node *nTable[26][26][27];
int main
{
...
node *nWord = malloc(sizeof(node));
node *nPath = nWord;
nTable[0][0][0] = nWord;
char *cWord
cWord = "abcde";
for (int n = 0; n < 5; n++)
{
nWord = malloc(sizeof(node));
switch (cWord[n])
case 'a':
nPath->a = nWord;
nPath = nWord;
break;
case 'b':
nPath->b = nWord;
nPath = nWord;
...
case 'z':
nPath->z = nWord; //code is the same for each case, only difference between each is which member its assigned to
nPath = nWord;
}
}
Note the above code is a much reduced form of my actual code, so there may be some basic syntax and other errors within this example, but the general purpose of my code should be apparent.
So is there a cleaner way to do this, without using a case statement for each letter? Something simple like "nPath->cWord[n] = code;" would be perfect! (though this as it is, obviously does not work)
Any ideas would be great! Also sorry in advance if Ive left out any important info

For starters this construction
char *cWord = {a,b,c,d,e};
is syntactically invalid. You may not initialize a scalar object with a braced list with more than one initializer.
As for you question when you could declare one data member of an array type within the structure instead of numerous data members like
struct node *a;
For example
#define N 26
typedef struct node
{
struct node *nodes[N];
} node;
And then use the following approach
const char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *p = strchr( letters, toupper( ( unsigned char )cWord[i] ) );
if ( p != NULL )
{
nPath->nodes[p - letters] = data;
// or
// nPath->nodes[p - letters] = malloc( sizeof( node ) );
}

Maybe you can focus on the words: at first you have a linked list of words. And then there will be a linked list for every letter in order do build an array of linked lists of words.
Consider this example:
#include <stdio.h>
typedef struct _node
{
char* word;
struct _node* next;
struct _node* prev;
}; // single node contains one word
typedef struct _node Node;
typedef struct
{
char letter;
unsigned size;
unsigned limit;
Node* start;
Node* end;
} _list;
typedef _list Data;
In this way you have a bit more of structure
See this code
#include "x25list.h"
int main(int argc, char** argv)
{
Data* db[26];
for (int i = 'a'; i < 'z'; i += 1)
{ // builds initial linked lists
int ix = i - 'a';
db[ix] = (Data*)malloc(sizeof(Data));
db[ix]->letter = i;
db[ix]->size = 0;
db[ix]->end = NULL;
db[ix]->start = NULL;
}; // for()
return 0;
};
In this way you have a linked list for each letter already built and start to catalog words using the notes on the lists pointed by the value of the letter minus the value of 'a'.

Related

How should I get to the actual node with a pointer to pointer?

I would like to set root_1's name to be "hi", just like the output of the current output of this code, which is mentioned below. Unfortunately, it doesn't work.
struct Node
{
int num;
char *name;
struct Node *child;
struct Node *sibling;
};
int main()
{
struct Node root = {1,"hi",NULL,NULL};
struct Node *root_0 = &root;
struct Node **root_1 = &root_0;
char *s = root.name; //s is now hi
root_0 -> name = s;
//*root_1 -> name = s; //?????
printf("%s\n", root_0 -> name);
return 0;
}
That's not how pointers work. root_1 doesn't have a name or any other field - all it does is pointing to root. You don't have to set anything for root_0 and root_1, you can access root's name through them like this:
int main()
{
struct Node root = { 1,"hi",NULL,NULL };
struct Node *root_0 = &root;
struct Node **root_1 = &root_0;
printf("root_0: %s\n", root_0->name);
printf("root_1: %s\n", (*root_1)->name);
return 0;
}
This prints:
root_0: hi
root_1: hi
So in other words, (*root_1)->name is root_0->name, which is root.name. Perhaps it's less confusing if you consider that root_0->name is the same as writing (*root_0).name and (*root_1)->name is like writing (**root_1).name. You just add a * per pointer level.
It's just that * has a lower priority than ->.
*foo->bar = *(foo->bar) != (*foo)->bar.
So in your case you'd need (*root_1)->name = s;

Pointers and structs

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dictionary.h"
#define HASH_SIZE 100
// prototype
int hash(char *word);
// counter
int counter;
// node
typedef struct
{
char *word;
node *next;
} node;
// hash table
node *hashtable[HASH_SIZE];
bool
load(const char *dictionary)
{
// open the dictionary
FILE *dict = fopen(dictionary, "r");
if(dict == NULL)
{
printf("Could not open %s.\n", dictionary);
return false;
}
// set all values in the hash table to null
for(int i = 0; i < HASH_SIZE; i++)
{
hashtable[i] = NULL;
}
// set the counter to 0
counter = 0;
// iterate through the words in the dictionary
while (!feof(dict))
{
// get word into a string
char gotcha[LENGTH];
fscanf(dict, "%s", gotcha);
// declare a node and allocate memory
node n;
n.word = malloc( strlen(gotcha)*sizeof(char) );
// save the word into the node
strcpy(n.word, gotcha);
// hash the word, baby!
int hash_value = hash(n.word);
// start saving addresses to the hashtable
n.next = hashtable[hash_value];
hashtable[hash_value] = &n;
//test
int len = strlen(n.word);
printf("%s\n", n.word);
printf("%i\n", len);
// that's one more!
counter++;
}
fclose(dict);
return true;
}
I am receiving the following two errors on these two lines of code:
n.next = hashtable[hash_value];
hashtable[hash_value] = &n;
dictionary.c:89:16: error: assignment from incompatible pointer type [-Werror]
dictionary.c:90:31: error: assignment from incompatible pointer type [-Werror]
How do I save pointer values in these two places? I am new to this, so please bear that in mind. :)
In your structure, the type node is not yet defined. Change it to use the structure tag:
typedef struct node
{
char *word;
struct node *next;
} node;
This:
typedef struct
{
char *word;
node *next;
} node;
is a syntax error. The node *next; occurs before the end of the typedef that creates node as a type. If your compiler for some reason accepted this, it probably thinks there are 2 different types called "node" now, which explains why one of them isn't compaible with the other. You should give up on that typedef silliness (structs generally shouldn't be typedef'ed) and just use
struct node
{
char *word;
struct node *next;
};
Define typedef names of structs before defining the structs. This allows mutually referring structs without concern for order and doesn't require inconsistent definitions, sometimes with the struct keyword and sometimes without it. Note that in C++ you can do away with the typedef line entirely:
typedef struct node node;
struct node
{
char* word;
node* next;
};

create C structs using a variable as filename

is it possible to do say
int filename = 0;
typedef struct{
char name;
char sname;
int number;
}foo;
foo filename;
filename++;
foo filename;
and have a new foo struct named 1 and another named 2?
C isn't interpreted language, so you can't create variable names runtime. The other way is to create array having multiple instances.
typedef struct{
char name;
char sname;
int number;
}foo;
foo *files = malloc(sizeof(foo)*3);
files[0].name = "A";
files[1].name = "B";
files[2].name = "C";
Edit: used malloc instad new foo[3]
No, this is not possible. Declaring foo filename; will attempt to declare another variable named filename of type foo, so the integer value will never come into play there.
In addition, the introduction of two more variables named filename within the same scope wouldn't be allowed.
You probably want a linked list, rather than an array, if you will be changing its size often. Then, your code looks something like:
typedef struct node{
char name;
char sname;
int number;
struct node* next;
}foo;
And you would use functions like the following to add new nodes/fetch nodes:
foo head = NULL;
void addNode(foo newNode)
{
if(head == NULL)
head = newNode;
else
{
foo temp = head;
while(foo->next != NULL)
foo = foo->next;
foo->next = newNode
}
}
foo fetchNode(int index)
{
if(index < 0)
return NULL;
int n = 0
foo temp = head;
while(n < index && temp != NULL)
{
temp = temp->next;
n++;
}
return temp;
}
The way this works is that each node has the necessary data, plus a pointer to the next node, which is NULL if its the last node. Then, all you need is a pointer to the first one and you can fetch nodes by walking the next pointers. This also makes it trivial to delete a node that is partway down the list.

Compare using strcmp on linked-list

I'm not a great understanding on linked-list, i don't know if it's possible, but i need to do it :) I have a linked list that are load to the struct, and i need to compare a all the chars on the struct....It's better with an example:
This is without linked lists
struct
typedef struct x{
char name[100];
}x;
typedef x Info;
typdef struct Elem{
Info node;
struct Elem*next;
}Element;
for(i=0;i<100;i++){
if(strcmp(a.name[i],a.name[i+1])==0){
printf("Same name\n");
}
}
else
printf("Diff name\n");
Now i need to do something like this but with linked-list
First of all: int strcmp ( const char * str1, const char * str2 ) compares two C-strings (char pointers). This means that a.name[i] should be a char pointer and not a char! Make sure this is the case (i.e. make sure a.name is an array of c-string arrays, and not an array of chars).
Secondly, if the previous is the case, your code will only compare string i with string i+1. It will not compare all strings with each other.
In any case, it looks like you are not doing whatever it is you want to do the right way. I'm guessing you want a struct that is defined like this:
struct example {
char * name;
// other members of choice
example * next;
}
A placeholder for a name, other members, and a next pointer to enable the linked list data type. That way you can compare names with:
while (list->next != 0 && list->next->next != 0) {
if (strcmp(list->name, list->next->name) == 0) // do something;
else // do something else;
}
or with a double loop if you want to compare all strings with each other.
So the first thing you need to do is understand the fundamentals of linked-list. You can read in detail here: http://www.codeproject.com/KB/cpp/linked_list.aspx
NOTE: You really can't undersand linked lists until you understand pointers. http://www.cplusplus.com/doc/tutorial/pointers/
Essentially a linked-list is composed of numerous "nodes" that link to each other. At a minimum each node will have two pieces of data, one being the data (in your case a character) and the other being a pointer to the next node in the list.
Defining a struct would look like (in pseudocode):
LinkedList nodeT {
char *c; //NOTE: strcmp only compares pointers to chars
nodeT *nextNode;
}
You would have a pointer to the first node of the linked list. Something like:
nodeT *firstElement;
Then cycling through the entire list is a piece of cake:
nodeT *curElement = firstElement;
while(curElement->next != NULL) { //assuming the last node's next pointer is NULL
if(strcmp(curElement->c,curElement->next->c)==0){
printf("Same name\n");
} else {
printf("Diff name\n");
}
curElement = curElement->nextNode;
}
But again, to understand this you need to understand the fundamentals of pointers.
Here is a program that traverses the linked list and compares the names of adjacent elements. I have taken the liberty of renaming a couple of things, but otherwise the code for the data structures is the same as yours.
#include <string.h>
#include <stdio.h>
#include <assert.h>
typedef struct Info_ {
char name[100];
} Info;
typedef struct Element_ {
Info info;
struct Element_* next;
} Element;
void print_comparisons(Element* elm)
{
assert(elm);
Element* cur = elm;
Element* next = cur->next;
for (; next; cur = next, next = next->next) {
if (strcmp(cur->info.name, next->info.name) == 0)
printf("Same name\n");
else
printf("Diff name\n");
}
}
int main()
{
Info a; a.name[0] = 'A'; a.name[1] = '\0';
Info b; b.name[0] = 'B'; b.name[1] = '\0';
Info c; c.name[0] = 'B'; c.name[1] = '\0';
Info d; d.name[0] = 'D'; d.name[1] = '\0';
Element na; na.info = a;
Element nb; nb.info = b;
Element nc; nc.info = c;
Element nd; nd.info = d;
na.next = &nb;
nb.next = &nc;
nc.next = &nd;
nd.next = NULL;
print_comparisons(&na);
}
The output of the program:
Diff name
Same name
Diff name

How do I sort a linked list of structures by one of the fields?

Wow now i know I dont. Lol.
I've got my structure like this:
struct Medico{
int Id_Doctor;
int Estado;
char Nombre[60]; ////focus on this part of the structure, this is name.
char Clave_Acceso[20];
char Especialidad[40];
struct Medico *next;
};
And I want to organize the structure depending on the name(alphabetical order..) any ideas on how to tackle this problem?
for example
Albert Haynesworth
Bob Marley
Carl Johnson
Thank you very much in advanced. :)(C, Unix)
Implementing a mergesort over a linked list in C is quite easy:
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct node {
struct node *next;
char *data;
};
struct node *
divlist (struct node *n) {
int i = 0;
if (n) {
struct node *tail, *n2 = n;
while (1) {
n2 = n2->next;
if (!n2) break;
if (i++ & 1) n = n->next;
}
tail = n->next;
n->next = NULL;
return tail;
}
return NULL;
}
struct node *
mergelists(struct node *a, struct node *b) {
struct node *n;
struct node **last = &n;
if (!a) return b;
if (!b) return a;
while (1) {
if (strcmp(a->data, b->data) > 1) {
*last = b;
last = &b->next;
b = b->next;
if (!b) {
*last = a;
break;
}
}
else {
*last = a;
last = &a->next;
a = a->next;
if (!a) {
*last = b;
break;
}
}
}
return n;
}
struct node *
sortlist (struct node *n) {
struct node *tail = divlist(n);
if (!tail) return n;
return mergelists(sortlist(n), sortlist(tail));
}
int main(int argc, char *argv[]) {
int i;
struct node *n1, *n = NULL;
for (i = argc; --i >= 1;) {
n1 = (struct node *)malloc(sizeof(*n1));
n1->data = argv[i];
n1->next = n;
n = n1;
}
n1 = n = sortlist(n);
while (n1) {
printf("%s\n", n1->data);
n1 = n1->next;
}
return 0;
}
Note that you will have to modify this code to use your data structure and the right comparison!
C can't sort for you, nor maintain a sorted data structure. As others have suggested, you need to sort it yourself. I would do this when you create a new Medico, since inserting into a linked list is easy, and you can just find where it belongs as you iterate.
If Medico's order needs to be different, than you will need to sort the list whenever you display it. You'll probably want to iterate to pull out every name, then sort the resultant array using any of a number of techniques (depending on the size).
Assuming the list order is otherwise of no concern, keep it in order.
Sounds like you want to look at implementations of either quicksort or mergesort. I believe that the c std lib qsort implementation takes an array and not a linked list, so you may need to implement your own (although I'm pretty sure that you could find a readily available implementation on the interwebz if you did a quick search)
If you want to sort an array of structures, you can use the qsort function, see man qsort. It takes a base address of the array, number of elements, element size and comparing function:
int compare(const void *a, const void *b) {
Medico *medA = (Medico*) a;
Medico *medB = (Medico*) b;
return /* compare medA and medB */;
}
Medico *medicos = /* initialize */;
qsort(medicos, numberOfMedicos, sizeof(Medico), compare);
D’oh, just now I noticed the next-record pointer that probably makes this answer useless. (I’ve changed the question title to make the linked list apparent.) To make at least something from this answer, you can always copy the list into an array:
Medico *medicos = calloc(sizeof(Medico), numberOfMedicos);
Medico *current = /* first record in your linked list */;
int i = 0;
assert(current);
do {
medicos[i++] = *current;
current = current->next;
} while (current);
// Here you can sort the array.
free(medicos);
Of course, it depends on the number of records and other variables.
(My C is a bit rusty, feel free to fix.)

Resources