qsort not sorting and strange output - c

so I'm using C, I cant seem to get this to work right. It's an array of pointers to structs which contain some contact info. I can't seem to get the qsort to sort correctly.
Here is my code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 20
#define ELEMENTS 50
int sortZips(const void *a, const void *b);
typedef struct contactInfo {
char name[MAX];
char street[MAX];
char cityState[MAX];
char zipCode[MAX];
} contacts;
int main() {
int i = 0;
contacts **contactArray = malloc(ELEMENTS * sizeof(contacts *));
/* allocate array */
for (i = 0; i < ELEMENTS; i++) {
contactArray[i] = malloc(sizeof(contacts));
}
/* populate array */
for (i = 0; i < ELEMENTS; i++) {
fgets(contactArray[i]->name,MAX,stdin);
fgets(contactArray[i]->street,MAX,stdin);
fgets(contactArray[i]->cityState,MAX,stdin);
fgets(contactArray[i]->zipCode,MAX,stdin);
printf("%s", contactArray[i]->name);
printf("%s", contactArray[i]->street);
printf("%s", contactArray[i]->cityState);
printf("%s", contactArray[i]->zipCode);
}
printf("\n");
/* qsort((void *)contactArray, ELEMENTS, sizeof(contacts *), sortZips); */
for (i = 0; i < ELEMENTS; i++) {
fputs(contactArray[i]->name,stdout);
fputs(contactArray[i]->street,stdout);
fputs(contactArray[i]->cityState,stdout);
fputs(contactArray[i]->zipCode,stdout);
}
}
/* sortZips() sort function for qsort */
int sortZips(const void *a, const void *b) {
const contacts *ia = *(contacts **)a;
const contacts *ib = *(contacts **)b;
return strcmp(ia->zipCode, ib->zipCode);
}
The output is printing the addresses (I have 50 in an input file) and then some random characters, like a huge block of them, then the sorted list after that which is messed up and not sorted right.
Please any help would be appreciated. I need to learn what's wrong here and why.
Thanx.

First rule: always check input functions - in this case, fgets(). You don't know whether everything is working correctly or not if you do not check.
Second: use enum in preference to #define in general.
With the check for early EOF in place, your code sorted my sample data (6 rows) cleanly. It also compiled cleanly - which is very unusual (that's a compliment; I use stringent warnings and even my code seldom compiles cleanly the first time). My amended version of your code is very similar to yours:
int main(void)
{
int i = 0;
int num;
contacts **contactArray = malloc(ELEMENTS * sizeof(contacts *));
/* allocate array */
for (i = 0; i < ELEMENTS; i++)
contactArray[i] = malloc(sizeof(contacts));
/* populate array */
for (i = 0; i < ELEMENTS; i++)
{
if (fgets(contactArray[i]->name,MAX,stdin) == 0 ||
fgets(contactArray[i]->street,MAX,stdin) == 0 ||
fgets(contactArray[i]->cityState,MAX,stdin) == 0 ||
fgets(contactArray[i]->zipCode,MAX,stdin) == 0)
break;
printf("%s", contactArray[i]->name);
printf("%s", contactArray[i]->street);
printf("%s", contactArray[i]->cityState);
printf("%s", contactArray[i]->zipCode);
}
printf("\n");
num = i;
qsort(contactArray, num, sizeof(contacts *), sortZips);
for (i = 0; i < num; i++)
{
fputs(contactArray[i]->name,stdout);
fputs(contactArray[i]->street,stdout);
fputs(contactArray[i]->cityState,stdout);
fputs(contactArray[i]->zipCode,stdout);
}
return 0;
}
The data I used was trivial repetitions of sets of 4 lines like this:
First LastName7
7 Some Street
City, CA
95437
Note that the 'error checking' I do in the input is the bare minimum that 'works'. If you get an over-long line in the input, one field will not contain a newline, and the next will contain the next section of the input line (possibly all the rest, possibly not - it depends on how badly overlong the line is).

If your addresses are printing out rubbish at the end, then it's almost certainly because you haven't allocated enough space for them. Twenty characters is a little on the low side for addresses.
What's probably happening is that you have an address like:
14237 Verylongstreetname Avenue
and, when you do fgets (street,20,stdin);, only 14237 Verylongstree will be read (19 characters, leaving space for the null terminator).
And, here's the crux: the file pointer will still be pointing at the tname Avenue bit so that, when you try to read the cityState, you'll get that. And, when you try to read the zipCode, you'll get the cityState line, effectively stuffing up your sorting.

I believe that you have enough space. Because you are using fgets and your size of MAX, the strings should be cut to fit and have a terminating NUL at the end.
Two things that might be messing it up:
fgets will read from where it stopped reading if the line is too long. That will result in an address of "This is too long so", " it will be cut\n". And then the rest of the input will be all over the place.
If you do not have enough input to fill ELEMENTS items then you'll get whatever random data was in the malloc'd memory. If you were to use calloc instead, it would zero the memory for you. Although the better idea would be to use a counter of how many items were actually read, instead of assuming there will be ELEMENTS items.

Related

How to find the total number of a certain element in an array(C)

I'm trying to create a complete C program to read ten alphabets and display them on the screen. I shall also have to find the number of a certain element and print it on the screen.
#include <stdio.h>
#include <conio.h>
void listAlpha( char ch)
{
printf(" %c", ch);
}
int readAlpha(){
char arr[10];
int count = 1, iterator = 0;
for(int iterator=0; iterator<10; iterator++){
printf("\nAlphabet %d:", count);
scanf(" %c", &arr[iterator]);
count++;
}
printf("-----------------------------------------");
printf("List of alphabets: ");
for (int x=0; x<10; x++)
{
/* I’m passing each element one by one using subscript*/
listAlpha(arr[x]);
}
printf("%c",arr);
return 0;
}
int findTotal(){
}
int main(){
readAlpha();
}
The code should be added in the findTotal() element. The output is expected as below.
Output:
List of alphabets : C C C A B C B A C C //I've worked out this part.
Total alphabet A: 2
Total alphabet B: 2
Total alphabet C: 6
Alphabet with highest hit is C
I use an array to count the number of the existence of each character,
I did this code but the display of number of each character is repeated in the loop
int main()
{
char arr[100];
printf("Give a text :");
gets(arr);
int k=strlen(arr);
for(int iterator=0; iterator<k; iterator++)
{
printf("[%c]",arr[iterator]);
}
int T[k];
for(int i=0;i<k;i++)
{
T[i]=arr[i];
}
int cpt1=0;
char d;
for(int i=0;i<k;i++)
{int cpt=0;
for(int j=0;j<k;j++)
{
if(T[i]==T[j])
{
cpt++;
}
}
if(cpt>cpt1)
{
cpt1=cpt;
d=T[i];
}
printf("\nTotal alphabet %c : %d \n",T[i],cpt);
}
printf("\nAlphabet with highest hit is : %c\n",d,cpt1);
}
There is no way to get the number of elements You write in an array.
Array in C is just a space in the memory.
C does not know what elements are actual data.
But there are common ways to solve this problem in C:
as mentioned above, create an array with one extra element and, fill the element after the last actual element with zero ('\0'). Zero means the end of the actual data. It is right if you do not wish to use '\0' among characters to be processed. It is similar to null-terminated strings in C.
add the variable to store the number of elements in an array. It is similar to Pascal-strings.
#include <stdio.h>
#include <string.h>
#define ARRAY_SIZE 10
char array[ARRAY_SIZE + 1];
int array_len(char * inp_arr) {
int ret_val = 0;
while (inp_arr[ret_val] != '\0')
++ret_val;
return ret_val;
}
float array_with_level[ARRAY_SIZE];
int array_with_level_level;
int main() {
array[0] = '\0';
memcpy(array, "hello!\0", 7); // 7'th element is 0
printf("array with 0 at the end\n");
printf("%s, length is %d\n", array, array_len(array));
array_with_level_level = 0;
const int fill_level = 5;
int iter;
for (iter = 0; iter < fill_level; ++iter) {
array_with_level[iter] = iter*iter/2.0;
}
array_with_level_level = iter;
printf("array with length in the dedicated variable\n");
for (int i1 = 0; i1 < array_with_level_level; ++i1)
printf("%02d:%02.2f ", i1, array_with_level[i1]);
printf(", length is %d", array_with_level_level);
return 0;
}
<conio.h> is a non-standard header. I assume you're using Turbo C/C++ because it's part of your course. Turbo C/C++ is a terrible implementation (in 2020) and the only known reason to use it is because your lecturer made you!
However everything you actually use here is standard. I believe you can remove it.
printf("%c",arr); doesn't make sense. arr will be passed as a pointer (to the first character in the array) but %c expects a character value. I'm not sure what you want that line to do but it doesn't look useful - you've listed the array in the for-loop.
I suggest you remove it. If you do don't worry about a \0. You only need that if you want to treat arr as a string but in the code you're handling it quite validly as an array of 10 characters without calling any functions that expect a string. That's when it needs to contain a 0 terminator.
Also add return 0; to the end of main(). It means 'execution successful' and is required to be conformant.
With those 3 changes an input of ABCDEFGHIJ produces:
Alphabet 1:
Alphabet 2:
Alphabet 3:
Alphabet 4:
Alphabet 5:
Alphabet 6:
Alphabet 7:
Alphabet 8:
Alphabet 9:
Alphabet 10:-----------------------------------------List of alphabets: A B C D E F G H I J
It's not pretty but that's what you asked for and it at least shows you've successfully read in the letters. You may want to tidy it up...
Remove printf("\nAlphabet %d:", count); and insert printf("\nAlphabet %d: %c", count,arr[iterator]); after scanf(" %c", &arr[iterator]);.
Put a newline before and after the line of minus signs (printf("\n-----------------------------------------\n"); and it looks better to me.
But that's just cosmetics. It's up to you.
There's a number of ways to find the most frequent character. But at this level I recommend a simple nested loop.
Here's a function that finds the most common character (rather than the count of the most common character) and if there's a tie (two characters with the same count) it returns the one that appears first.
char findCommonest(const char* arr){
char commonest='#'; //Arbitrary Bad value!
int high_count=0;
for(int ch=0;ch<10;++ch){
const char counting=arr[ch];
int count=0;
for(int c=0;c<10;++c){
if(arr[c]==counting){
++count;
}
}
if(count>high_count){
high_count=count;
commonest=counting;
}
}
return commonest;
}
It's not very efficient and you might like to put some printfs in to see why!
But I think it's at your level of expertise to understand. Eventually.
Here's a version that unit-tests that function. Never write code without a unit test battery of some kind. It might look like chore but it'll help debug your code.
https://ideone.com/DVy7Cn
Footnote: I've made minimal changes to your code. There's comments with some good advice that you shouldn't hardcode the array size as 10 and certainly not litter the code with that value (e.g. #define ALPHABET_LIST_SIZE (10) at the top).
I have used const but that may be something you haven't yet met. If you don't understand it and don't want to learn it, remove it.
The terms of your course will forbid plagiarism. You may not cut and paste my code into yours. You are obliged to understand the ideas and implement it yourself. My code is very inefficient. You might want to do something about that!
The only run-time problem I see in your code is this statement:
printf("%c",arr);
Is wrong. At this point in your program, arr is an array of char, not a single char as expected by the format specifier %c. For this to work, the printf() needs to be expanded to:
printf("%c%c%c%c%c%c%c%c%c%c\n",
arr[0],arr[1],arr[2],arr[3],arr[4],
arr[5],arr[6],arr[7],arr[8],arr[9]);
Or: treat arr as a string rather than just a char array. Declare arr as `char arr[11] = {0};//extra space for null termination
printf("%s\n", arr);//to print the string
Regarding this part of your stated objective:
"I shall also have to find the number of a certain element and print it on the screen. I'm new to this. Please help me out."
The steps below are offered to modify the following work
int findTotal(){
}
Change prototype to:
int FindTotal(char *arr);
count each occurrence of unique element in array (How to reference)
Adapt above reference to use printf and formatting to match your stated output. (How to reference)

How to set the values from the token character to this array called customerData[][] in C?

I just started learning C language and I need some help with a program. Here is the code.
Questions:
What is this? customerData[NUM_FIELDS][FIELD_LENGTH];
Is it a char 2D array?
How do you input data into the array? fgetC, putchar, getchar ?
#include <stdio.h> #include <string.h> #include <stdlib.h>
#define INPUT_LENGTH 128
#define FIELD_LENGTH 30
#define NUM_FIELDS 9
int main()
{
FILE *data=NULL;
char input[INPUT_LENGTH];
char customerData[NUM_FIELDS][FIELD_LENGTH];
int element=0;
char *next;
char ch;
data= fopen("data.txt","r");
if(data!=NULL)
{
//token=strtok(input,"|");
/*while loop will go through line by line and stored it in an input array*/
while(fgets(input,INPUT_LENGTH,data)!= NULL)
{
next=strtok(input,"|");
while(next!=NULL)
{
//ch=getchar()
//>probably a get char for ch
strcpy(next,customerData[element][strlen(next)]);
/*need to put the values into customer data one by one*/
printf("%s\n",next);
//element+=1;
next=strtok(NULL,"|");
}
//element=0;
}
printf("program is done\n");
}
fclose(data);
return 0;
}
In general, "help me with my code" questions are off-topic on Stack Overflow. In order to keep the question on-topic, I'm going to focus only on the question of how to access 2D char arrays.
Yes, this is a 2D char array. Or, put another way, it's an array with NUM_FIELDS elements, where each element of the array is a char array with FIELD_LENGTH elements.
There are loads of ways to insert data into a 2D char array, but there are probably two I've encountered most often. Which one you choose to use will depend on how you want to think of this array.
Option 1: A 2D array of single chars
The first way to think about this variable is simply as a 2D array of chars - a grid of elements that you can access. Here, you can simply input values using the normal assignment operator. You'll want to make sure that your indexes are in range, or you'll start accessing invalid memory.
//Set a known element that's definitely in range
customerData[1][2] = 'A';
//Loop through all the elements
for(int ii = 0; ii < NUM_FIELDS; ii++)
{
for (int jj = 0; jj < FIELD_LENGTH; jj++)
{
customerData[i][j] = 'B';
}
}
//Set an element from variables
char nextInput = getNextCharFromInput();
if(x < NUM_FIELD && y < FIELD_LENGTH)
{
customerData[x][y] = nextInput;
}
//Bad. This could corrupt memory
customerData[100][60] = 'X';
//Risky without check. How do you know x and y are in range?
cusomterData[x][y] = 'X';
You could certainly write your code by assigning these elements on character at a time. However, the broader context of your program heavily implies to me that the next option is better.
Option 2: A 1D array of fixed-length strings
In C, a "string" is simply an array of chars. So another way to look at this variable (and the one that makes the most sense for this program) is to treat it as a 1D array of length NUM_FIELDS, where each element is a string of length FIELD_LENGTH.
Looking at this this way, you can start using the C string functions to input data into the array, rather than needing to deal character by character. As before, you still need to be careful of lengths so that you don't go off the end of the strings.
Also be aware that all array decay into pointers, so char* is also a string (just of unknown length).
//Set a specific field to a known string, which is short enough to fit
strcpy(customerData[2], "date");
//Loop through all fields and wipe their data
for(int ii = 0; ii < NUM_FIELDS; ii++)
{
memset(customerData[ii], 0, FIELD_LENGTH);
}
//Set field based on variables
if(x < NUM_FIELDS)
{
//Will truncate next if it is too long
strncpy(customerData[x], next, FIELD_LENGTH);
//Will not input anything if field is too long
if(strlen(next) < FIELD_LENGTH)
{
strcpy(customerData[x], next);
}
}
//Bad. Could corrupt memory
strcpy(customerData[100], "date");
strcpy(customerData[1], "this string is definitely much longer than FIELD_LENGTH");
//Risky. Without a check, how do you know either variable in in range?
strcpy(customerData[x], next);
getchar and fgetC both deal with reading characters, from stdout and a file respectively, so can't be used to put data into a variable. putchar does deal with put character into things, but only stdout, so can't be used here.

Alphabetically Ordering an array of words

I'm studying C on my own in preparation for my upcoming semester of school and was wondering what I was doing wrong with my code so far.
If Things look weird it is because this is part of a much bigger grab bag of sorting functions I'm creating to get a sense of how to sort numbers,letters,arrays,and the like! I'm basically having some troubles with the manipulation of strings in C currently.
Also, I'm quite limited in my knowledge of C at the moment!
My main Consists of this:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
int numbers[10];
int size;
int main(void){
setvbuf(stdout,NULL,_IONBF,0); //This is magical code that allows me to input.
int wordNumber;
int lengthOfWord = 50;
printf("How many words do you want to enter: ");
scanf("%i", &wordNumber);
printf("%i\n",wordNumber);
char words[wordNumber][lengthOfWord];
printf("Enter %i words:",wordNumber);
int i;
for(i=0;i<wordNumber+1;i++){ //+1 is because my words[0] is blank.
fgets(&words[i], 50, stdin);
}
for(i=1;i<wordNumber+1;i++){ // Same as the above comment!
printf("%s", words[i]); //prints my words out!
}
alphabetize(words,wordNumber); //I want to sort these arrays with this function.
}
My sorting "method" I am trying to construct is below! This function is seriously flawed, but I'd thought I'd keep it all to show you where my mind was headed when writing this.
void alphabetize(char a[][],int size){ // This wont fly.
size = size+1;
int wordNumber;
int lengthOfWord;
char sortedWords[wordNumber][lengthOfWord]; //In effort for the for loop
int i;
int j;
for(i=1;i<size;i++){ //My effort to copy over this array for manipulation
for(j=1;j<size;j++){
sortedWords[i][j] = a[i][j];
}
}
//This should be kinda what I want when ordering words alphabetically, right?
for(i=1;i<size;i++){
for(j=2;j<size;j++){
if(strcmp(sortedWords[i],sortedWords[j]) > 0){
char* temp = sortedWords[i];
sortedWords[i] = sortedWords[j];
sortedWords[j] = temp;
}
}
}
for(i=1;i<size;i++){
printf("%s, ",sortedWords[i]);
}
}
I guess I also have another question as well...
When I use fgets() it's doing this thing where I get a null word for the first spot of the array. I have had other issues recently trying to scanf() char[] in certain ways specifically spacing my input word variables which "magically" gets rid of the first null space before the character. An example of this is using scanf() to write "Hello" and getting " Hello" or " ""Hello"...
Appreciate any thoughts on this, I've got all summer to study up so this doesn't need to be answered with haste! Also, thank you stack overflow as a whole for being so helpful in the past. This may be my first post, but I have been a frequent visitor for the past couple of years and it's been one of the best places for helpful advice/tips.
You're going to like this - it's a derivation of QSort, adapted to your situation. It may not work quite perfectly for you without a touchup here or there (TEST FIRST!):
void qsort (Strings[], NumberOfItems)
{
char Temp[51]; // Assumes your max string length of 50
int I1 = 0; // Primary index
int I2 = 0; // Index + 1
int NumberOfItems_1 = 0;
bool Swapped = false;
do // Outer loop
{
Swapped = false;
// Decrement our limit
NumberOfItems--;
// Save time not performing this subtraction many times
NumberOfItems_1 = NumberOfItems - 1;
// Repeatedly scan the list
for (I1 = 0; I1 < NumberOfItems_1; I1++)
{
// Save time not performing this addition many times
// I1 points to the current string
// This points to the next string
I2 = I1 + 1;
// If the current string is greater than the next string in the list,
// swap the two strings
if (strcmp(Strings[I1], Strings[I2]) > 0)
{
strcpy (Temp, Strings[I1]);
strcpy (Strings[I1], Strings[I2]);
strcpy (Strings[I2], Temp);
Swapped = true;
}
}
}
while (Swapped); // Break out when we've got nothing left to swap
}
I see a few things wrong with your code off the bat. First, you declare sortedWords as a multidimensional array (since you have sortedWords[wordnumber][lengthofword], but you try to use it with only one index later on.. this doesn't work! Also, your passing of the 2D array is not valid. Check out this post to see the valid ways to pass a 2D array: Passing a 2D array to a C++ function
Function declaration and definition
The function declaration is invalid, as you've found out. You must specify the size of every dimension of the array except the first, so you might write:
void alphabetize(char a[][SOMESIZE], int size)
However, you have a non-constant second dimension (so you're using a VLA or variable length array), which means that you need to pass both sizes to the function, and pass them before you pass the array:
void alphabetize(int size, int length, char a[size][length])
and then invoke it:
alphabetize(wordNumber, lengthOfWords, words);
Of course, you should also declare the function before you try calling it.
There are probably other issues to be addressed, but this is the immediate one that jumps out. For example, you need to use size and length to control the loops in the function. You probably don't need to copy the data into the local array (in which case you don't need the local array). Etc.
You should consider compiling with options such as:
gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \
-Wold-style-definition -Wold-style-declaration -Werror …
Note that note all versions of GCC support all those options, but use as many as are supported.
Input issue
You have:
int i;
for (i = 0; i < wordNumber + 1; i++) { //+1 is because my words[0] is blank.
fgets(&words[i], 50, stdin);
}
You're stepping out of bounds of your array, which potentially wreaks havoc on your code. The first entry is blank because scanf() leaves the newline in the input buffer. You should read to the end of line before going to line-based input:
int c;
while ((c = getchar()) != EOF && c != '\n')
;
You should also check the fgets() returns a non-null pointer; don't continue if it does.

Find size of input char* and copy portion to output char* C

I have a char array LL,4014.84954 that I send into a function like this example:
#include <stdio.h>
#include <math.h>
void myFunction(char* in_string, char* out_string) {
printf("Start_String=%s\n", in_string);
int in_size = (int)(sizeof(in_string));
printf("%d\n", in_size);
int i = 0;
for(i = 0; i <= in_size-ceil(in_size/2); i++) {
out_string[i] = in_string[i];
}
}
int main(int arg) {
char in_string[] = "LL,4014.84954";
char out_string[] = "";
printf("In_String=%s\n", in_string);
myFunction(in_string, out_string);
printf("Out_String=%s\n", out_string);
}
My question has two parts.
How do I get the length of this char array? int in_size = (int)(sizeof(in_string)); in this example gives me 8 which is the size of the pointer (long int). I know I could make a for loop that marches through until it see the null termination, but is there a nicer way? I previously was using char[] and sizeof works great, but now I am converting to char*.
How can I write a portion of these chars to out_string. My example currently writes all chars to out_string.
Here is the raw output:
In_String=LL,4014.84954
Start_String=LL,4014.84954
8
Out_String=LL,40014.84954
(1)
Answer to question 2:
char out_string[] = "";
out_string[] is of only one size. you assigning out_string[i] = ... for i > 0 is wrong and cause an undefined error. Instead of this you should declare out_string[] like this:
out_string[] = malloc(strlen(in_string) + 1);
// ^ extra for \0 char
(2)
additionally #WhozCraig commenting correct, you actually need strlen() to find length of string. sizeof you are using wrong.
So replace:
int in_size = (int)(sizeof(in_string));
by
int in_size = strlen(in_string);
(3)
Also, what is in_size-ceil. As I can understands from your raw output you don't need such kind of function and calculations. Just replace your for loop:
for(i = 0; i <= in_size-ceil(in_size/2); i++)
by
for(i = 0; i < in_size; i++)
(4)
At the end don;t forget to terminate you string out_string, after for loop add this line
out_string[i] = '\0'
Regarding your first question, use strlen().
Regarding the second question, first of all you need to make sure that out_string is wide enough to accommodate the result. Currently, it isn't, and the behaviour of your code is undefined. Once you fix that, to copy a portion of the string you'd need to change the initial and final conditions of the for loop, not forgetting about the NUL terminator.

Use of array of arrays of string in C for parsing text file

I would like to read from N text files (having similar structure: a few lines, each line having the same small number of words) and store in a string matrix the words read, in such a way that in each (row, col) position I have one word.
A simple (two lines, three words per line) specimen for the files is the following:
line1word1 line1word2 line1word3
line2word1 line2word2 line2word3
Delimiter for the words is space.
I have attempted this code:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_LENGTH 1000
#define MAX_TOKS 100
#define DELIMITERS " "
// line parsing utility
int parseString(char* line, char*** argv) {
char* buffer;
int argc;
buffer = (char*) malloc(strlen(line) * sizeof(char));
strcpy(buffer,line);
(*argv) = (char**) malloc(MAX_TOKS * sizeof(char**));
argc = 0;
(*argv)[argc++] = strtok(buffer, DELIMITERS);
while ((((*argv)[argc] = strtok(NULL, DELIMITERS)) != NULL) &&
(argc < MAX_TOKS)) ++argc;
return argc;
}
int main() {
char S[MAX_STRING_LENGTH];
char **A;
int n,i,j,l;
FILE *f;
char file[50];
char ***matrix;
matrix = malloc(MAX_TOKS * sizeof(char**));
//memory allocation for matrix
for (i = 0; i < MAX_TOKS; i++)
{
matrix[i] = malloc(MAX_TOKS * sizeof(char *));
for (j = 0; j < MAX_TOKS; j++)
{
matrix[i][j] = malloc(MAX_TOKS * sizeof(char));
}
}
int NFILE = 10; // number of files to be read
for(i=0;i<NFILE;i++)
{
sprintf(file,"file%d.txt",i);
f = fopen(file,"r");
l=0; // line-in-file index
while(fgets(S,sizeof(S),f)!=NULL) {
n = parseString(S,&A);
for(j=0;j<n;j++) {
matrix[i][l]=A[j];
printf("%s\t%s\n",matrix[i][l],A[j]);
}
l++;
}
fclose(f);
}
free(matrix);
free(A);
return(0);
}
The problem I can't solve is that there when checking for correspondance between the arrays (in order to be sure I am storing the single words correctly) using
printf("%s\t%s\n",matrix[i][l],A[j]);
I find that the last word (and only the last one) of each line, regardless of the file number, is not stored in matrix. That is to say, line1word1 and line1words of file0 are correctly stored in matrix[0][0][0] and matrix[0][0][1], but in the field matrix[0][0][2] there isn't line1word3, even if A[2] has it!
What am I doing wront? Any suggestion?
Many thanks in advance,
cheers
char ***matrix doesn't declare a three dimensional array. Your matrix would need to be something like char *matrix[a][b] to hold a two dimensional array of string pointers. In order to calculate addresses within an array, the compiler needs to know the all of dimensions but one. If you think about it, you will probably see why...
If you have two arrays:
1 2 3 1 2 3 4 5 6 7
4 5 6 8 9 10 11 12 13 14
7 8 9 15 16 17 18 19 20 21
You can see that item[1][1] is NOT the same item. Regardless of the dimensions in your array, the elements are typically arranged sequentially in memory, with each row following the previous (or possible column, depending on language, I suppose.) If you have an array of pointers, the actual content may be elsewhere, but the points would be arranged like this. So, in my examples above, you must provide the compiler with the number of columns so that it can find members (the number of rows can be variable.) In a three dimensional array, you must provide the first TWO dimensions so that the compiler may calculate item offsets.
I hope that helps.
EDIT: You can have truly dynamic array dimensions by creating your own function to process all array item accesses. The function would need to know the dynamic dimensions and the item index(s) so that it could calculate the appropriate address.
This looks wrong: buffer = (char*) malloc(strlen(line) * sizeof(char));
Firstly, there is no need to cast malloc in C. If your code doesn't compile without the cast, there are two possible reasons:
There is no prototype for malloc. Obviously this can cause problems, because no prototype means the function returns a default type: int, or an error occurs. This can cause your program to misbehave. To avoid this, #include <stdlib.h>.
You're using a C++ compiler. Stop. Either program in C++ (stop using malloc) or use a C compiler. If you want to use this project in a C++ project, compile your C code with a C compiler and link to it in your C++ compiler.
Secondly, sizeof(char) is always 1. There is no need to multiply by it.
Thirdly, a string is a sequence of characters ending at the first '\0'. This means a string always occupies at least 1 character, even if it is an empty string. What does strlen("") return? What is sizeof("")? You need to add 1 to make room for the '\0': buffer = malloc(strlen(line) + 1);.
This looks slightly wrong: (*argv) = (char**) malloc(MAX_TOKS * sizeof(char**));
malloc returns a pointer to an object. *argv is a char **, which means it points to a char *. However, in this case malloc returns a pointer to char ** objects. The representation isn't required to be identical. To avoid portability issues assosciated with this, follow this pattern variable = malloc(n * sizeof *variable); ... in this case, *argv = malloc(MAX_TOKS * **argv);
It gets more gritty as it goes. Forget everything you think you know about your code; Pretend you're going to come back to this in 24 months. What are you going to think of this?
argc = 0;
(*argv)[argc++] = strtok(buffer, DELIMITERS);
while ((((*argv)[argc] = strtok(NULL, DELIMITERS)) != NULL) &&
(argc < MAX_TOKS)) ++argc;
There's actually an off-by-one here, too. Assuming argc == MAX_TOKS, your loop would attempt to assign to (*argv)[MAX_TOKS]. This loop is where I believe your problem lies, and the solution is to express your intent more clearly rather than attempting to cram as much code into one line as possible. How would you rewrite this? Here's what I'd do, in this situation:
char *arg;
size_t argc = 0;
do {
arg = strtok(buffer, DELIMITERS);
buffer = NULL;
(*argv)[argc] = arg;
argc++;
} while (argc < MAX_TOKS && arg != NULL);
The problem is that your parsing loop doesn't increment when strtok returns NULL. Hence, your function returns the position of the last item. Supposing you had two tokens, your parsing function would return 1. Your display loop displays items up to, but not including this position: for(j=0;j<n;j++). You could use the suggested improvement, or change your loop: for (j = 0; j <= n; j++). Either way, you'll need to fix those off-by-ones.
Out of curiosity, which book are you reading?

Resources