I am trying to make an array with 10 pointers which will receive the input from scanf within a for loop. Then I want to call that list. However, I get a runtime error for memory:
#include <stdio.h>
int main(void) {
int i;
char * string[10];
//printf("%s", *string[0]);
for(i = 0; i<3; i++)
{
printf("what is string");
scanf("%s", string[i]);
printf("%s", string[i]);
}
return 0;
}
You need to think about the data structure with which you are attempting to store your strings.
char * string[10];
This declares an array of 10 elements of type "char *" or "pointer to a char".
Now think about what you are doing with this operation.
scanf("%s", string[i]);
You are using scanf to attempt to read user input. Does your function call make sense though?
The first parameter is your format string. "%s" makes sense because you want the user to provide you a string. Let's ignore the fact that you have not put any bounds on the length of that input for the moment.
Your second parameter needs to be be the variable where you want to store the input. You have provided "string[i]". Remember that in C, strings are just char arrays. However, your variable "string[i]" has the type "char *". This doesn't make sense. How can you store data of type "char array[]" in variable of type "char * array[]"? The types are different.
You need to create a buffer within which you can store your input string. Something like:
char buffer[256];
Then you call scanf like:
scanf("%s", buffer);
Keep in mind that this isn't the best idea because you don't know how many characters the user might enter. This will take the input character array and store them in a variable that correctly represents that data.
Note that this call would be safer:
fgets(buffer, sizeof(buffer)/sizeof(char), stdin);
fgets will read a fixed number of characters from the stream. In the above call I define that number by just taking the length of my buffer variable. I divide by sizeof(char) just to make sure that any strange representation issues where a char != 1 byte are taken care of. Also note that you will end up with the carraige return in your buffer (fgets will grab it).
Once you have your string in an appropriate variable, you can save it and get a pointer to a copy of that string with:
char * ptr = strdup(buffer);
This will create a new copy of the string stored in buffer and return a pointer to that new copy location. This pointer can then go into your array.
Note that you cannot just do:
string[i] = buffer
Because string[i] is just a pointer to the buffer variable...which will be overwritten during each iteration! You would end up with an array of pointers to the same variable, which would all end up giving you to the same string (the last input).
Put it all together in something like this:
char buffer[256];
printf("What is your string? ");
fgets(buffer, sizeof(buffer)/sizeof(char), stdin);
string[i] = strdup(buffer);
Because strdup() uses a malloc under the hood, you will need to free all your strings at the end of your program with:
int i = 0;
for(; i < sizeof(string)/sizeof(char *); i++) {
if(string[i]) free(string[i]);
}
You need to either allocate memeory dynamically or make your 2-d array:
char string[10][100];
You can do something like this to allocate memory -
for(i=0;i<10;i++){
string[i]=malloc(sizeof(char)*10);
}
So that your program does not give error.
But after this you have to free the memory allocated.
Related
I am trying to accept some values from the user and store them in a char pointer array like so:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *names[3];
char name[20];
int i;
for(i = 0; i < 3; i++) {
printf("Enter your name\n");
scanf("%s", name);
names[i] = (char *)malloc(strlen(name));
names[i] = &name;
// strcpy(names[i], name);
}
printf("Printing the names\n");
for(i = 0; i < 3; i++) {
printf("%s\n", names[i]);
}
}
However, for the following input, I get the following output
Input:
Mark Drew Andrew
Output:
Andrew Andrew Andrew
Why is this happening? When I use the strcpy function I have commented out instead, it seems to work fine.
names[i] = &name; is assigning every element of names to the same character buffer, so only the final version of what's in name will persist in the output.
You need to use strcpy (better still, strncpy) to copy the contents of name to names[i].
And don't forget to call free on every element of names when you're done.
You have memory leak in the code and also assigning the wrong assignment type . correct type assignment would be names[i]=name but still that won't solve the problem. Then also it wont work. You need to use strcpy to store different names. Here you have assigned to names[i] to the same variable - that's why the same output you got.
Note that &name is of type char(*)[20] which you assigned to char*(Compiler warned about this). And for all of the 3 input you got the pointer to array of char which is always the same - so it is pointing to the same array. And now the last value it contained is the input "Andrew". That's what it printed.
So the thing would be
strcpy(names[i],name);
Also scanf should be
if( scanf("%19s",name)!=1 ){
fprintf(stderr,"Error in input\n");
exit(EXIT_FAILURE);
}
malloc's return value should be checked and there is no need for casting.Because void* to char* conversion is implicitly done.
Another easy way would be to use strdup to duplicate the strings.
names[i]=strdup(name);
Also don't forget to free (using free() - this you will have to do in case of strdup also) the dynamically allocated memory when you are done working with it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *names[3];
char name[20];
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("Enter your name\n");
if(scanf("%19s", name)!= 1){
fprintf(stderr, "%s\n","Error in input" );
exit(EXIT_FAILURE);
}
names[i] = malloc(strlen(name)+1);
if( names[i] == NULL){
perror("malloc");
exit(EXIT_FAILURE);
}
strcpy(names[i],name);
}
printf("Printing the names\n");
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("%s\n", names[i]);
}
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++)
free(names[i]);
return 0;
}
names[0], names[1] and names[2] are pointing to same memory location name, for i=0 Mark will be stored in name for i=1 Drew will be stored and for i=2 Andrew will be stored, so by the end of the loop your array is pointing to name whose value is Andrew
C strings are simple arrays of characters, terminated by a trailing null character. You cannot assign arrays to other arrays, but you can assign them to pointers.
Now by doing names[i] = &name;, you are doing such a pointer assignment. Other than Java or C++ strings, just the address is copied to the pointer, there is no copying of string contents involved (by the way, &name is of bad type: char(*)[20], i. e. a pointer to array of length 20, you need a pointer to char, which you get by simply assigning name directly: names[i] = name;; name decays to a pointer automatically in this case).
The result is that all your string pointers in names point to one and the same character array name, overwriting the pointers to the arrays created by malloc (these are then lost completely, so you cannot free them again either, i. e. you have a memory leak!).
Instead, you have to copy the strings explicitly. However, to not forget the trailing null character:
int len = strlen(name) + 1;
// trailing null char(!): ^^^
names[i] = malloc(len);
memcpy(names[i], name, len);
Notice: using memcpy. Alternatives would have been strcpy or strncpy, but as length (including the trailing null character!) is known anyway, memcpy is most efficient...
Alternative could have been:
names[i] = malloc(20);
scanf("%19s", names[i]);
You spare copying for the price of the arrays potentially being too long. Have a close look on the format string: By adding a maximal length (you need to leave space for the terminating null character again, thus one less!) you prevent the user from writing beyond your buffer (which would be undefined behaviour and potentially lead to crash). If you do not return the array anywhere, even nicer:
char names[3][20];
Edit: Nice alternative, too: strdup (see coderredoc's answer); Two other important points:
always check the result of malloc for being null (memory allocation might have failed! - again see coderredoc's answer).
avoid (further) memory leaks by freeing the created strings again (not with my very last alternative)!
You use names[i] = &name; in every loop iteration, however name gets overwritten with different name, meaning at the end of each iteration, you have all your names[i] (up to the number of iterations so far) all point to name which obviously holds one specific name (which is "Andrew" in your case).
Consider how the memory changes while your code runs:
|--name--| ... |--names[0]--|,|--names[1]--|,|--names[2]--| (loop starting)
|--"Mark"--|...|--points to name, i.e. points to "Mark"--|,|--names[1]--|,|--names[2]--| (1st iteration)
|--"Drew "--|...|--points to name, i.e. points to "Drew "--|,|--points to name, i.e. points to "Drew "--|,|--names[2]--| (2nd iteration)
|--"Andrew"--|...|--points to name, i.e. points to "Andrew"--|,|--points to name, i.e. points to "Andrew"--|,|--points to name, i.e. points to "Andrew"--| (3rd iteration)
Fix this by using strcpy as you mentioned, which will result in:
|--name--| ... |--names[0]--|,|--names[1]--|,|--names[2]--| (loop starting)
|--"Mark"--|...|--copied the value of name, i.e. holds "Mark"--|,|--names[1]--|,|--names[2]--| (1st iteration)
|--"Drew "--|...|--copied the value of name, i.e. holds "Mark"--|,|--copied the value of name, i.e. holds "Drew "--|,|--names[2]--| (2nd iteration)
|--"Andrew"--|...|--copied the value of name, i.e. holds "Mark"--|,|--copied the value of name, i.e. holds "Drew "--|,|--copied the value of name, i.e. holds "Andrew"| (3rd iteration)
Basically, you pointed to variable that changed over time (name) instead of saving it's value (using strcpy)
#note: names[i] = (char *)malloc(strlen(name));should benames[i] = (char *)malloc(strlen(name) + 1);
The line names[i]=&name; doesn't make sense.
You should be getting a compilation error and if you aren't turn on all warnings and errors on your compiler.
It doesn't make sense because names[i] is a pointer to character (char*) and &name is a pointer to a pointer to character (char** or more accurately char(*)[20]).
But changing that to names[i]=name; won't help. It makes names[i] point to the start of the array name. There is only one instance of name. So all elements of names will point to the same location (name) at the end of the loop.
Here's a version with the basic problems fixed:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *names[3];
char name[20];
int i;
for(i = 0; i < 3; i++) {
printf("Enter your name\n");
scanf("%s", name);
names[i] = (char *)malloc(strlen(name)+1);//+1 to include NUL terminator.
strcpy(names[i], name);//Copy name into the space allocated.
}
printf("Printing the names\n");
for(i = 0; i < 3; i++) {
printf("%s\n", names[i]);
free(names[i]);//Release malloc'ed memory after use.
}
}
There's still a security issue that the user can exceed the name buffer by entering more than 19 characters. You should impose a limit on scanf and/or use scanf_s if available using scanf("%19s",name).
The mistake is in the following line ,
names[i] = &name;
it only contact the address of the name string , but it points the last value what the user is input which means it presistant with the last value only .
You must use string copy 'strcpy()' function to copy the input 'name' string in the array of pointer string 'name'.
strcpy(name[i],&name)
I need to read an array of n strings from 2 letters in each (e.g. n = 3, need to read something near "ab bf cs"). I use this code and get segmentation fault:
int n;
scanf("%d", &n);
char array[n][2];
char *tmp;
for (int i = 0; i < n; i++)
{
scanf("%s", &tmp);
strcpy(array[i], tmp);
}
Please help me to fix it!
Problem 1
To store a string of length 2, you need a char array of size 3. Use:
char array[n][3];
Problem 2
You are using &tmp in the call to scanf. That is wrong on two accounts.
The type is not appropriate. Type of &tmp is char**. scanf needs a char* with the %s format specifier.
&tmp cannot hold a string.
You could use
scanf("%s", tmp);
That will be OK from type point of view but it won't ok from run time point of view since tmp does not point to anything valid.
You can do away with tmp altogether and use:
scanf("%s", array[i]);
That entire block of code can be:
int n;
scanf("%d", &n);
char array[n][3];
for (int i = 0; i < n; i++)
{
// Make sure you don't read more than 2 chars.
scanf("%2s", array[i]);
}
There are a couple things you need to fix here.
Remember that one "string" (char[]) always ends with a null terminating character \0. So, whenever you create a char[], you must leave enough space for that. For example:
char str[3] = "foo"; looks like ['f', 'o', 'o'], but
char str[4] = "foo"; looks like ['f', 'o', 'o', '\0'].
Unexpected consequences can occur when using the former.
This means that you should change char array[n][2] to char array[n][3].
Remember that pointers are different than arrays. If you want to use a pointer, you must allocate memory for it, otherwise you will most likely seg fault.
In your program, it's simple enough that you can probably get away with not using a pointer, especially since you know the the length of each string that you will be storing (2).
You could change char *tmp to char tmp[3].
You can actually refactor your code so you don't need the tmp variable at all. If you want, you can just store the word you read into the array directly. For example:
scanf("%s", array[n]);
If you are really insistent on using that tmp variable as a char *, then allocate memory for it, like so:
char *tmp = (char*)malloc(3 * sizeof(char));
As a sidenote, if you are adhering to the ISO C90 standards, you shouldn't intermix variable declarations in code, so in your loop where you have for(int i = 0...), you should declare int i; at the top of the file, and then assign it in the loop for(i = 0...).
First, as user3121023 pointed out in the comments, char *tmp; creates a pointer without memory allocated to it. scanf() needs an array, and you need space for the terminating '\0' (which is probably the source of your segmentation fault). So you could try scanf("%s", array[i]);.
Second, it's a good idea to use "%2s" instead of "%s" so the user doesn't input 3 digits and overflow your buffer.
Here's one solution.
int n;
scanf("%d", &n);
char array[n][3];
for (int i = 0; i < n; i++)
{
scanf("%2s", array[i]);
}
In the code below, I hope you can see that I have a char* variable and that I want to read in a string from a file. I then want to pass this string back from the function. I'm rather confused by pointers so I'm not too sure what I'm supposed to do really.
The purpose of this is to then pass the array to another function to be searched for a name.
Unfortunately the program crashes as a result and I've no idea why.
char* ObtainName(FILE *fp)
{
char* temp;
int i = 0;
temp = fgetc(fp);
while(temp != '\n')
{
temp = fgetc(fp);
i++;
}
printf("%s", temp);
return temp;
}
Any help would be vastly appreciated.
fgetc returns an int, not a char*. This int is a character from the stream, or EOF if you reach the end of the file.
You're implicitly casting the int to a char*, i.e., interpreting it as an address (turn your warnings on.) When you call printf it reads that address and continues to read a character at a time looking for the null terminator which ends the string, but that address is almost certainly invalid. This is undefined behavior.
I've taken some liberties with what you wanted to accomplish. Rather that deal with pointers, you can just use a fixed sized array as long as you can set a maximum length. I've also included several checks so that you don't run off the end of the buffer or the end of the file. Also important is to make sure that you have a null termination '\0' at the end of the string.
#define MAX_LEN 100
char* ObtainName(FILE *fp)
{
static char temp[MAX_LEN];
int i = 0;
while(i < MAX_LEN-1)
{
if (feof(fp))
{
break;
}
temp[i] = fgetc(fp);
if (temp[i] == '\n')
{
break;
}
i++;
}
temp[i] = '\0';
printf("%s", temp);
return temp;
}
So, there are several problems here:
You're not setting aside any storage for the string contents;
You're not storing the string contents correctly;
You're attempting to read memory that doesn't belong to you;
The way you're attempting to return the string is going to give you heartburn.
1. You're not setting aside storage for the string contents
The line
char *temp;
declares temp as a pointer to char; its value will be the address of a single character value. Since it's declared at local scope without the static keyword, its initial value will be indeterminate, and that value may not correspond to a valid memory address.
It does not set aside any storage for the string contents read from fp; that would have to be done as a separate step, which I'll get to below.
2. You're not storing the string contents correctly
The line
temp = fgetc(fp);
reads the next character from fp and assigns it to temp. First of all, this means you're only storing the last character read from the stream, not the whole string. Secondly, and more importantly, you're assigning the result of fgetc() (which returns a value of type int) to an object of type char * (which is treated as an address). You're basically saying "I want to treat the letter 'a' as an address into memory." This brings us to...
3. You're attempting to read memory that doesn't belong to you
In the line
printf("%s", temp);
you're attempting to print out the string beginning at the address stored in temp. Since the last thing you wrote to temp was most likely a character whose value is < 127, you're telling printf to start at a very low and most likely not accessible address, hence the crash.
4. The way you're attempting to return the string is guaranteed to give you heartburn
Since you've defined the function to return a char *, you're going to need to do one of the following:
Allocate memory dynamically to store the string contents, and then pass the responsibility of freeing that memory on to the function calling this one;
Declare an array with the static keyword so that the array doesn't "go away" after the function exits; however, this approach has serious drawbacks;
Change the function definition;
Allocate memory dynamically
You could use dynamic memory allocation routines to set aside a region of storage for the string contents, like so:
char *temp = malloc( MAX_STRING_LENGTH * sizeof *temp );
or
char *temp = calloc( MAX_STRING_LENGTH, sizeof *temp );
and then return temp as you've written.
Both malloc and calloc set aside the number of bytes you specify; calloc will initialize all those bytes to 0, which takes a little more time, but can save your bacon, especially when dealing with text.
The problem is that somebody has to deallocate this memory when its no longer needed; since you return the pointer, whoever calls this function now has the responsibility to call free() when it's done with that string, something like:
void Caller( FILE *fp )
{
...
char *name = ObtainName( fo );
...
free( name );
...
}
This spreads the responsibility for memory management around the program, increasing the chances that somebody will forget to release that memory, leading to memory leaks. Ideally, you'd like to have the same function that allocates the memory free it.
Use a static array
You could declare temp as an array of char and use the static keyword:
static char temp[MAX_STRING_SIZE];
This will set aside MAX_STRING_SIZE characters in the array when the program starts up, and it will be preserved between calls to ObtainName. No need to call free when you're done.
The problem with this approach is that by creating a static buffer, the code is not re-entrant; if ObtainName called another function which in turn called ObtainName again, that new call will clobber whatever was in the buffer before.
Why not just declare temp as
char temp[MAX_STRING_SIZE];
without the static keyword? The problem is that when ObtainName exits, the temp array ceases to exist (or rather, the memory it was using is available for someone else to use). That pointer you return is no longer valid, and the contents of the array may be overwritten before you can access it again.
Change the function definition
Ideally, you'd like for ObtainName to not have to worry about the memory it has to write to. The best way to achieve that is for the caller to pass target buffer as a parameter, along with the buffer's size:
int ObtainName( FILE *fp, char *buffer, size_t bufferSize )
{
...
}
This way, ObtainName writes data into the location that the caller specifies (useful if you want to obtain multiple names for different purposes). The function will return an integer value, which can be a simple success or failure, or an error code indicating why the function failed, etc.
Note that if you're reading text, you don't have to read character by character; you can use functions like fgets() or fscanf() to read an entire string at a time.
Use fscanf if you want to read whitespace-delimited strings (i.e., if the input file contains "This is a test", fscanf( fp, "%s", temp); will only read "This"). If you want to read an entire line (delimited by a newline character), use fgets().
Assuming you want to read an individual string at a time, you'd use something like the following (assumes C99):
#define FMT_SIZE 20
...
int ObtainName( FILE *fp, char *buffer, size_t bufsize )
{
int result = 1; // assume success
int scanfResult = 0;
char fmt[FMT_SIZE];
sprintf( fmt, "%%%zus", bufsize - 1 );
scanfResult = fscanf( fp, fmt, buffer );
if ( scanfResult == EOF )
{
// hit end-of-file before reading any text
result = 0;
}
else if ( scanfResult == 0 )
{
// did not read anything from input stream
result = 0;
}
else
{
result = 1;
}
return result;
}
So what's this noise
char fmt[FMT_SIZE];
sprintf( fmt, "%%%zus", bufsize - 1 );
about? There is a very nasty security hole in fscanf() when you use the %s or %[ conversion specifiers without a maximum length specifier. The %s conversion specifier tells fscanf to read characters until it sees a whitespace character; if there are more non-whitespace characters in the stream than the buffer is sized to hold, fscanf will store those extra characters past the end of the buffer, clobbering whatever memory is following it. This is a common malware exploit. So we want to specify a maximum length for the input; for example, %20s says to read no more than 20 characters from the stream and store them to the buffer.
Unfortunately, since the buffer length is passed in as an argument, we can't write something like %20s, and fscanf doesn't give us a way to specify the length as an argument the way fprintf does. So we have to create a separate format string, which we store in fmt. If the input buffer length is 10, then the format string will be %10s. If the input buffer length is 1000, then the format string will be %1000s.
The following code expands on that in your question, and returns the string in allocated storage:
char* ObtainName(FILE *fp)
{
int temp;
int i = 1;
char *string = malloc(i);
if(NULL == string)
{
fprintf(stderr, "malloc() failed\n");
goto CLEANUP;
}
*string = '\0';
temp = fgetc(fp);
while(temp != '\n')
{
char *newMem;
++i;
newMem=realloc(string, i);
if(NULL==newMem)
{
fprintf(stderr, "realloc() failed.\n");
goto CLEANUP;
}
string=newMem;
string[i-1] = temp;
string[i] = '\0';
temp = fgetc(fp);
}
CLEANUP:
printf("%s", string);
return(string);
}
Take care to 'free()' the string returned by this function, or a memory leak will occur.
I have a text file called "graphics" which contains the words "deoxyribonucleic acid".
When I run this code it works and it returns the first character. "d"
int main(){
FILE *fileptr;
fileptr = fopen("graphics.txt", "r");
char name;
if(fileptr != NULL){ printf("hey \n"); }
else{ printf("Error"); }
fscanf( fileptr, "%c", &name);
printf("%c\n", name);
fclose( fileptr );
return 0;
}
When I am using the fscanf function the parameters I am sending are the name of the FILE object, the type of data the function will read, and the name of the object it is going to store said data, correct? Also, why is it that I have to put an & in front of name in fscanf but not in printf?
Now, I want to have the program read the file and grab the first word and store it in name.
I understand that this will have to be a string (An array of characters).
So what I did was this:
I made name into an array of characters that can store 20 elements.
char name[20];
And changed the parameters in fscanf and printf to this, respectively:
fscanf( fileptr, "%s", name);
printf("%s\n", name);
Doing so produces no errors from the compiler but the program crashes and I don't understand why. I am letting fscanf know that I want it to read a string and I am also letting printf know that it will output a string. Where did I go wrong? How would I accomplish said task?
This is a very common problem. fscanf reads data and copies it into a location you provide; so first of all, you need the & because you provide the address of the variable (not the value) - that way fscanf knows where to copy TO.
But you really want to use functions that copy "only as many characters as I have space". This is for example fgets(), which includes a "max byte count" parameter:
char * fgets ( char * str, int num, FILE * stream );
Now, if you know that you only allocated 20 bytes to str, you can prevent reading more than 20 bytes and overwriting other memory.
Very important concept!
A couple of other points. A variable declaration like
char myString[20];
results in myString being a pointer to 20 bytes of memory where you can put a string (remember to leave space for the terminating '\0'!). So you can usemyStringas thechar *argument infscanforfgets`. But when you try to read a single character, and that characters was declared as
char myChar;
You must create the pointer to the memory location "manually", which is why you end up with &myChar.
Note - if you want to read up to white space, fscanf is the better function to use; but it will be a problem if you don't make sure you have the right amount of space allocated. As was suggested in a comment, you could do something like this:
char myBuffer[20];
int count = fscanf(fileptr, "%19s ", myBuffer);
if(count != 1) {
printf("failed to read a string - maybe the name is too long?\n");
}
Here you are using the return value of fscanf (the number of arguments correctly converted). You are expecting to convert exactly one; if that doesn't work, it will print the message (and obviously you will want to do more than print a messageā¦)
Not answer of your question but;
for more efficient memory usage use malloc instead of a static declaration.
char *myName // declara as pointer
myName = malloc(20) // same as char[20] above on your example, but it is dynamic allocation
... // do your stuff
free(myName) // lastly free up your allocated memory for myName
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.