I am writing a function for use in a small C program. The function is supposed to prompt the user for n 7-digit alphanumeric course code strings, and return those strings as an array. I have written a function that I believe should do that, however I have experienced some errors.
char ** getNCourses(int n) {
char **courses;
courses = malloc(n * sizeof(char));
for (int i=0; i<n; i++) {
printf(INQUIRY_COURSE_CODE"\n", i);
courses[i] = malloc(8);
scanf("%7s", courses[i]);
flush();
}
return courses;
}
I have a few lines of code to print the result of said array:
// prompt the user for course codes
char **pCourses;
pCourses = getNCourses(MAX_COURSES);
for (int i=0; i<MAX_COURSES; i++) {
printf("%s ", pCourses[i]);
}
And here is an output of the program being run. As can be seen, the outputted course codes are "corrupted":
Registering 10 courses.
Please enter the 7-digit course code for course 0:
CST8101
Please enter the 7-digit course code for course 1:
CST8102
[...]
CST8109
Please enter the 7-digit course code for course 9:
CST8110
�#�� CST8102 �#�� �#�� �#�� CST8106 CST8107 CST8108 CST8109 CST8110 %
I am unsure as to why some of the course codes have no problem appearing, yet others are entirely incorrect. I believe it is not an issue with reading the data, but actually allocating the memory and returning the array, but I am at a loss.
char **courses;
courses = malloc(n * sizeof(char));
courses points to char *. Each char * requires sizeof (char *) bytes, not sizeof (char) bytes. n of them require n * sizeof (char *) bytes.
You can avoid errors like this by always writing Pointer = malloc(n * sizeof *Pointer);. Then sizeof *Pointer always produces the size of the type being pointed to; you never have to deduce the type. It also automatically adjusts to declaration changes: If the code is edited to change the declaration of the pointer, the sizeof will still be correct because it uses the pointer to get the type, rather than a hard-coded type.
Related
I am writing a simple program that takes the command line arguments and stores them into a char **. I am trying to learn more about memory management but cannot get past this simple stumbling block. My program is supposed to copy the command line argumetns into a dynamicly allocated char **. However the first position in my array is always corrupter. Below is the code and what it prints:
if (strcmp(argv[1], "test") ==0)
{
test();
}
else
{
char ** file_names = malloc(10);
for(int i =0; i < argc-1; ++i)
{
file_names[i] = malloc(10);
strcpy(file_names[i], argv[i+1]);
printf("%s, %s\n", argv[i+1], file_names[i]);
}
printf("____________\n");
for(int i =0; i < argc-1; ++i)
{
printf("%s\n", file_names[i]);
}
}
and the out come is:
what, what
test, test
again, again
wow, wow
____________
pK#??
test
again
wow
can someone please explain why this is happening? Thanks
This:
char ** file_names = malloc(10);
is a bug. It attempts to allocate 10 bytes, which has nothing at all to do with how many bytes you need. Under-allocating and then overwriting gives you undefined behavior.
It should be something like:
char **file_names = malloc(argc * sizeof *file_names);
This computes the size of the allocation by multiplying the number of arguments (argc, if you don't want to store argv[0] then this should be (argc - 1), of course) by the size of a character pointer, which is expressed as sizeof *file_names. Since file_names is of type char * *, the type of *file_names is char *, which is what you want. This is a general pattern, it can be applied very often and it lets you stop repeating the type name. It can protect you from errors.
For instance compare:
double *floats = malloc(1024 * sizeof(float)); /* BAD CODE */
and:
double *floats = malloc(1024 * sizeof *floats);
If you imagine that originally it was float *floats (as the naming suggests) then the first variant contains another under-allocation bug, while the second "survived" the change of types without error.
Then you need to check that it succeeded, before assuming it did.
You want to allocate the right amount of memory for file_names, probably more like:
char ** file_names = malloc(sizeof(char*) * (argc - 1));
I am coding a program which takes a text file as an input, makes the index of the words of it and prints the output(the index) in a file and in the screen.
the input file may be huge. but we KNOW that the maximum variety of the words used in the text file is 200. we don't know what's the maximum of lines and characters of each word. so I should reserve a large number for them. I took the maximum of line 1000 and the maximum characters of each word 100.
I am programming in Turbo C and (I am forced to use that). the compiler allocates just 64kb memory (with the size of the compiler included) and so I have to use MALLOC.
my program is supposed to work in this algorithm: it reads the input file line by line with fgets. then in the current line, it reads word by word with strtok. so far I have the xth word in yth line. I want to put the words in an array of pointers. so I need a char * word[200]. and I want to show how many times, which word is repeated in which line. so I need a int index [200][1000]. if in yth line, the xth word is existed I would do index[x][y]++.
so now I need to allocate MALLOC memory to these char * word[200] and int index[200][1000] . Can anyone help? I tried all the answers to such questions and none of them helped.
see errors:
char **words;
words = malloc(sizeof(char*) * 200);//cannot conver void * to char **
for(int i = 0; i < 200; i++)
words[i] = malloc(100);//cannot convert void * to char *
int **index;
index = malloc(sizeof(int*) * 200)//cannot convert void * to int **
for (i = 0; i < 200; i++)
index[i] = malloc(sizeof(int) * 1000);
The malloc function returns a void * object, which you can think of as "a pointer to an undefined type". It's C's way of formatting a pointer when you don't know what kind of object it points to.
The errors Turbo C is issuing mean that you're not allowed to assign a void * to a different kind of pointer (e.g. a char ** variable or int *) without casting it explicitly to that type. You can do that by writing the malloc calls this way:
char **words;
words = (char **) malloc(sizeof(char*) * 200);
for(int i = 0; i < 200; i++)
words[i] = (char *) malloc(100);
int **index;
index = (int **) malloc(sizeof(int*) * 200);
for (i = 0; i < 200; i++)
index[i] = (int *) malloc(sizeof(int) * 1000);
Casting the result from malloc is very unpopular these days, but if you have to use a compiler that considers implicit pointer casting to be an error, then the hell with that. :-)
Your compiler does not conform to the C standard: In C you are allowed to implicitely convert any void* to any other pointer (the opposite is also true). So the line
int* foo = malloc(sizeof(*foo));
is perfectly valid C code.
Your compiler, however, expects the following:
int* foo = (int*)malloc(sizeof(*foo));
to tell it that you really want to convert a void* to an int*. You can fix your code in an analog way.
Sidenote:
It's good to remember this, because C++ has the same problem as your compiler (with the only difference that in C++ it is the standard that overly picky). So in C++ the first variant is illegal as well.
I have an assignment I've been working on for a few hours now, and I can't seem to get it quite right. The assignment is to take a random number of names (from stdin), sort them, and then output them in alphabetical order. I can't find any sites online that handle this kind of sorting specifically, and have had no luck trying to implement qsort() into my code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int stringcmp(const void *a, const void *b)
{
const char **ia = (const char **)a;
const char **ib = (const char **)b;
return strcmp(*ia, *ib);
}
void main(int argc, char *argv[])
{
char *input[] = {" "};
char temp[20][20];
int i = 0;
int num = 0;
int place = 0;
int stringlen = sizeof(temp) / sizeof(char);
printf("How many names would you like to enter? ");
scanf("%d", &num);
while (place < num)
{
printf("Please input a name(first only): ");
scanf("%s", input[place]);
printf("The name you entered is: ");
printf("%s\n", input[place]);
place++;
}
//qsort(temp, stringlen, sizeof(char *), stringcmp); <-- just an idea I was messing with
qsort(input, stringlen, sizeof(char *), stringcmp);
printf("Names:\n");
for(i=0; i<place; i++)
printf("%s\n", input[i]);
system("PAUSE");
return(EXIT_SUCCESS);
}
The main problem is, when I go to output my code, I cannot use the char *input variable because of how its declared. The temp[] will display, but will not be sorted by qsort because it is not declared as a pointer. Any ideas?
You can't declare your input array like that. Since you know how many the user requires, you can dynamically allocate the array:
char **input = malloc(num * sizeof(char*));
Likewise, when you read your strings in, they need somewhere to go. Simply passing an uninitialized pointer to scanf is not okay. I suggest you define the maximum length of a name and have a temporary buffer for reading it:
const size_t MAX_NAME = 50;
char name[MAX_NAME];
...
for( i = 0; i < num; i++ )
{
printf("Please input a name(first only): ");
scanf("%s", name);
input[i] = strdup(name);
}
[Note this does not prevent the user from overflowing the 'name' buffer. I used scanf for illustrative purposes only]
You seem to be passing the wrong array length to qsort. Try this:
qsort(input, num, sizeof(char *), stringcmp);
When you are finished, you need to release memory for all the names and the array.
for( i = 0; i < num; i++ ) free(input[i]);
free(input);
could you explain
the ** declarations throughout the code? I'm not sure what they're
used for, although I know the function for stringcmp is a widely used
algorithm, I have no idea how it works; I'm thrown off by the double
de-reference markers.
Yep, in the case where I used it, I am telling C that to get a single character, I have to dereference a pointer twice. When you index a pointer, it's dereferencing. So I allocated an array by requesting a block of memory containing num * sizeof(char*) bytes. Because I assigned that pointer to a char**, the compiler knows that I am pointing to a chunk of memory that contains char* values.
If I ask for input[0] (this is the same as *input) it should look at the very start of that memory and pull out enough bytes to form a char*. When I ask for input[1], it skips past those bytes and pulls out the next bunch of bytes that form a char*. Etc... Likewise, when I index a char*, I am pulling out single characters.
In your stringcmp function, you have the following situation. You passed a void* pointer to qsort so it doesn't actually know the size of the data values stored in your array. That's why you have to pass both the array length AND the size of a single element. So qsort just blindly rips through this arbitrary-length array of arbitrary-sized values and fires off memory addresses that ought to contain your data for comparison. Because qsort doesn't know anything else about your array elements except where they are located, it just uses void*.
But YOU know that those pointers are going to be the memory addresses of two of your array elements, and that your array elements are char*. So you need the address of a char* (hence you cast the pointers to char**). Now you need to dereference these pointers when you call strcmp() because that function requires a char* (ie a value that points directly to the memory containing your string characters). That is why you use the * in strcmp(*ia, *ib).
One quick way to fix your program is to declare input as an array of pointers, like this:
char *input[20];
When you read names in, use tmp[place] for your buffer, and store the pointer into input, like this:
scanf("%19s", tmp[place]);
input[place] = tmp[place];
Now sorting the input should work fine.
This has a limitation of being limited to 20 lines of 20 characters max. If you learned about malloc in the class, you should be able to fix that by allocating your strings and the string array dynamically.
Say I want to read in a list of pages
name of max 19 character, e.g. 4
(Number of page) Name1 Name2 Name3
Name4
I am trying to use a global 2D array
to store the page number and page
name, I got an error saying assignment
from incompatiable pointer type...
Thanks
static int nPages;
static char** pageName;
int main(void){
scanf(" %d", &nPages);
pageName = (char *)malloc(nPages*sizeof(char));
for(int i=0; i < nPages ;i++){
pageName[i] = (char *)malloc(20*sizeof(char));
scanf(" %s", pageName[i]);
}
//Free Memory Here of coz.
return 0;
}
Never cast the return value of malloc() in C. It hides compiler warnings that actually help you, and it's never necessary so it just adds clutter. You should use:
pageName = malloc(nPages * sizeof *pageName);
Note how this is free from repetitions of the type name of pageName. Here, sizeof *pageName means "the size of the object pointed at by pageName", i.e. "the size of a character pointer". You should expect a sizeof expression as malloc()'s argument very often.
Also, sizeof (char) is always 1 in C, so that particular expression, it can be argued, adds more clutter than it helps make the code safe.
The problem lies right there:
pageName = (char *)malloc(nPages*sizeof(char));
pageName is a char **, not a char *. So it should read:
pageName = malloc(nPages*sizeof(char*)); // sizeof(char *), and no need to cast
EDIT: removed the cast
Your defined pageName as char ** but when calling malloc to initialize the pointer, you use a cast to (char *). Maybe that is the problem.
You are assigning char* to char** at your first malloc line. Just drop the (char*) cast - C handles void* to char** conversion automatically.
char* and char** are two different types, and you can't freely assign the first to the latter.
Having said that, you have to do a two-step initialization. First, you have to malloc() space for nPages string:
pageName = (char**) malloc(nPages * sizeof(char*));
then, you have to malloc() space for each pageName:
for (i = 0; i < nPages; ++i) {
pageName[i] = (char*) malloc(20);
}
I have an unsorted dictionary file named "dict.txt".
I have managed to put the words of the file in an array and the qsort() I use also seems to be working just fine (That is, the array is sorted).
The problem arises when I call bsearch(),
the program crashes and my question is:
Why is this happening?
I use gcc to compile and don't use an IDE of any sort so I don't have any debugger nor do I know how to use one (yet).
I am quite aware that the code presented here might contain several problems.
That is because I am quite new to c and my background is mainly Java (which despite the similarities seems to be a drawback, because I am so used to OO and c is obviously not OO).
Any advice would be greatly appreciated.
int strcmp_mod(const void *p1, const void *p2) {
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
int main(void) {
int size, i;
char **words;
char *pItem;
char *key = "fight";
char* buf = load_file("dict.txt"); if (buf == NULL) return 1;
size = count_words(buf);
words = (char**)malloc((size+1) * sizeof(char*));
for (i=0; i<size; i++) {
words[i] = (char*)malloc(80 * sizeof(char));
}
copy_words_to_lower(buf, words, size);
words[size] = '\0';
qsort(words, size, sizeof(char*), strcmp_mod);
for (i=0; i<size; i++) {
printf("%s\n", words[i]);
}
pItem = (char *) bsearch(key, words, size, sizeof(char*), strcmp_mod);
if (pItem!=NULL)
printf ("%s is in the array.\n", pItem);
else
printf ("%s is not in the array.\n", key);
return 0;
}
Try giving bsearch the address of key.
Why is this happening?
You're passing a char* as the key parameter to bsearch, but your comparator expects the result of casting a char** to void*.
Once you've fixed that, the next problem is that the return value from bsearch is a pointer to the matching item in the array. So again a char** not a char*.
Any advice would be greatly appreciated.
Either get a debugger, or else get ready to add lots of logging to your code.
Also the construction of your words array is slightly off. As it is it gets the job done, but it might be an idea to allocate the buffer for each word as you go, rather than all the same size at the start. Who knows if somebody will send you a file with a word in it more than 80 chars long? You terminate the list of words with a nul character, '\0', when you probably mean to terminate it with a null pointer, NULL. '\0' actually works, because it's another way of saying 0, and 0 converts to a null pointer. But it's not what you mean. And the array doesn't need to be null-terminated at all right now, because every time you use it after that you specify its length, size.