Uninitialised values in dynamic array in C - c

I've been given a task that requires a dynamic 2D array in C, but we haven't even covered pointers yet, so I'm kind of at a loss here. I have to read some text input and store it in a 2D array, without limiting its size.
Unfortunately, Valgrind keeps throwing me an error saying that there's an uninitialised value, when the puts() function executes and sometimes it prints out some random signs. I understand that I must have omitted some indexes, but I just can't find where the issue stems from. Additionally, all advices regarding the quality of my code are very much appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
#define MULT 3
#define DIV 2
char **read(int *row, int *col) {
char **input = NULL;
int row_size = 0;
int col_size = 0;
int i = 0;
int c;
while ((c = getchar()) != EOF) {
if (c != '\n') { // skip empty lines
assert(i < INT_MAX);
if (i == row_size) { // if not enough row memory, allocate more
row_size = 1 + row_size * MULT / DIV;
input = realloc(input, row_size * sizeof *input);
assert(input != NULL);
}
char *line = NULL;
int j = 0;
// I need all the rows to be of the same size (see last loop)
line = malloc(col_size * sizeof *line);
// do while, so as to not skip the first character
do {
assert(j < INT_MAX-1);
if (j == col_size) {
col_size = 1 + col_size * MULT / DIV;
line = realloc(line, col_size * sizeof *line);
assert(line != NULL);
}
line[j++] = c;
} while(((c = getchar()) != '\n') && (c != EOF));
// zero-terminate the string
if (j == col_size) {
++col_size;
line = realloc(line, col_size * sizeof *line);
line[j] = '\0';
}
input[i++] = line;
}
}
// Here I give all the lines the same length
for (int j = 0; j < i; ++j)
input[j] = realloc(input[j], col_size * sizeof *(input+j));
*row = i;
*col = col_size;
return input;
}
int main(void) {
int row_size, col_size, i, j;
char **board = read(&row_size, &col_size);
// Initialize the remaining elements of each array
for (i = 0; i < row_size; ++i) {
j = 0;
while (board[i][j] != '\0')
++j;
while (j < col_size-1)
board[i][++j] = ' ';
}
for (i = 0; i < row_size; ++i) {
puts(board[i]);
}
for (i = 0; i < row_size; ++i)
free(board[i]);
free(board);
return 0;
}

Related

Segfault error when mallocing an array of structs

after a long time spent trying to debug this I've come for your help.
Basically in this exercise I'm trying to read the string "31|Name1;23|Name2;15|Name3" and store it in an array of struct s_perso where the | are marking the end of an age and the beginning of a name, and where the ; are marking the beginning of a new struct.
Here's the given ft_perso.h :
#include <string.h>
#ifndef FT__PERSO__H
#define FT__PERSO__H
typedef struct s_perso
{
char *name;
float life;
int age;
char *profession;
}
t_perso;
#endif
We will only use the datas age and name from this struct s_perso.
Here's my code :
#include "ft_perso.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int numberofstructs(char *str)
{
int i;
int length;
i = 0;
length = 0;
if (str[0])
length = 0;
else
{
while (str[i])
{
if (str[i] == ';')
length += 1;
i++;
}
}
return (length);
}
int get_data_length(char *str, int i)
{
int length;
length = 0;
while (str[i] != '|' && str[i] != ';' && str[i] != '\0')
{
length++;
i++;
}
return (length);
}
char *get_data(char *str, int i)
{
int j;
char *str2;
j = 0;
str2 = (char *)malloc(sizeof(char) * get_data_length(str, i) + 1);
while (str[i] != '|' && str[i] != ';' && str[i] != '\0')
{
str2[j] = str[i];
i++;
j++;
}
str2[j] = '\0';
return (str2);
}
t_perso **ft_decrypt(char *str)
{
int i;
int j;
t_perso **textttt_perso;
i = 0;
j = 0;
textttt_perso = (t_perso **)malloc(sizeof(t_perso **));
*textttt_perso = (t_perso *)malloc(sizeof(t_perso *) * numberofstructs(str));
while (j <= strlen(str) && str[j])
{
if (str[j] == ';')
{
i++;
j++;
}
textttt_perso[i]->age = atoi(get_data(str, j));
j = j + get_data_length(str, j) + 1;
textttt_perso[i]->name = get_data(str, j);
j = j + get_data_length(str, j);
}
textttt_perso[i+1] = 0;
return (textttt_perso);
}
int main(void)
{
int i;
t_perso **tab;
i = 0;
char str[29] = "31|Name1;23|Name2;15|Name3";
tab = ft_decrypt(str);
while(i <= numberofstructs(str))
{
printf("age = %d\n", tab[i]->age);
printf("age = %s\n", tab[i]->.name);
i++;
}
}
From my debugging, I get the segfault error on the second call (when i = 1 and we are working on the substring 23) instruction of t_perso **ft_decrypt(char *str) :
textttt_perso[i]->age = atoi(get_data(str, j));
My guess is that my allocation of memory either for the array of struct in itself or the number of arrays it can contain is wrong. I can't point my finger on the problem tho...
Thanks in advance for your help, have a nice day !
You never allocate space for an actual structure. In your example:
textttt_perso = (t_perso **)malloc(sizeof(t_perso **));
allocates space for one pointer and:
*textttt_perso = (t_perso *)malloc(sizeof(t_perso *) * numberofstructs(str));
allocates enough space for 3 pointers. At some point you need to allocate space for the actual structures.
You also have other issues. In numberofstructs() you have if(str[0]) that will cause length to always be zero. Also in numberofstructs(), you count the semi-colons. If there is data after the last sem-colon you would need to add 1 to length.
You have many other issues in this code that will show up if the data isn't perfect but here is an implementation of ft_decrypt that should work. Initial malloc should be to hold the array of pointers. Then the loop should allocate a structure for each array entry.
t_perso** ft_decrypt(char* str)
{
int i = 0;
int j = 0;
t_perso** textttt_perso;
textttt_perso = malloc(sizeof(*textttt_perso) * numberofstructs(str));
while (j <= strlen(str) && str[j])
{
if (str[j] == ';')
{
i++;
j++;
}
textttt_perso[i] = malloc(sizeof(*textttt_perso[i]));
textttt_perso[i]->age = atoi(get_data(str, j));
j = j + get_data_length(str, j) + 1;
textttt_perso[i]->name = get_data(str, j);
j = j + get_data_length(str, j);
}
return (textttt_perso);
}

Debugging csv read in c

I'm having some trouble with a project I'm working on for my master thesis. I got some help from a user here a while back, but the code doesn't quite seem to be working. My application crashes and saves a dump-file, but my debugging skills are quite limited so I'm not sure what is causing the crash. The code that I have that I believe is causing the crash looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdbool.h>
#include "flames.h"
#include "fmodel.h"
typedef struct row_tag
{
int index;
double *data;
} row_t;
size_t get_col_count(FILE *is)
{
size_t col_count = 1;
int ch;
while ((ch = fgetc(is)) != EOF && ch != '\n')
if (ch == ',')
++col_count;
rewind(is);
return col_count;
}
row_t* csv_read(FILE *is, size_t *cols, size_t *rows)
{
*cols = get_col_count(is);
*rows = 0;
char const *origin_format = "%*[^ ,]%c";
char const *row_header_format = "%d%c";
char const *format = "%lf%c";
row_t *csv = NULL;
bool valid = true;
for (size_t current_row = 0; valid; ++current_row) {
csv = (row_t*)realloc(csv, (current_row + 1)* sizeof(row_t));
csv[current_row].data = (double*)calloc((size_t)cols - 1, sizeof(double));
for (size_t current_col = 0; valid && current_col < (size_t)cols; ++current_col) {
char delim;
if (!current_col && !current_row) {
if (fscanf(is, origin_format, &delim) != 1 || delim != ',') {
valid = false;
//continue;
}
csv[0].index = -1;
}
else if (!current_col) {
int result = -1;
if ((result = fscanf(is, row_header_format, &csv[current_row].index, &delim)) != 2 || delim != ',') {
valid = false;
continue;
}
}
else {
if (fscanf(is, format, &csv[current_row].data[current_col - 1], &delim) != 2 || delim != ',' && delim != '\n')
valid = false;
}
}
if (!valid)
free(csv[current_row].data);
else *rows = current_row + 1;
}
return csv;
}
void csv_free(row_t *csv, size_t rows)
{
for (size_t row = 0; row < rows; ++row)
free(csv[row].data);
free(csv);
}
double csv_get_value(row_t *csv, int col_index, size_t cols, int row_index, size_t rows)
{
size_t col;
for (col = 1; csv[0].data[col] != col_index && col < cols; ++col);
if (col >= cols || csv[0].data[col] != col_index)
return 0.;
size_t row;
for (row = 1; csv[row].index != row_index && row < rows; ++row);
if (row >= rows || csv[row].index != row_index)
return 0.;
return csv[row].data[col];
}
And then in my main function: (note that FMHPrint is just a printing function in the application I'm working in called FLAMES, therefore the includes aswell)
char const *filename = "Dampening.csv";
FILE *is = fopen(filename, "r");
if (!is) {
FMHPrint(0, 0, "Couldnt open \%s\ for reading!\n\n", filename);
return (FFAILURE);
}
size_t cols;
size_t rows;
row_t *csv = csv_read(is, &cols, &rows);
FMHPrint(0, 0, "Cols: %zu\n", cols);
FMHPrint(0, 0, "Rows: %zu\n", rows);
fclose(is);
for (size_t y = 0; y < rows; ++y) {
printf("%2d: ", csv[y].index);
for (size_t x = 0; x < cols - 1; ++x)
printf("%f ", csv[y].data[x]);
putchar('\n');
}
double value = csv_get_value(csv, 550, cols, 7, rows);
FMHPrint(0, 0, "Dampening value is: %f", value);
csv_free(csv, rows);
I have no idea why FLAMES keeps crashing, and the DMP-file isn't very helpful for me. Can anyone explain what is wrong? :)
Regards, Anders
You pass cols and rows as pointer to the reading function, so that you can update the variables in the calling function. When you allocate memory for each row:
csv[current_row].data = (double*)calloc((size_t)cols - 1, sizeof(double));
you use cols, which is a pointer. You probably got a compiler warning, so you decided to cast the value to size_t, but that doesn't solve the problem, it only makes the warning go away.
The value you want is where the pointer points to, so you must dereference it:
csv[current_row].data = calloc(*cols - 1, sizeof(double));

Reversing the order of words backwards in a string

Sorry for such a mediocre question, but I ran into what seems to be a tiny problem, but simply can't get over it. For my task I have to take a line of string from a file, and put it into another file backwards, for example:
one two three
four five six
would be
three two one
six five four
My problem is, is that I'm getting
three two one
si five four
So basically the flaw is that there is a space character at the beginning of each line and the last letter of the last word is always missing. Here's my reverse function:
void reverse(char input[], int length, char output[]) {
char space = 32;
input[length - 1] = space;
int value = 0;
int i, k = 0, j;
for (i = 0; i <= length; i++) {
if (input[i] == space) {
for (j = i - 1; j >= k; j--, value++) {
output[value] = input[j];
}
if (j == -1) {
output[value] = space;
value++;
}
k = i;
}
}
char c = 0;
for (int i = 0, j = length - 1; i <= j; i++, j--) {
c = output[i];
output[i] = output[j];
output[j] = c;
}
}
What I'm doing is first reversing each word by character, and then the whole line. If someone could help me find the last bits that I've missed I would greatly appreciate it.
The flaws come from your approach:
why do you force a space at offset length - 1? If you read the line with fgets(), there is probably a newline ('\n') at the end of the line, but it might be missing at the end of the input, which would explain the x getting overwritten on the last line.
you should not modify the input buffer.
Here is a simplified version, along with a simple main function:
#include <stdio.h>
#include <string.h>
void reverse(const char *input, int length, char *output) {
int i, j, k, v;
for (i = k = v = 0;; i++) {
if (i == length || input[i] == ' ') {
for (j = i; j-- > k; v++) {
output[v] = input[j];
}
for (; i < length && input[i] == ' '; i++) {
output[v++] = ' ';
}
if (i == length) {
output[v] = '\0';
break;
}
k = i;
}
}
for (i = 0, j = length - 1; i < j; i++, j--) {
char c = output[i];
output[i] = output[j];
output[j] = c;
}
}
int main() {
char input[256];
char output[256];
while (fgets(input, sizeof input, stdin)) {
reverse(input, strcspn(input, "\n"), output);
puts(output);
}
return 0;
}
Output:
three two one
six five four
Here is a simpler reverse function that operates in one pass:
#include <string.h>
void reverse(const char *input, int length, char *output) {
int i, j, k, v;
for (i = k = 0, v = length;; i++) {
if (i == length || input[i] == ' ') {
for (j = i; j-- > k;) {
output[--v] = input[j];
for (; i < length && input[i] == ' '; i++) {
output[--v] = ' ';
}
if (v == 0) {
output[length] = '\0';
break;
}
k = i;
}
}
}
Replace input[length - 1] = space; with input[length] = space;

Detecting EOF of a txt File in C

I wrote this code which reads every char of my text and puts it into my char array. My Problem is that the end of the file is not detected and so the fscanf() returns after the end of the text every time the last char until my array is filled. How can I prevent that? I am programming in C.
My Code:
int main() {
char array[50][50];
char buff;
FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r");
for (int i = 0; i < 50; i++) {
for (int k = 0; k < 50; k++) {
fscanf(cola, "%c", &buff);
array[i][k] = buff;
}
}
fclose(cola);
for (int i = 0; i < 50; i++) {
for (int k = 0; k < 50; k++) {
printf("%c", array[i][k]);
}
}
return 0;
}
Thank you for your help.
fscanf() returns the number of successful conversions. You should test the return value and also handle newline characters specifically:
#include <stdio.h>
int main(void) {
char array[50][50];
char buff;
FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r");
if (cola == NULL) {
return 1;
}
for (int i = 0; i < 50; i++) {
for (int k = 0; k < 50; k++) {
if (fscanf(cola, "%c", &buff) != 1 || buff == '\n') {
array[i][k] = '\0';
break;
}
array[i][k] = buff;
}
}
fclose(cola);
for (int i = 0; i < 50; i++) {
for (int k = 0; k < 50 && array[i][k] != '\0'; k++) {
printf("%c", array[i][k]);
}
printf("\n");
}
return 0;
}
The code can be simplified if you use getc() instead of fscanf() to read bytes from the file:
#include <stdio.h>
int main(void) {
char array[50][51];
int c, i, k, n;
FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r");
if (cola == NULL) {
return 1;
}
for (n = 0; n < 50; n++) {
for (k = 0; k < 50; k++) {
if ((c = getc(cola)) == EOF || c == '\n') {
break;
}
array[n][k] = c;
}
array[n][k] = '\0';
if (c == EOF && k == 0)
break;
}
fclose(cola);
for (i = 0; i < n; i++) {
puts(array[i]);
}
return 0;
}
Replace:
for (int i = 0; i < 50; i++) {
for (int k = 0; k < 50; k++) {
fscanf(cola, "%c", &buff);
array[i][k] = buff;
}
}
with:
for (int i = 0; i < 50; i++) {
for (int k = 0; k < 50; k++) {
int c = getc(cola);
if (c == EOF)
break;
array[i][k] = c;
}
}
Since buff is then unused, don't define it. Note that the return type of getc() is an int, not just a char. Always check the I/O function for success/failure. In your original code, you don't even check whether the I/O operation succeeds, which makes detecting EOF impossible.
Note that this code makes a number of assumptions that may or may not be justifiable. For example, you assume each line in the file consists of 49 characters plus a newline; you also assume you'll never need to print the information as a 'string' (your existing code does not; it prints character by character, so it is 'safe').
You might want to describe the input as:
Read up to 50 lines with up to 49 characters plus a newline in each line, storing the result in the variable array with each line being a null-terminated string.
This is more resilient to common problems (short lines, long lines, not enough lines). The code for that might be:
enum { LINE_LEN = 50, NUM_LINES = 50 };
char array[NUM_LINES][LINE_LEN];
int i;
for (i = 0; i < LINE_LEN; i++)
{
int c;
int k;
for (k = 0; k < LINE_LEN; k++)
{
c = getc(cola);
if (c == EOF || c == '\n')
break;
if (k == LINE_LEN - 1)
{
/* Too long - gobble excess */
while ((c = getc(cola)) != EOF && c != '\n')
;
break;
}
array[i][k] = c;
}
array[i][k] = '\0';
if (c == EOF)
break;
}
int num_lines = i; // You have num_lines lines of data in your array
I found one version of the Coca Cola™ ASCII art image at https://www.ascii-code.com/ascii-art/logos/coca-cola.php which looks similar to what you have in your images, but there are many other sources and variants:
__ ___ __ .ama ,
,d888a ,d88888888888ba. ,88"I) d
a88']8i a88".8"8) `"8888:88 " _a8'
.d8P' PP .d8P'.8 d) "8:88:baad8P'
,d8P' ,ama, .aa, .ama.g ,mmm d8P' 8 .8' 88):888P'
,d88' d8[ "8..a8"88 ,8I"88[ I88' d88 ]IaI" d8[
a88' dP "bm8mP8'(8'.8I 8[ d88' `" .88
,88I ]8' .d'.8 88' ,8' I[ ,88P ,ama ,ama, d8[ .ama.g
[88' I8, .d' ]8, ,88B ,d8 aI (88',88"8) d8[ "8. 88 ,8I"88[
]88 `888P' `8888" "88P"8m" I88 88[ 8[ dP "bm8m88[.8I 8[
]88, _,,aaaaaa,_ I88 8" 8 ]P' .d' 88 88' ,8' I[
`888a,. ,aadd88888888888bma. )88, ,]I I8, .d' )88a8B ,d8 aI
"888888PP"' `8""""""8 "888PP' `888P' `88P"88P"8m"
This file's longest line is the first at 67 characters plus newline; the shortest is 61 characters plus newline. The file only has 13 lines and 845 characters (LF line endings) in total. Thus, your program is ill-equipped to deal with this particular data file. It looks for 2,500 characters, and won't get them.
My complete test code was rigged to read from standard input, rather than a fixed file name.
#include <stdio.h>
int main(void)
{
FILE *cola = stdin;
enum { LINE_LEN = 80, NUM_LINES = 50 };
char array[NUM_LINES][LINE_LEN];
int i; // Need value of i after loop
for (i = 0; i < NUM_LINES; i++)
{
int c; // Need value of c after loop
int k;
for (k = 0; k < LINE_LEN; k++)
{
c = getc(cola);
if (c == EOF || c == '\n')
break;
if (k == LINE_LEN - 1)
{
/* Too long - gobble excess */
while ((c = getc(cola)) != EOF && c != '\n')
;
break;
}
array[i][k] = c;
}
array[i][k] = '\0';
if (c == EOF)
break;
}
int num_lines = i; // You have num_lines lines of data in your array
for (i = 0; i < num_lines; i++)
puts(array[i]);
return 0;
}
I tested it on the data file shown, with an empty line at the end, and with a couple of lines containing more than 79 characters after the blank line. It handled all those special cases correctly. Note that handling user input is hard; handling perverse user input is harder. The code is less compact. You could change the rules and then change the code to match. I'm not sure this is the most minimal way to code this; it does work, however. It might be better to have a function to handle the inner input loop; the outer loop could test the return value from that function. This would cut down on the special case handling.
#include <assert.h>
#include <limits.h>
#include <stdio.h>
static int read_line(FILE *fp, size_t buflen, char *buffer)
{
assert(buflen < INT_MAX);
int c; // Need value of c after loop
size_t k; // Need value of k after loop
for (k = 0; k < buflen; k++)
{
if ((c = getc(fp)) == EOF || c == '\n')
break;
if (k == buflen - 1)
{
/* Too long - gobble excess */
while ((c = getc(fp)) != EOF && c != '\n')
;
break;
}
buffer[k] = c;
}
buffer[k] = '\0';
return (k == 0 && c == EOF) ? EOF : (int)k;
}
int main(void)
{
enum { LINE_LEN = 80, NUM_LINES = 50 };
char array[NUM_LINES][LINE_LEN];
int i;
for (i = 0; i < NUM_LINES; i++)
{
if (read_line(stdin, LINE_LEN, array[i]) == EOF)
break;
}
int num_lines = i;
for (i = 0; i < num_lines; i++)
puts(array[i]);
return 0;
}
This produces the same output from the same input as the previous version.
int main() {
//char array[50][50];
char buff;
int t;
FILE *cola = fopen("C:/Users/danie/Desktop/cola.txt", "r");
if (cola == NULL)
{
printf("Cannot open file \n");
exit(0);
}
while (1) {
t = fgetc(cola);
if (t == EOF)
break;
buff = t;
printf("%c", buff);
}
fclose(cola);
return 0;
}

Edit Distance Matrix

I'm trying to build a program that takes two strings and fills in the edit distance matrix for them. The thing that is tripping me up is, for the second string input, it is skipping over the second input. I've tried clearing the buffer with getch(), but it didn't work. I've also tried switching over to scanf(), but that resulted in some crashes as well. Help please!
Code:
#include <stdio.h>
#include <stdlib.h>
int min(int a, int b, int c){
if(a > b && a > c)
return a;
else if(b > a && b > c)
return b;
else
return c;
}
int main(){
// allocate size for strings
int i, j;
char *input1 = (char*)malloc(sizeof(char)*100);
char *input2 = (char*)malloc(sizeof(char)*100);
// ask for input
printf("Enter the first string: ");
fgets(input1, sizeof(input1), stdin);
printf("\nEnter the second string: ");
fgets(input2, sizeof(input2), stdin);
// make matrix
int len1 = sizeof(input1), len2 = sizeof(input2);
int c[len1 + 1][len2 + 1];
// set up input 2 length
for(i = 0; i < len2 + 1; i++){
c[0][i] = i;
}
// set up input 1 length
for(i = 0; i < len1 + 1; i++){
c[i][0] = i;
}
// fill in the rest of the matrix
for(i = 1; i < len1; i++){
for(j = 1; j < len2; j++){
if(input1[i] == input2[j]) // if the first letters are equal make the diagonal equal to the last
c[i][j] = c[i - 1][j - 1];
else
c[i][j] = 1 + min(c[i - 1][j - 1], c[i - 1][j], c[i][j - 1]);
}
}
// print the matrix
printf("\n");
for(j = 0; j < len2; j++){
for(i = 0; i < len1; i++){
printf("| %d", c[i][j]);
}
printf("\n");
}
return 1;
}
Stick with fgets.
As others have pointed out, use char input1[100] instead of char *input1 = malloc(...)
But, even with that change, which makes the sizeof inside of the fgets correct, using sizeof when setting up len1 and len2 is wrong. You'll be processing an entire buffer of 100, even if their are only 10 valid characters in it (i.e. the remaining ones are undefined/random).
What you [probably] want is strlen [and a newline strip] to get the actual useful lengths.
Here's the modified code [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
min(int a, int b, int c)
{
if (a > b && a > c)
return a;
if (b > a && b > c)
return b;
return c;
}
int
main(void)
{
// allocate size for strings
int i;
int j;
char input1[100];
char input2[100];
// ask for input
printf("Enter the first string: ");
fgets(input1, sizeof(input1), stdin);
int len1 = strlen(input1);
if (input1[len1 - 1] == '\n') {
input1[len1 - 1] = 0;
--len1;
}
printf("\nEnter the second string: ");
fgets(input2, sizeof(input2), stdin);
int len2 = strlen(input2);
if (input2[len2 - 1] == '\n') {
input2[len2 - 1] = 0;
--len2;
}
// make matrix
int c[len1 + 1][len2 + 1];
// set up input 2 length
for (i = 0; i < len2 + 1; i++) {
c[0][i] = i;
}
// set up input 1 length
for (i = 0; i < len1 + 1; i++) {
c[i][0] = i;
}
// fill in the rest of the matrix
for (i = 1; i < len1; i++) {
for (j = 1; j < len2; j++) {
// if the 1st letters are equal make the diagonal equal to the last
if (input1[i] == input2[j])
c[i][j] = c[i - 1][j - 1];
else
c[i][j] = 1 + min(c[i - 1][j - 1], c[i - 1][j], c[i][j - 1]);
}
}
// print the matrix
printf("\n");
for (j = 0; j < len2; j++) {
for (i = 0; i < len1; i++) {
printf("| %d", c[i][j]);
}
printf("\n");
}
return 1;
}
UPDATE:
Okay sweet I see what you mean! The reason I was trying to use malloc though was to avoid making the matrix that I had to print a size of 100x100 blank spaces.
With either the fixed size input1 or the malloced one, fgets will only fill it to the input size entered [clipped to the second argument, if necessary]. But, it does not pad/fill the remainder of the buffer with anything (e.g. spaces on the right). What it does do is add an EOS [end-of-string] character [which is a binary 0x00] after the last char read from input [which is usually the newline].
Thus, if the input string is: abcdef\n, the length [obtainable from strlen] is 7, input[7] will be 0x00, and input1[8] through input1[99] will have undefined/random/unpredictable values and not spaces.
Since a newline char isn't terribly useful, it is often stripped out before further processing. For example, it isn't terribly relevant when computing edit distance for a small phrase.
Does using strlen() only count the number of chars inside the array, or does it include all the blank spaces too?
As I mentioned above, fgets does not pad the string at the end, so, not to worry. It will do what you want/expect.
strlen only counts chars up to [but not including the EOS terminator character (i.e.) zero]. If some of these chars happen to be spaces, they will be counted by strlen--which is what we want.
Consider computing the edit distance between any two of the following phrases:
quick brown fox jumped over the lazy dogs
the quick brown fox jumped over lazy dogs
quick brown fox jumps over the lazy dog
In each case, we want strlen to include the [internal/embedded] spaces in the length calculation. That's because it is perfectly valid to compute the edit distance of phrases.
There is a valid usage for malloc: when the amount of data is too big to fit on the stack. Most systems have a default limit (e.g. under linux, it's 8 MB).
Suppose we were computing the edit distance for two book chapters [read from files], we'd have (e.g.):
char input1[50000];
char input2[50000];
The above would fit, but the c matrix would cause a stack overflow:
int c[50000][50000];
because the size of this would be 50000 * 50000 * 4 which is approx 9.3 GB.
So, to fit all this data, we'd need to allocate it on the heap. While it is possible to do a malloc for c and maintain the 2D matrix access, we'd have to create a function and pass off the pointer to c to it.
So, here's a modified version that takes input of arbitrarily large sizes:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define sysfault(_fmt...) \
do { \
fprintf(stderr,_fmt); \
exit(1); \
} while (0)
#define C(y,x) c[((y) * (len2 + 1)) + (x)]
long
min(long a, long b, long c)
{
if (a > b && a > c)
return a;
if (b > a && b > c)
return b;
return c;
}
char *
input(const char *prompt,long *lenp,const char *file)
{
FILE *fp;
char *lhs;
int chr;
long siz;
long len;
if (file != NULL)
fp = fopen(file,"r");
else {
fp = stdin;
printf("Enter %s string: ",prompt);
fflush(stdout);
}
lhs = NULL;
siz = 0;
len = 0;
while (1) {
chr = fgetc(fp);
if (chr == EOF)
break;
if ((chr == '\n') && (file == NULL))
break;
// grow the character array
if ((len + 1) >= siz) {
siz += 100;
lhs = realloc(lhs,siz);
if (lhs == NULL)
sysfault("input: realloc failure -- %s\n",strerror(errno));
}
lhs[len] = chr;
len += 1;
}
if (file != NULL)
fclose(fp);
if (lhs == NULL)
sysfault("input: premature EOF\n");
// add the EOS
lhs[len] = 0;
// return the length to the caller
*lenp = len;
return lhs;
}
int
main(int argc,char **argv)
{
long i;
long j;
char *input1;
long len1;
char *input2;
long len2;
long *c;
--argc;
++argv;
switch (argc) {
case 2:
input1 = input("first",&len1,argv[0]);
input2 = input("second",&len2,argv[1]);
break;
default:
input1 = input("first",&len1,NULL);
input2 = input("second",&len2,NULL);
break;
}
// make matrix
c = malloc(sizeof(*c) * (len1 + 1) * (len2 + 1));
if (c == NULL)
sysfault("main: malloc failure -- %s\n",strerror(errno));
// set up input 2 length
for (i = 0; i < len2 + 1; i++) {
C(0,i) = i;
}
// set up input 1 length
for (i = 0; i < len1 + 1; i++) {
C(i,0) = i;
}
// fill in the rest of the matrix
for (i = 1; i < len1; i++) {
for (j = 1; j < len2; j++) {
// if the 1st letters are equal make the diagonal equal to the last
if (input1[i] == input2[j])
C(i,j) = C(i - 1,j - 1);
else
C(i,j) = 1 + min(C(i - 1,j - 1), C(i - 1,j), C(i,j - 1));
}
}
// print the matrix
printf("\n");
for (j = 0; j < len2; j++) {
for (i = 0; i < len1; i++) {
printf("| %ld", C(i,j));
}
printf("\n");
}
return 1;
}

Resources