here is my code
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define STRING_LENGTH 20
#define MAX 30
int read_string(char string[], int n);
int compare(const void*a, const void*b);
int main(){
int i;
char* word_list[30];
char word[STRING_LENGTH + 1];
for (i = 0; ; i++){
printf("\nEnter a word.\n");
read_string(word, STRING_LENGTH);
if (word[0] == '\0')
break;
word_list[i] = (char *)malloc(STRING_LENGTH + 1);
//free(word_list);
strcpy(word_list[i], word);
}
int length = sizeof(word_list)/sizeof(char*);
int j;
qsort(word,length, sizeof(char*), compare);
for (j = 0; word_list[j] != '\0'; j++)
printf("%s\n", word_list[j]);
return 0;
}
int compare(const void*element1, const void *element2){
const char *string1 = *(const char**)element1;
const char *string2 = *(const char**)element2;
return strcmp(string1,string2);
}
int read_string(char string[], int n){
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
string[i++] = ch;
string[i] = '\0';
return i;
}
My program is supposed to read in strings via the read_string function and then strcpy is used to place them as elements of an array of pointers, then the names are sorted alphabetically. It compiles and reads i my input but it crashes once it gets to qsort(). I know qsort() is causing the problem but I don't know why. Any help would be very appreciated.
There are a number of issues as pointed out in the comments. The primary being you are calling qsort with the incorrect pointer, and incorrect number of members (30) instead of the number of strings read. A correction would be:
qsort (word_list, i, sizeof (char *), compare);
While you can use a sentinel to stop the print after qsort completes, why? You already know how many strings you read in i. So simply:
for (j = 0; j < i; j++)
printf ("%s\n", word_list[j]);
As an aside, it would be useful to be able to stop input after any number of strings in read_string. Allowing for an keyboard generated EOF to signal end of input by ctrl+d (ctrl+z on windows) will work. E.g.:
while ((ch = getchar ()) != '\n' && ch != EOF)
Notice that it is word_list and not word you would like to sort.
int main() {
int i;
char* word_list[30];
char word[STRING_LENGTH + 1];
for(i = 0; ; i++) {
printf("\nEnter a word.\n");
read_string(word, STRING_LENGTH);
if(word[0] == '\0')
break;
word_list[i] = (char *)malloc(STRING_LENGTH + 1);
strcpy(word_list[i], word);
}
// call qsort with the number of items in the wordlist
qsort(word_list, i, sizeof(char*), compare);
}
int compare(const void*element1, const void *element2){
const char **string1 = (const char**)element1;
const char **string2 = (const char**)element2;
return strcmp(*string1,*string2);
}
You should also call set length to the number of items in word_list before calling qsort() not to the number of characters in a word.
For instance if you have read in two words from stdin then you call qsort() like this qsort(word_list, 2, sizeof(char*), compare);
Perhaps I also need to mention that you should free the memory allocated by malloc() when you have finished using the word list.
Related
This is from an exercise in Chapter 9 of Programming in C 4th Edition. The programme is to read in characters into a string and extract a portion of the string into a sub-string by specifying a start position and number of characters.
The programme compiles and runs well except when the zeroth position of the source is stated as the start. Nothing is then displayed.
This is my code.
/* Programme to extract a portion from a string using function
sub-string (source, start, count, result) ex9.4.c
ALGORITHM
Get text input into a char array (declare to be fixed size);
Determine length of source string;
Prepare result array to be dynamic length using desired count + 1;
Copy from source array into result array
*/
#include <stdio.h>
#include <stdbool.h>
#define MAX 501
void read_Line (char buffer[]);
int string_Length (char string[]);
void sub_String (char source[], int start, int count, char result[]);
int main(void)
{
char strSource[MAX];
bool end_Of_Text = false;
int strCount = 0;
printf("This is a programme to extract a sub-string from a source string.\n");
printf("\nType in your text (up to 500 characters).\n");
printf("When you are done, press 'RETURN or ENTER'.\n\n");
while (! end_Of_Text)
{
read_Line(strSource);
if (strSource[0] == '\0')
{
end_Of_Text = true;
}
else
{
strCount += string_Length(strSource);
}
}
// Declare variables to store sub-string parameters
int subStart, subCount;
char subResult[MAX];
printf("Enter start position for sub-string: ");
scanf(" %i", &subStart);
getchar();
printf("Enter number of characters to extract: ");
scanf(" %i", &subCount);
getchar();
// Call sub-string function
sub_String(strSource, subStart, subCount, subResult);
return 0;
}
// Function to get text input
void read_Line (char buffer[])
{
char character;
int i = 0;
do
{
character = getchar();
buffer[i] = character;
++i;
}
while (character != '\n');
buffer[i - 1] = '\0';
}
// Function to count determine the length of a string
int string_Length (char string[])
{
int len = 0;
while (string[len] != '\0')
{
++len;
}
return len;
}
// Function to extract substring
void sub_String (char source[], int start, int count, char result[])
{
int i, j, k;
k = start + count;
for (i = start, j = 0; i < k || i == '\0'; ++i, ++j)
{
result[j] = source[i];
}
result[k] = '\0';
printf("%s\n", result);
}
I am using Code::Blocks on Linux Mint.
Being someone that just started learning programming recently with CS50 and 'Programming in C' books, I did not know how to setup the debugger in Code::Blocks. But thanks to the push by #paulsm4, I managed to get the debugger working. Using the watches window of the debugger, I could see that the while loop in the main function was overwriting the first character in the source array with a null character. The fix was to add a break statement. Thanks to #WhozCraig and #Pascal Getreuer for pointing out other errors that I had missed. This is the corrected code now:
/* Programme to extract a portion from a string using function
sub-string (source, start, count, result) ex9.4.c
ALGORITHM
Get text input into a char array (declare to be fixed size);
Determine length of source string;
Prepare result array to be dynamic length using desired count + 1;
Copy from source array into result array
*/
#include <stdio.h>
#include <stdbool.h>
#define MAX 501
void read_Line (char buffer[]);
int string_Length (char string[]);
void sub_String (char source[], int start, int count, char result[]);
int main(void)
{
char strSource[MAX];
bool end_Of_Text = false;
int strCount = 0;
printf("This is a programme to extract a sub-string from a source string.\n");
printf("\nType in your text (up to 500 characters).\n");
printf("When you are done, press 'RETURN or ENTER'.\n\n");
while (! end_Of_Text)
{
read_Line(strSource);
if (strSource[0] == '\0')
{
end_Of_Text = true;
}
else
{
strCount += string_Length(strSource);
}
break;
}
// Declare variables to store sub-string parameters
int subStart, subCount;
char subResult[MAX];
printf("Enter start position for sub-string: ");
scanf(" %i", &subStart);
getchar();
printf("Enter number of characters to extract: ");
scanf(" %i", &subCount);
getchar();
// Call sub-string function
sub_String(strSource, subStart, subCount, subResult);
return 0;
}
// Function to get text input
void read_Line (char buffer[])
{
char character;
int i = 0;
do
{
character = getchar();
buffer[i] = character;
++i;
}
while (character != '\n');
buffer[i - 1] = '\0';
}
// Function to count determine the length of a string
int string_Length (char string[])
{
int len = 0;
while (string[len] != '\0')
{
++len;
}
return len;
}
// Function to extract substring
void sub_String (char source[], int start, int count, char result[])
{
int i, j, k;
k = start + count;
// Source[i] == '\0' is used in case count exceeds source string length
for (i = start, j = 0; i < k || source[i] == '\0'; ++i, ++j)
{
result[j] = source[i];
}
result[j] = '\0';
printf("%s\n", result);
}
I'm writing code to find the longest row in the input stream and print it out. However, after I defined an int called max_count = 0, I always found an overflow, which displayed max_count as 1633771873. I've initialized that variable, so I don't know where the problem is. You probably do not need to figure out all of the functions, but each of them has its comment.
Here is my code:
#include <stdio.h>
#define DEFAULT 10
int getline(char line[], int limit);
void copy(char from[], char to[]);
int enlarge(int lim, char s[]);
main()
{
int i;
int max_count = 0;
char line[DEFAULT];
char maxline[DEFAULT];
while ((i = getline(line, DEFAULT)) != 0) {
if (i > max_count) { // where weird thing happend (max_count=1633771873)
max_count = i;
copy(line, maxline);
}
}
if (max_count > 0) {
printf("maxline: %s", maxline);
} else {
printf("No maxline");
}
return 0;
}
/*get a row from input stream and return its length*/
int getline(char s[], int lim)
{
int i, c;
for (i = 0; ((c = getchar()) != EOF) && (c != '\n'); ++i) {
if (i == lim - 1) {
lim = enlarge(lim, s);
}
s[i] = c;
}
if (c == '\n') {
s[i] = c;
++i;
}
if (i == lim) {
enlarge(lim, s);
}
s[i] = '\0';
return i;
}
/*copy an array to another */
void copy(char from[], char to[])
{
int i = 0;
while (from[i] != '\0') {
to[i] = from[i];
++i;
}
}
/*expand an array twice as its capacity*/
int enlarge(int lim, char s[])
{
s[lim - 1] = '\0';
lim *= 2;
char temp[lim];
copy(s, temp);
s = temp;
return lim;
}
This is the console window:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
^Z
maxline:
--------------------------------
Process exited after 15.19 seconds with return value 3221225477
You have a buffer with space for 10 characters:
#define DEFAULT 10
char line[DEFAULT];
You enter 37 characters including a newline:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Your getline function tries to store them all in line (by the way, enlarge doesn't do anything useful).
The first 10 characters fit into line. The other 27 characters and the terminating '\0' overwrite other random variables which come after line in memory.
That is why max_count holds the ASCII codes for aaaa.
Your enlarge function isn't doing what you think it is.
int enlarge(int lim, char s[])
{
s[lim - 1] = '\0';
lim *= 2;
char temp[lim];
copy(s, temp);
s = temp;
return lim;
}
You're creating a new array temp within the scope of the function. You then copy the address of the start of the array to s. Since s is a parameter to the function, modifying s won't be reflected in the calling function. So after this function returns s in getline is unchanged.
Even if you were to fix this by either returning a char * or changing the function to accept a char ** and assigning temp to the dereferenced pointer, you would be returning the address of a variable local to enlarge. That variable goes out of scope when the function returns and so the pointer would be invalid.
The only way you can change the size of an array is if you first allocate it dynamically with malloc and then later use realloc to change its size.
Also, getline is the name of a function on POSIX systems. You should change the name to something else.
I need help with the following program:
Print all sorted strings from the input array of strings.
Assume that strings are sorted if characters are in lexicographic order (also assume that the only characters in strings are letters).
Example:
INPUT:
n=2
1. string: stack
2. string: exchange
OUTPUT:
No sorted strings
I am having a problem with accessing some variables in the following program:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 30
char** read(int *pn);
int isSorted(char *str);
char** sortedArr(char **str,int pn);
char** read(int *pn)
{
do
{
printf("n = ");
scanf("%d",pn);
}
while(*pn < 1);
char **arr;
arr=(char **)malloc(*pn * sizeof(char *));
int i;
for(i=0; i<*pn;i++)
arr[i]=(char *)malloc(MAX+1);
for(i=0; i<*pn; i++)
{
printf("%d. word: ",i+1);
scanf("%s",arr[i]);
}
return arr;
}
int isSorted(char *str)
{
int i,j;
for(i=0; i<strlen(str)-1;i++)
for(j=i+1; j<strlen(str); j++)
if(strcmp(str+i,str+j) > 0)
return 1;
return 0;
}
char** sortedArr(char **str,int pn)
{
char **sortArr;
sortArr=(char **)malloc(pn * sizeof(char *));
int i;
for(i=0; i<pn; i++)
{
if(isSorted(sortArr[i]))
sortArr[i]=(char *)malloc(MAX+1);
}
for(i=0; i<pn; i++)
{
if(isSorted(sortArr[i]))
printf("%d. word: %s",sortArr+i);
}
return sortArr;
}
int main()
{
char **arr;
char **sortArr;
int pn;
arr=read(&pn);
sortArr=sortedArr(arr,pn);
int i;
for(i=0; i<pn; i++)
free(arr[i]);
free(arr);
return 0;
}
The problem is how to access the variable pn in function sortedArr()?
Your program has several issues, I'll try to address the most part of them.
Let's start from the isSorted function, if your goal is to check if a word has all the character sorted, you don't need to use strcmp or a nested loop. All what you need is something like this:
// check if all the character in the string are sorted
int isSorted( char *str )
{
int i, length = strlen(str) - 1;
for ( i=0; i < length; i++ )
if ( str[i] > str[i+1] )
return 0;
return 1;
}
I'll separate the printing function from the input one and copy ones, so to have:
void print( char **str, int n )
{
int i;
for ( i = 0; i < n; i++ ) {
printf("word %d: %s\n", i + 1, str[i]);
}
}
There are several methods to read some words from stdin and some are presented in other answers. I'll use this, but you should check all the pointers returned from malloc:
char** read(int *pn)
{
char buffer[BUFSIZE];
printf("Please, enter number of words: ");
while ( *pn < 1 && fgets(buffer, BUFSIZE, stdin) ) {
sscanf(buffer, "%d", pn);
}
int i = 0, length;
printf("\nPlease, enter the words: \n");
char **arr = malloc(*pn * sizeof(char *));
while ( i < *pn ) {
// read words one line at a time
if ( !fgets(buffer, BUFSIZE, stdin) )
break;
length = strlen(buffer);
// ignore empty lines
if ( length < 2 )
continue;
arr[i] = malloc(length);
memcpy(arr[i],buffer,length);
// add the null terminator
arr[i][length - 1] = '\0';
++i;
}
*pn = i;
return arr;
}
Then, I'm not really sure if you want only to print the words that are "sorted" or you actually need to copy them (and print later). I'll show you the latter:
// return an array of string containing only the sorted ones
char** sortedArr( char **str, int n, int *m)
{
char **sortArr = NULL;
char *sorted = calloc(n,1);
int i;
*m = 0;
// first find (and count) the sorted strings
for ( i = 0; i < n; i++ ) {
if ( isSorted(str[i]) ) {
// maybe you need only to print str[i] now...
sorted[i] = 1;
++(*m);
}
}
// then copy the sorted. I'm not sure if you need this
int j = 0, length = 0;
if ( *m ) {
sortArr = malloc(*m * sizeof(char *));
for ( i = 0; i < n; i++ ) {
if ( !sorted[i] )
continue;
length = strlen(str[i]) + 1;
sortArr[j] = malloc(length);
memcpy(sortArr[j],str[i],length);
}
}
free(sorted);
return sortArr;
}
Finally the main function (and the one to free allocated memory):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define BUFSIZE 128
char** read( int *pn );
int isSorted( char *str );
char** sortedArr( char **str, int n, int *m );
void print( char **str, int n );
void freeArr( char **str, int n );
void freeArr( char **str, int n )
{
if ( !str ) return;
int i;
for ( i = 0; i < n; i++ ) {
free(str[i]);
}
free(str);
}
int main()
{
char **arr = NULL;
char **sortArr = NULL;
int n = 0, m = 0;
arr = read(&n);
print(arr, n);
printf("\nSorted: \n");
sortArr = sortedArr(arr,n,&m);
print(sortArr, m);
freeArr(sortArr, m);
freeArr(arr, n);
return 0;
}
Hope it helped. Tell me if there is something wrong or I have misunderstood your task.
Change the sortedArr function, so that it takes pn as an argument:
char** sortedArr(char **str, int pn)...
Then, in your main(), change the sortArr=sortedArr(arr) to sortArr=sortedArr(arr, pn), to pass the value to the function. Then you can use the pn value inside the sortedArr function.
One head-scratcher is:
for(i=0; i<*pn;i++)
arr[i]=(char *)malloc(strlen(arr+i)+1);
for(i=0; i<*pn; i++)
{
printf("%d. word: ",i+1);
scanf("%s",arr[i]);
}
You should combine both because you cannot allocate properly until you know the length of word. Allocating some strlen(arr+i)+1 for who knows what is at arr+i is meaningless (and unless arr+i just happens to be a nul-terminated string, undefined behavior). When you are taking input, you can/should use a temprory buffer to hold the input until you can validate it is what you want it to be. At minimum, test the return to scanf to insure you actually had a sucessful conversion to the type of input you are expecting. For instance:
for (i = 0; i < *pn; i++)
{
char buf[64] = ""; /* however long is sufficient for word */
printf ("%d. word: ",i+1);
if (scanf ("%63[^\n']%*c",buf) == 1)
arr[i] = strdup (buf); /* allocates/copies buf for arr[i] */
else {
fprintf (stderr, "error: invalid input for word[%d].\n", i);
}
}
(note: you have similar allocation issues in sortedArr)
For your sort, you need to pass pn as a parameter to sortedArr so you at least know how many strings you are dealing with. Rather than looping and attempting to compare adjacent elements, why not have your sortedArr function simply call qsort and sort the array you create in read. If you want to preserve both the original and sorted arrays, then have sortedArr call memcpy and create a copy of arr and then call qsort on the copy. All you need to do is create a string comparison compare function for qsort to compare arr or the copy, e.g.:
/* comparison function for qsort (char **) */
int cmp_str (const void *a, const void *b)
{
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference
note: to reverse sort order, swap a and b, below */
return strcmp (* (char * const *) a, * (char * const *) b);
}
Which you can then use to sort your arr (or copy) with
qsort (arr, pn, sizeof *arr, cmp_str);
Much less error-prone then a one-off loop and index attempt. Give it a try and let me know if you have questions.
When I compile this code as it is, I get this error:
variable-sized object 'word_list' may not be initialized
char *word_list[i] = malloc(sizeof(char) * STRING_LENGTH );
I know on line 13 my variable word_list is just a pointer to characters and that it is causing the problem. I have to use an array of pointers instead of it but I am not sure how to do this properly. The word_list variable is supposed to be an array that is to have strings copied into it using strcpy.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define STRING_LENGTH 20
#define MAX 30
int read_string(char string[], int n);
int compare(const void*a, const void*b);
int main(){
int i;
char* word_list;
char word[STRING_LENGTH + 1];
for (i = 0; ; i++){
printf("\nEnter a word.\n");
read_string(word, STRING_LENGTH);
if (word[0] == '\0')
break;
char *word_list[i] = malloc(sizeof(char) * STRING_LENGTH );
free(word_list);
strcpy(word_list[i], word);
}
int length = sizeof(word_list)/sizeof(char*);
int j;
for (j = 0; word_list[j] != '\0'; j++)
printf("%s\n", word_list[j]);
qsort(word,length, sizeof(char*), compare);
for (j = 0; word_list[j] != '\0'; j++)
printf("%s\n", word_list[j]);
return 0;
}
int compare(const void*element1, const void *element2){
const char *string1 = *(const char**)element1;
const char *string2 = *(const char**)element2;
return strcmp(string1,string2);
}
int read_string(char string[], int n){
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
string[i++] = ch;
string[i] = '\0';
return i;
}
So if I am understanding correctly, you want an array of strings? (note this is just an array of pointers and you still need to malloc the space for each individual string)
So
char *word_list[NUM_STRINGS];
word_list[i] = malloc(strlen(word) + 1);
strcpy(word_list[i], word);
But it looks like all elements in your array will be the same length? In which case you can just use something like this, without the need for malloc or free:
char word_list[NUM_STRINGS][STRING_LENGTH+1];
strcpy(word_list[i], word);
If you're looking to have it loop indefinitely while their is user input then you won't be able to accomplish this easily with an array (only if you care about them going over the NUM_STRINGS limit). A linked list would work better in my opinion.
You declared word_list as a character pointer, not an array of same. If your malloc is intended to allocate space for one of the elements of an array, you need to create the array first. (The line with the error looks like you are declaring a variable.)
If you know how big this array needs to be, you should be all set. But if you don't, you might want to look into how realloc works.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define STRING_LENGTH 20
#define MAX 30
int read_string(char string[], int n);
int main(){
int i = 0;
char *name_list[MAX];
char word[STRING_LENGTH + 1];
for (;; i++){
printf("Enter a word.\n");
read_string(word, STRING_LENGTH);
if (word[i] == '\0')
break;
name_list[i] = malloc(sizeof(char) * 20);
strcat(name_list[i], word);
}
}
int read_string(char string[], int n){
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
string[i++] = ch;
string[i] = '\0';
return i;
}
The point of this program is to read in words and place them into an array of pointers for sorting. this is what i have so far, my debugger is saying that the use of strcat is unsafe but I do not know why. It says to use strcat_s but that crashes my program. Any help on how to get this working?
Ok, I tested your code and I came to the following final code that is working for me and does not give me a warning when compiled with -Wall.
Since you are using strcat instead of strcpy, the string stored in words gets added to the data in the array name_list. But because you didn't put all values in that array to 0, it could happen some garbage data is stored in name_list[i] and the words string gets concatenated after that garbage data.
Therefore I used calloc so all values in the memory you allocate are zero. Another way is to just keep malloc but then change strcat() in strcpy().
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define STRING_LENGTH 20
#define MAX 30
int read_string(char string[], int n);
int main(){
int i;
char *name_list[MAX];
char word[STRING_LENGTH + 1];
for (i = 0; i < MAX; i++){
printf("\nEnter a word.\n");
read_string(word, STRING_LENGTH);
printf("\nword%d=%s", i, word);
if (strcmp(word, "") == 0)
break;
name_list[i] = calloc(STRING_LENGTH + 1, 1);
strcat(name_list[i], word);
printf("\nname_list[%d] = %s", i, name_list[i]);
}
return 0;
}
int read_string(char string[], int n){
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
string[i++] = ch;
string[i] = '\0';
return i;
}
Use the memcpy() function :
void *memcpy(void *str1, const void *str2, size_t n)
or the strcpy() function :
char *strcpy(char *dest, const char *src)