struct dict {
int len;
char (*dict0)[MAX_WORD_LEN+1];
char (*dict1)[MAX_WORD_LEN+1];
};
/* the memory allocation */
void createDict(struct dict* myDict)
{
(*myDict).dict0 = malloc((*myDict).len*sizeof(char));
(*myDict).dict1 = malloc((*myDict).len*sizeof(char));
if(((*myDict).dict0==0)||((*myDict).dict1==0))
exit(1);
}
for(int i = 0; i < words; i++)
{
scanf("%s", p_diction->dict0[i]);
scanf("%s", p_diction->dict1[i]);
}
for(int i=0; i<words; i++)
{
printf("%s ", &p_diction->dict0[i]);
printf("%s\n", &p_diction->dict1[i]);
}
p_diction is a pointer to a dict type.
I set words to 11 and input the following:
one lo
two ba
three li
day night
work eat
great terrible
terrible disaster
A a
start delay
finish never
I you
But when I printf to check the strings, they print the following:
one ree
two y
three rk
day eat
work rrible
great terrible
terrible art
A nish
start delay
finish never
I you
Any idea as to why the first scanf reads it perfectly, while the second one just reads random stuff from words that will come later?
It looks like you are using a one-dimensional array to hold multiple strings(words) which is possible if you end each string(word) with a unique character that signifies the end of a word. But it wouldn't be possible to do that using the vanilla functions scanf() and printf() you would have to write your own. For example lets say you end each word with a #. The following loop would print all the words to your screen.
int i = 0;
while((*myDict).dict0[i] != '\0')//While index dose not point to end of array
{
for(; (*myDict).dict0[i] != '#'; i++)//Print one word a single character at a time
{
printf("%c", (*myDict).dict0[i]);
}
printf("\n"); i++;//Print a newline after the word and then increase the index by one
//so that it does not point to '#'.
}
But you could also make your dict0 and dict1 arrays into arrays of pointers in which each element is a pointer that points to a single word.
You're redeclaring i and you don't need to(for the printf loop). for the second loop i already exists so i=0 is enough to 'reinit' the variable.
About the read loop, scanf is 'safe' if you do separation between words with enter/return. If you are separating them with spaces scanf is not a good option. The second scanf is looking for that enter/return, if you don't give it by the stdin it will fail and it will probably storing garbage until it finds the enter/return(\n).
And one more thing, how are you declaring pdiction?
If you do:
struct dict *pdiction = malloc(sizeof(struct dict));
You access its 'inner' variables with:
pdiction->len = 10; /*Setting len to 10*/
But if you declare it, inside the same function as the read/write loop, like this:
struct dict pdiction;
You access the 'inner' variables this way:
pdiction.len = 10;
The second way is how C structures are normally accessed. The so called dot notation.
The first way is kinda like an evoluiton of C: It was so common to use the 'pointed by in the field' for pointers to structures that C gives you a better/easier/more readable way to do it: the arrow notation.
What I've wrote in the first case is exactly the same as:
(*pdiction).len = 10; /*The (structure) pointed by pdiction in the field len*/
So be careful with that.
Just to finish, I think that you've done that on purpose, but the printf is probably printing memory addresses and not the actual strings because you are using the memory address 'selector' ampersand.
[EDIT] - After seing your edit I can see one possible mistake. If in your struct you have declared the strings with a static value:
char (*dict0)[MAX_WORD_LEN+1];
char (*dict1)[MAX_WORD_LEN+1];
You don't need to 'realloc' space for them.
What you need is to have a vector of strings or a vector of dictionaries.
The access pdiction->dict[i] is only a char.
If you want a vector instead, you need to do change your allocation.
I would do it by declaring only pointers in the struct and doing the vector allocation later instead of having a vector of dicts(structs).
You can do that by changing your struct to this:
struct dict {
int len;
char **dict0;
char **dict1;
};
And then you'll have to alloc your vectors in the function:
pdiciton->dict0 = malloc(NUMBER_OF WORDS * sizeof (char*));
for(i=0;i<NUMBER_OF_WORDS;i++)
pdiction->dict0[i] = malloc(MAXIMUM_SIZE_OF_A_WORD*sizeof(char));
The same for dict1.
This might seem odd, but if you think about it is actually logic: if a string is a vector of characters, a vector of strings is a matrix of characters.
The problem with scanf though, seems the same: It is safe to use with enter/return but not with spaces. Try to insert all your data with an enter/return next instead of spaces and see if it works.
Hope this helps.
Related
I read somewhere that when we use structs we can't just write something like «example1.name = "Jim";», instead of it we should write «strcpy(example1.name, "Jim");»
The thing is that I need to use a struct and I need to scanf (and right after that sscanf) some information that corresponds to a string and I don't know how I should do it.
Could somebody help me, please?
Edit:
My code isn't complete and I know it's wrong, but I'll post it here so that you know what I am talking about:
int main(){
struct Cards{
int value;
char type[4];
};
for(i=1, 0 < i && i < 11, i++){
struct Cards cardi;
}
scanf("%d %s", &cardi.value, cardi.type);
/*at this point I just know it's wrong but I am really bugged.
I thought about something like «scanf("%d %s", &cardi.value, strcpy(cardi.type, "%s"));»
but I just know it's very wrong */
return 0;
}
This isn't true only about structs, but for all strings. You can use = for strings, only when you initialize them. You can scanf a string and place it in a string. Example:
scanf("%s", my_struct.str);
If you already have a string and you want to pass it in a struct, or in an other string variable you then need strcpy:
char str1[] = "abc", str2[4];
strcpy(str2, str1);
or
strcpy(my_struct.str, str1);
Edit:
for(i=1, 0 < i && i < 11, i++) {
struct Cards cardi;
}
In your code cardi is not card0, card1 etc, it is a struct Cards variable with the name cardi.
If you want to store 10 cards, you sould make an array of struct Cards with capacity of 10 like:
struct Cards array[10];
for (i = 0; i < 10; i++) {
scanf("%d %s", &array[i].value, array[i].type);
}
Anyway i suggest that you focus on learning the basics on arrays, strings and pointers before you use structs.
Edit 2: You don't want to define structs inside your main, because in that way, if you write a function it will not "see" your struct. Structs usually are written in the top of the code.
Your use of scanf() is correct. However scanning strings using %s is dangerous and discouraged. This is because the type member of your structure has space for only 4 characters, including the terminating '\0' (NUL) character. Entering a string longer than 3 characters will result in the extra characters being written into an unknown area of memory likely corrupting other variables and leading to incorrect program execution or a program crash. You can correct this by adding a string length limit, simply replace %s with %3s, where 3 is the maximum numbers of characters that will be accepted (scanf will automatically add a '\0' (NUL) character following the last scanned character).
Below are some additional comments:
If you want to declare an array for 10 Cards, you could do it this way without any loops:
struct Cards cardi[10];
To scan values into the first card (C arrays are 0-based):
// this will scan into the first card; type field is restricted to at most 3 characters
scanf("%d %3s", &cardi[0].value, cardi[0].type);
At the top of the file you'll want to add:
#include <stdio.h>
This header file provides a prototype (declaration) for the scanf function (among many others).
I'm trying to add (well, append really) the letters in the alphabet to an empty char array. However, I appear to run into some sort of pointer issue I don't understand, as my array contains only the last character. I tried moving the letter char outside of the for loop, but the compiler didn't like that. I also looked on here about how to create a list of all alphabetical chars, and one of the better answers was to type them all in 1 at a time. However, my problem means I don't fully understand for loops and pointers in C, and I want to.
#include <stdio.h>
int main(void) {
char *empty_list[26];
for (int i = 0; i < 26; i++){
char letter = i + 65;
empty_list[i] = &letter;
}
printf("%s", *empty_list);
return 0;
}
The main problem is your declaration:
char *empty_list[26];
defines an array of 26 pointers to characters. In your current code you assign each element in the array the address of the variable letter. Since that is out of scope when you print it is luck that it prints out the last one, it could equally have printed out garbage or crashed if the code between was complex. It could also have printed out additional garbage after the letter with what you already have since there is no way of knowing whether there is a string terminating character (\0) after the letter. In your existing code printf("%s", *empty_list); prints the first pointer from the array as a null terminated string, which if you ignore the loss of scope and assume the memory contents are still around, will be the last value from the loop since all pointers in your array point to the memory that letter was stored at and that memory has the last value from the loop.
If your intention was to create an array with the letters then it should be:
char empty_list[27];
It needs to be 27 as you need to leave space for the string terminating character at the end. One way to fill that in would be to use:
empty_list[26] = '\0';
after the end of your for loop and before you print the contents of the array (do not include the asterisk here - because it is an array the compiler will automatically take the address of the first element):
printf("%s", empty_list);
As brothir mentioned in the comments when you assign the value of the letter to the element in the array it should be without the ampersand:
empty_list[i] = letter;
There are a few things wrong with your code.
Firstly, the type of empty_list is presently an array of pointers to char, when it really should be an array of char, since your intent is to print it out as if it were the latter in the call to printf after your loop. char empty_list[26]; is the correct declaration.
Secondly, in your loop, you assign &letter when all you need is letter. Heck, you don't even need the intermediate variable letter. Just empty_list[i] = i + 'A'; will suffice.
Lastly, you are passing empty_list to printf to satisfy a format specifier %s, which expects a null-terminated string. What you need to do is add another element to empty_list and set that to zero:
char empty_list[27];
// populated 0..25 with 'A'..'Z' in your code...
empty_list[26] = '\0';
printf("%s\n", empty_list);
// Output: ABC...Z
With the above help (much appreciated), my working code to create an array of letters in C is below:
#include <stdio.h>
int main(void) {
// create an array with 1 extra space for null terminator
char empty_list[27];
// add null terminator so string knows when it's finished.
empty_list[26] = '\0';
for (int i = 0; i < 26; i++){
// add 65 to get ASCII value for 'A'
char letter = A + i;
// insert each char into the array sequentially
empty_list[i] = letter;
}
printf("%s", empty_list);
return 0;
}
I am trying to concatenate a random number of lines from the song twinkle twinkle. Into the buffer before sending it out because I need to count the size of the buffer.
My code:
char temp_buffer[10000];
char lyrics_buffer[10000];
char *twinkle[20];
int arr_num;
int i;
twinkle[0] = "Twinkle, twinkle, little star,";
twinkle[1] = "How I wonder what you are!";
twinkle[2] = "Up above the world so high,";
twinkle[3] = "Like a diamond in the sky.";
twinkle[4] = "When the blazing sun is gone,";
twinkle[5] = "When he nothing shines upon,";
srand(time(NULL));
arr_num = rand() % 5;
for (i=0; i<arr_num; i++);
{
sprintf(temp_buffer, "%s\n", twinkle[i]);
strcat(lyrics_buffer, temp_buffer);
}
printf("%s%d\n", lyrics_buffer, arr_num);
My current code only prints 1 line even when I get a number greater than 0.
There are two problems: The first was found by BLUEPIXY and it's that your loop never does what you think it does. You would have found this out very easily if you just used a debugger to step through the code (please do that first in the future).
The second problem is that contents of non-static local variables (like your lyrics_buffer is indeterminate. Using such variables without initialization leads to undefined behavior. The reason this happens is because the strcat function looks for the end of the destination string, and it does that by looking for the terminating '\0' character. _If the contents of the destination string is indeterminate it will seem random, and the terminator may not be anywhere in the array.
To initialize the array you simply do e.g.
char lyrics_buffer[10000] = { 0 };
That will make the compiler initialize it all to zero, which is what '\0' is.
This initialization is not needed for temp_buffer because sprintf unconditionally starts to write at the first location, it doesn't examine the content in any way. It does, in other words, initialize the buffer.
Update the buffer address after each print after initializing buffer with 0.
char temp_buffer[10000] = {0};
for (i=0; i<arr_num; i++) //removed semicolon from here
{
sprintf(temp_buffer + strlen(temp_buffer), "%s\n", twinkle[i]);
}
temp_buffer should contain final output. Make sure you have enough buffer size
You don't need strcat
I need to write a program that does the following:
Prompt the user and ask them how many words they wish to enter
Allocate dynamic memory using malloc() to store each individual word
Dynamically allocate another array to store pointers to each of these individual word strings, fill it with the pointers
Dynamically allocate an additional array for temporary storage of an incoming word
Prompt the user to enter a string with the same number of words they previously entered.
Read the users' input into the temporary array one word at a time, once each word has been read, transfer it into one of the dynamically allocated arrays.
After all of this I should be able to print each word individually.
A sample run could look like this:
How many words do you wish to enter? 5
Enter 5 words now:
I enjoyed doing this exercise
Here are your words:
I
enjoyed
doing
this
exercise
When reading the string, the program should read the word into a temporary array of char, use malloc() to allocate enough storage to hold the word, and store the address in the array of char pointers.How to handle this step?
Here is my question: How can I size my temporary array to be large enough to hold any arbitrary word the user may enter, and then how do I transfer it to the storage array?
#include <stdio.h>
#include <stdlib.h>
enum { MAX_WORD_SIZE = sizeof("Supercalifragilisticexpialidocious") };
void resign(char**,int);
int main()
{
char **p;
int n,i;
printf("How many words do you wish to enter?");
scanf("%d",&n);
p=calloc(n, sizeof(char*));
puts("Here are your words:");
resign(p,n);
for (i=0; i<n; i++) {
puts(p[i]);
}
}
void resign(char**p,int n)
{
int i=0,j=0;
char c;
char * temp_word = NULL;
getchar();
temp_word = malloc(MAX_WORD_SIZE);
while ((c=getchar())!='\n') {
if (c!=' ') temp_word[i++]=c;
else {temp_word[i]='\0';
p[j]=temp_word;
temp_word = malloc(MAX_WORD_SIZE);
i=0;j++;}
}
temp_word[i]='\0';
p[j]=temp_word;
free(temp_word);
}
Here is a description of malloc().
It takes a parameter size, in bytes.
So if the user entered 5 words, then you need to first allocate an array big enough to store 5 pointers.
Eg.
char ** words = NULL;
words = malloc(sizeof(char *) * <NUMBER OF CHARACTERS THE USER ENTERED>);
If we assume ASCII is being used and a character is a char, then each letter in each of the words is a byte. We also need to account for any spaces, line feeds, perhaps a carriage return, and a trailing null.
So how many letters are in a word? IDK, that's up to you. But if we assume that "Supercalifragilisticexpialidocious" from Mary Poppins in the longest word we will encounter, then we need to allocate at least 34 characters, plus an extra for a trailing null terminator. So that leaves us with up to 35 bytes per word.
This will work:
enum { MAX_WORD_SIZE = sizeof("Supercalifragilisticexpialidocious") };
This will also take into account the trailing \0 - MAX_WORD_SIZE will be 35.
char * temp_word = NULL;
temp_word = malloc(MAX_WORD_SIZE);
To create all of your other word arrays you'd need to do something similar in a loop:
for(int i = 0; i< <NUMBER OF WORDS THE USER ENTERED>; i++)
{
words[i] = malloc(MAX_WORD_SIZE);
}
To convert the number of words entered by the user into an integer you should use atoi()
After this you could use getchar() to pull the string from stdin one character at a time and manually place them into temporary you allocated, stopping when you get to a ' ' space.
To copy the temporary array into one of your word arrays you can use strcpy(), just ensure your temporary array always has a trailig \0 after the word.
Don't forget to free() that pointer when you're done.
I am assuming you are doing this for some sort of a homework assignment on dynamic memory and that is why your application is defined the way it is. But if you aren't you should consider just reading the input into one buffer first using fgets() and using strtok() to split the string after its been read. That approach will take more memory but it would be cleaner.
Another thing you should consider doing is using calloc() instead of malloc() to do your allocation, that way you can ensure that all of your arrays are initialized with 0's - it could save you from some confusion later on if there is garbage data already in those arrays.
Yet another thing to think about: The temporary array in this example can be allocated automatically or statically using temp_word[MAX_WORD_SIZE]; since I used an enum to store MAX_WORD_SIZE as a constant. There is no direct need to use malloc for this.
I have a tiny problem with my assignment. The whole program is about tree data structures but I do not have problems with that.
My problem is about some basic stuff: reading strings from user input and then storing them in an array list.
char str[1000];
fgets(str, 1000, stdin);
int x = 0;
int y = 0;
int z = 0;
char **list;
list = (char**)malloc((x+1)*sizeof(char));
list[x] = (char*)malloc((y+1)*sizeof(char));
while(str[z] != '\n')
{
list[x][y] = str[z];
z++;
if(str[z] == ',')
{
x++;
y = 0;
list = (char**)realloc(list, (x+1) * sizeof(char*));
list[x] = (char*)malloc((y + 1)*sizeof(char));
z++;
if(str[z] == ' ') // Skips space after the comma
{
z++;
}
}
else if(str[z] == '\n')
{
break;
}
else
{
y++;
list[x] = (char*)realloc(list[x], (y+1)*sizeof(char));
}
}
I pass this list array into another function.
As an example, inputs could be something like
Abcde, Fghijk, Lmnop, Qrstu
and I am trying to split each of these words into the array list.
Abcde
Fghijk
Lmnop
Qrstu
When I try to output the strings I sometimes get weird, excessive characters such as upside down question marks and numbers.
printf("%s ", list[some_number]);
gets me
Fghijk¿
or
Fghijk\200
All of my program works as expected except for this minor problem which I am having trouble solving. Even with the same exact inputs the bugs may or may not appear. I am guessing it has to do with memory allocation?
Thanks for your help!
You need to put '\0' at the end of your new string.
See most of the C library functions such as printf and strlen process strings assuming \0 as the end character of all. Otherwise, they keep on reading the memory out of bounds either making a memory violation or gets some where the value 0 and stops and all the bytes in between in the memory are interpreted to their extended ascii equivalent hence you are getting such a strange behaviour.
So, allocate an extra byte for \0 character and assign it to the last byte.
Either initialize your variables to null, or as tomato said, put a null character at the end of the new string.
C lacks many of the luxuries programmers now take for granted when it comes to memory management. You're on the right path with malloc but that function only allocates memory... it doesn't clear it out. As a result, your variables will have the correct amount of space (critical for reducing memory leaks and overflow errors), but will be filled with garbage. This garbage could be anything, and in your case, it's an upside down question mark. Appropriate, don't you think?
I could be mistaken since I can't run the code myself without more information, but after your
char **list;
list = (char**)malloc((x+1)*sizeof(char));
list[x] = (char*)malloc((y+1)*sizeof(char));
statements, you'll want to do something like this:
list = NULL;
and the like to clear out the garbage.
Furthermore, you may care to use the strlen() function (contained in string.h) to figure out just how many blocks of memory you need to allocate.
Clearing out the spaces you use for variables is a good practice to get into with C. Good to see you learning it as well.