Accessing information from nested struct passed to function - c

I have the following code:
struct wordPair {
char* englishWord;
char* foreignWord;
};
struct dictionary {
struct wordPair ** data;
int nbwords;
int size;
};
Say I have struct dictionary *dictionaryPtr filled with some data, and I pass it to the following function:
char* dictionary_translate( struct dictionary* d,
const char* const english_word,
const char* const foreign_word)
Within the function dictionary_translate, how can I access the data from the struct wordPair that is nested within the passed struct? I need the function to return a strdup of either englishWord or foreignWord.
I was trying d->data->englishWord, but this gives me the error "request for member 'englishWord' in something not a structure or union".
UPDATE!
What I need the function dictionary_translate to do is determine if there is a matching word pair that contains one of the words passed to it, and return the strdup of the translation (the other word in the pair). Here is the array of words I have defined:
const char* test_translations[NB_TESTS][NB_COLS] =
{
{"hello", "hola"},
{"cat", "gato"},
{"dog", "perro"},
{"thanks", "gracias"},
{"pants", "pantalones"},
{"shoes", "zapatos"},
};
This is how I'm calling the function in the first test I'm trying, which is when the translate function is passed an English word and is required to return a foreign word:
char* translationPtr = NULL;
for (i = 0; i < NB_TESTS; i++) {
translationPtr = dictionary_translate(dictionaryPtr, test_translations[i][0], NULL);
printf("English Word %s translated: %s\n", test_translations[i][0], translationPtr);
}
Here is the translate function as I have it so far...
char* dictionary_translate( struct dictionary* d,
const char* const english_word,
const char* const foreign_word){
int i;
if (d == NULL) return NULL;
for (i = 0; i < d->nbwords; i++) {
if (strcmp(english_word, d->data[i]->englishWord) == 0)
return strdup(d->data[i]->foreignWord);
else if (strcmp(foreign_word, d->data[i]->foreignWord) == 0)
return strdup(d->data[i]->englishWord);
}
return NULL;
}
As soon as the program gets to the translation function, it crashes. I can't make sense of the debugger to find out what is going on, but it seems like translationPtr never has a value other than NULL (0x0). I'm new with the debugger, so I'm sure it could tell me more if I knew how to read it.

It isn't entirely clear what your function is to do, but about the simplest implementation that might legitimately work is:
#include <string.h>
struct wordPair
{
char *englishWord;
char *foreignWord;
};
struct dictionary
{
struct wordPair **data;
int nbwords;
int size;
};
extern char *dictionary_translate(struct dictionary *d,
const char *const english_word,
const char *const foreign_word);
char *dictionary_translate(struct dictionary *d,
const char *const english_word,
const char *const foreign_word)
{
for (int i = 0; i < d->nbwords; i++)
{
if (strcmp(english_word, d->data[i]->englishWord) == 0)
return strdup(d->data[i]->foreignWord);
else if (strcmp(foreign_word, d->data[i]->foreignWord) == 0)
return strdup(d->data[i]->englishWord);
}
return 0;
}
I think you should review the design of your struct dictionary. Using a double pointer seems unnecessary (or the reason for using it is not obvious). The only advantage is that you'd have a contiguous array of pointers to struct wordPair, while the actual struct wordPair elements need not be contiguously allocated themselves. The following code is a more orthodox definition, assuming that a contiguous array of struct wordPair is not a problem:
#include <string.h>
struct wordPair
{
char *englishWord;
char *foreignWord;
};
struct dictionary
{
struct wordPair *data;
int nbwords;
int size;
};
extern char *dictionary_translate(struct dictionary *d,
const char *const english_word,
const char *const foreign_word);
char *dictionary_translate(struct dictionary *d,
const char *const english_word,
const char *const foreign_word)
{
for (int i = 0; i < d->nbwords; i++)
{
if (strcmp(english_word, d->data[i].englishWord) == 0)
return strdup(d->data[i].foreignWord);
else if (strcmp(foreign_word, d->data[i].foreignWord) == 0)
return strdup(d->data[i].englishWord);
}
return 0;
}
Given the sample test code where one of the arguments to dictionary_translate() is a NULL pointer, the code in the function must be revised not to dereference the argument if it is null. This assumes the double-pointer version of struct dictionary.
char *dictionary_translate(struct dictionary *d,
const char *const english_word,
const char *const foreign_word)
{
for (int i = 0; i < d->nbwords; i++)
{
if (englishWord != NULL && strcmp(english_word, d->data[i]->englishWord) == 0)
return strdup(d->data[i]->foreignWord);
else if (foreignWord != NULL && strcmp(foreign_word, d->data[i]->foreignWord) == 0)
return strdup(d->data[i]->englishWord);
}
return 0;
}

d->(*data)->englishWord
Should compile.

Related

Memory leak with json_object_get_string

I am quite new in C, and I am using the json-c library. I am completely sure that the problem I have is with json_object_get_string, because if I don't use it and put a string manually in my structure it works in valgrind without any memory leak.
I put a reduced version to make it clearer.
struct Example {
char *id;
char *name;
char *logPath;
};
struct Examples {
struct Example *examples;
size_t size;
};
struct Examples list() {
size_t i, count = 0;
struct Example *examples = NULL;
for (i = 0; i < 59; i++) {
json_object *root_obj = json_object_from_file("path.json");
json_object *jID;
json_object *jName;
json_object *jLogPath;
if (json_object_object_get_ex(root_obj, "Name", &jName) ==
TRUE &&
json_object_object_get_ex(root_obj, "LogPath", &jLogPath) ==
TRUE &&
json_object_object_get_ex(root_obj, "ID", &jID) == TRUE) {
char *id = strdup(json_object_get_string(jID));
char *name = strdup(json_object_get_string(jName));
char *logPath = strdup(json_object_get_string(jLogPath));
json_object_put(root_obj);
count++;
struct Examples *tmpExamples = realloc(examples, count * sizeof(struct Example));
if (tmpExamples == NULL) {
if (examples) {
free(examples);
}
die("Realloc");
}
struct Example example = { id, name, logPath };
examples = tmpExamples;
examples[i] = container;
}
struct Examples examplesList = { examples, count };
return examplesList;
}
I have tried to free the variables that I have assigned with strdup after adding it to the struct, but then I lose the real value of the string. I don't really understand what happens.
In the end I have not complicated with so much char pointer and the chars of the struct I have put it as char array if I can name it that way
struct Example {
char id[64];
char name[30];
char logPath[165];
};
struct Example example;
strcpy(example.id, json_object_get_string(jID));
strcpy(example.name, json_object_get_string(jName));
strcpy(example.logPath, json_object_get_string(jLogPath));
json_object_put(root_obj);

C structs, strings and segmentation faults [duplicate]

This question already has answers here:
Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer
(5 answers)
Closed 5 years ago.
So this is supposed to be a concordance program where it grabs words from a text file. I'm trying to use a struct to store the string, and also the number of times the word occurs in the text file. I also want to place the struct object into an array of structs, because I will need to sort the words alphabetically once I have them all. However, I'm getting a segmentation fault inside my createStruct function. I know the problem is with my limited knowledge of pointers and passing by reference. I've been messing with createStruct and compareStruct for days now and it just isn't clicking.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct word{
char *wordArr;
int wordCount;
}word;
char *makeLowerCase(char word[]);
char *removeFirstChar(char word[]);
char *removeLastChar(char word[]);
void createStruct(struct word wordObj, char word[]);
void structCompare(struct word wordObj, struct word objArr[]);
int main(int argc, char * argv []) {
char buff[] ="##Hello$$$$$"; //hard coded, will grab words from a .txt file
struct word newWord = {.wordArr = NULL, .wordCount = 0};
struct word structArray[500];
makeLowerCase(buff);
removeFirstChar(buff);
removeLastChar(buff);
createStruct(newWord, buff);
structCompare(newWord, structArray);
//trying to print from the array
printf("%s %d", structArray->wordArr, structArray->wordCount);
return 0;
}
char *makeLowerCase(char grabbedWord[]) {
int i;
size_t wordLength = strlen(grabbedWord);
for(i = 0; i < wordLength; i++) {
grabbedWord[i] = tolower(grabbedWord[i]);
}
return grabbedWord;
};
char *removeFirstChar(char inputWord[]) {
int i = 0;
size_t length = strlen(inputWord);
if (!isalnum(inputWord[i])) {
i++;
strncpy(inputWord, &inputWord[i], length);
return removeFirstChar(inputWord);
}
return inputWord;
};
char *removeLastChar(char inputWord[]) {
size_t length = strlen(inputWord);
if (!isalnum(inputWord[length - 1])) {
inputWord[length - 1] = 0;
return removeLastChar(inputWord);
}
return inputWord;
};
void createStruct(struct word wordObj, char string[]) {
strcpy(wordObj.wordArr, string);
wordObj.wordCount = 1;
};
void structCompare(struct word obj, struct word structArr[]) {
int i;
for(i = 0; i < sizeof(structArr); i++) {
if(structArr[i].wordCount == 0) {
strcpy(structArr[i].wordArr, obj.wordArr);
structArr[i].wordCount = obj.wordCount;
}
else if(strcmp(structArr[i].wordArr, obj.wordArr) == 0) {
structArr->wordCount++;
}
else {
strcpy(structArr[i].wordArr, obj.wordArr);
structArr[i].wordCount = obj.wordCount;
}
}
};
You get a segmentation fault because of a NULL pointer.
For copying a string, you use strcpy(char *dest, char *src). But the dest needs to be allocated. In your case, is just NULL;
So this is what you need to do:
// Add a \0 to the end of a string so you know when to stop.
char buff[] ="##Hello$$$$$\0";
// Allocate the char array so you know where to copy it. I allocate it by default to 500, change this based on your needs.
struct word newWord = {.wordArr = (char *)malloc(sizeof(char) * 500), .wordCount = 0};
If you pass the struct to a function directly, you will pass a copy of it so any change done in the function, will not be seen outside of the function. So you need to pass a pointer to the struct instead of the actual struct.

Write to double const pointer

I want to implement my own string implementation for education. For that I defined a struct named string as follows:
struct string {
const char *const data;
const int length;
};
I use functions to create these string structs and then I assign them to variables.
In order to override the const int length I use the following trick:
*(int *) &result.length = // a int
Now I also want to write to the const char *const data.
As far as I know the first const makes sure that you cant edit the items at which the pointer points, and the second const is that you can't point the pointer to a different memory location. These are properties of an immutable string. So my question is: How can I assign something to the const char *const data like I did to the const int length?
Edit: result as shown above is an instance of the struct string
Form the struct string at its declaration and initialize it.
Also recommend to store the size and not the length and use size_t.
#include <stdio.h>
#include <stdlib.h>
struct string {
const char * const data;
const size_t size;
};
struct string string_copy(const char *src) {
size_t size = strlen(src) + 1;
char *copy = malloc(size);
if (copy) {
memcpy(copy, src, size);
} else {
size = 0;
}
struct string retval = {copy, size}; // ****
return retval;
// or return a compound literal (C99)
return (struct string){ copy, size};
}
void string_free(struct string s) {
free((void*)s.data);
}
int main(void) {
struct string a = string_copy("Hello");
printf("%zu <%s>\n", a.size, a.data);
string_free(a);
// do not use `a` subsequently
return 0;
}
I do not recommend to initialize with a string literal like struct string retval = {"World", 6}; as that limits the usefulness of struct string.
Using a opaque struct has many advantages #Jonathan Leffler that exceed this approach - mainly to keep other code from messing with the struct string.

How to use strings with structs in C?

I have seen the answer to this question but it is terribly uninformative for newbie's like myself and still can't manage to get it to work. I am trying to declare a member of the struct called "name" that takes a string value and then trying to figure out how to get that value and print it. Every way I have tried produces an error...
typedef struct {
float height;
int weight;
char name[];
} Person;
void calculateBMI(Person x) {
//printf will go here here
}
int main(int argc, const char * argv[]) {
Person Michael;
Michael.height = 62.0;
Michael.weight = 168;
Michael.name[] "Michael";
Person Steve;
Steve.height = 50.4;
Steve.weight = 190;
Steve.name = "Steven";
calculateBMI(Michael);
calculateBMI(Steve);
}
You have to specify the length of the char array, like this:
typedef struct {
float height;
int weight;
char name[30];
} Person;
Then you use strcpy to populate it:
int main(int argc, const char * argv[]) {
Person Michael;
Michael.height = 62.0;
Michael.weight = 168;
strcpy(Michael.name, "Michael");
Person Steve;
Steve.height = 50.4;
Steve.weight = 190;
strcpy(Steve.name, "Steven");
calculateBMI(Michael);
calculateBMI(Steve);
}
This solution will be the cleanest in all the common cases as you are allocating the space into the stack when you declare a new variable of type Person . In most complex scenarios you don't know the size of the char array and maybe you need to keep it as small as possible. In those case you can use a malloc solution.
Remember that everytime you are using malloc youy have to remember to free the allocated space when you are done with the data.
You can to declare the name member as char * and allocate space to copy the string into it
typedef struct {
float height;
int weight;
char *name;
} Person;
size_t length;
const char *name = "Michael";
length = strlen(name);
Michael.name = malloc(1 + length);
if (Michael.name != NULL)
strcpy(Michael.name, name);
and then when you are done using the struct, don't forget to free
free(Michael.name);
or do as HAL9000 suggests, but this solution wont work for longer strings.
You could simplify this process by creating a helper function like
char *dupstr(const char *src)
{
char *dst;
size_t length;
if (src == NULL)
return NULL;
length = strlen(src);
dst = malloc(1 + length);
if (dst == NULL)
return NULL;
strcpy(dst, src);
return dst;
}
and then
typedef struct {
float height;
int weight;
char *name;
} Person;
Michael.name = dupstr("Michael");
but you will also need to call free after finished using the struct.
typedef struct {
float height;
int weight;
char name[];
} Person;
this struct has no size declared for the name this means that when you create the struct you must also create space for the name .
int main(int argc, const char * argv[])
{
Person *Michael=malloc(sizeof(Person)+strlen("Michael")+1);
if(!Michael)return 1;
Michael->height = 62.0;
Michael->weight = 168;
strcpy(Michael->name,"Michael");
calculateBMI(Michael);
free(Michael);
}

Practice Exam Questions

This is a practice exam question that I am having some difficulty with:
struct bodytp // Is there an error?
{
char *name; // If so, fix the error.
int len;
};
main()
{
struct bodytp person;
keepname(&person , "Waterman");
printf("%s\n", person.name);
}
void keepname(struct bodytp *onept, const char *last)
{
int len;
char *tpt;
for ( len = 0; last[len] != '\0'; )
len++;
char name[len+1];
for ( tpt = name; *tpt++ = *last++; )
;
onept->name = name;
onept->len = len;
}
I have determined that there is an error, as when I run it, I get garbage output from printf. I have also determined that person's name is indeed "Waterman" after the keepname function call. I have tried dereferencing person.name to person -> name, changing the problem from a stack-based question to a heap-based question by eliminating the ampersand operator and malloc-ing the struct, but nothing worked. Can anyone steer me in the right direction? Thank you in advance.
Is there an error?
struct bodytp // Is there an error?
{
char *name; // If so, fix the error.
int len;
};
No there is no error. It is a valid structure definition.
Now errors follow.:)
Function main shall be declared as
int main( void )
Though it is not an error nevertheless it would be better that before the function call there woud be the function prototype
keepname(&person , "Waterman");
The program has undefined behaviour because there is an assignment of a pointer to the structure by the address of a local array that will be destroyed after exiting the function
void keepname(struct bodytp *onept, const char *last)
{
//...
char name[len+1];
//...
onept->name = name;
//...
}
The valid function could be defined like
void keepname(struct bodytp *onept, const char *last)
{
int len = 0;
char *tpt;
while ( last[len] != '\0' ) len++;
char *name = malloc( len + 1 );
for ( tpt = name; *tpt++ = *last++; ) ;
onept->name = name;
onept->len = len;
}
In this case you have to free the alocated memory in main.
Take into account that you coud use standard C functions strlen and strcpy in the function.
You need to allocate the memory for the name in the heap.
void keepname(struct bodytp *onept, const char *last)
{
int len;
char *tpt;
for ( len = 0; last[len] != '\0';len++);
char *name=malloc(len+1);
onept->name = name;
onept->len = len;
for ( ; *name++ = *last++ ; );
}

Resources