This is an excerpt from a Conway's Game of Life-program that I'm writing. In this part I'm trying to get the program to read a file that specifies what cells are to be populated at the start of the game (i.e. the seed).
I get a weird bug. In the read_line function, the program crashes online[i++] = ch statement. When I debug the program, I see that the line-pointer is NULL when it crashes. Fair enough, I think, I should initialize line. But here is the (for me) strange part:
The read_line function has already successfully execute twice and got me the first two lines (4\n and 3 6\n) from the seed file. And when I look at the execution in the debugger, I see that line is indeed holding a value in those first two executions of read_line. How is this possible? How can line be initialized without me initializing it and then suddenly not be initialized anymore?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define MAX_COORDINATE_SIZE 50
#define MAX_FILENAME_SIZE 20
#define MAX_GENERATIONS 10
#define MAX_REPETITION_PERIOD 4
struct coord{ //Holds coordinates to a cell
int x;
int y;
};
struct cell{
int pop; //Populated
int age;
};
struct coord *read_init(FILE *fp, int *i);
static int read_line(FILE *fp, char *line, int max_length);
struct coord read_coords(char *line);
struct cell **create_board(int x, int y);
struct cell **start_game(FILE *fp, int nrows, int ncols);
struct cell new_cell(int x, int y, int pop, int age);
void print_board(struct cell **board, int nrows, int ncols);
void populate_board(struct coord *coords, struct cell ***board, int *n);
int main(int argc, const char * argv[]) {
int gens;
char gens_string[MAX_GENERATIONS];
if(argc != 3){
fprintf(stderr, "Usage: %s <seed-file> <generations>\n<seed-file> can me up to %d characters long\n", argv[0], MAX_FILENAME_SIZE);
exit(1);
}
FILE *fp = fopen(argv[1], "r");
strncat(gens_string, argv[2], MAX_GENERATIONS);
gens = atoi(gens_string);
int nrows = 10;
int ncols = 10;
struct cell **board= start_game(fp, nrows, ncols);
print_board(board, nrows, ncols);
return 0;
}
struct coord *read_init(FILE *fp, int *n){ //Takes in filename and returns list of coordinates to be populated
char raw_n[100];
struct coord *coords;
char *line;
read_line(fp, raw_n, 100); // get the first line of the file (number of popuated cells)
*n = atoi(raw_n);//make an int out of raw_n
coords = malloc(sizeof(struct coord)*(*n)); //Allocate memory for each coord
for(int i = 0; i<(*n); i++){ // for each line in the file (each populated cell)
read_line(fp, line, MAX_COORDINATE_SIZE);
coords[i] = read_coords(line); //Put coordinates in coords
line = '\0';
}
return coords; // return coordinates
}
static int read_line ( FILE *fp, char *line, int max_length)
{
int i;
char ch;
/* initialize index to string character */
i = 0;
/* read to end of line, filling in characters in string up to its
maximum length, and ignoring the rest, if any */
for(;;)
{
/* read next character */
ch = fgetc(fp);
/* check for end of file error */
if ( ch == EOF )
return -1;
/* check for end of line */
if ( ch == '\n' )
{
/* terminate string and return */
line[i] = '\0';
return 0;
}
/* fill character in string if it is not already full*/
if ( i < max_length )
line[i++] = ch;
}
/* the program should never reach here */
return -1;
}
struct coord read_coords(char *line){ // Returns coordinates read from char *line
struct coord c;
char *x;
char *y;
x = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
y = malloc(sizeof(char)*MAX_COORDINATE_SIZE);
int i = 0;
do{
x[i] = line[i]; //Get the x coordinate
i++;
}while(line[i] != ' ');
i++;
do{
y[i-2] = line[i];
i++;
}while(line[i] != '\0');
c.x = atoi(x)-1;
c.y = atoi(y)-1;
return c;
}
void init_board(int nrows, int ncols, struct cell ***board){
*board = malloc(nrows * sizeof(*board) + nrows * ncols * sizeof(**board));
//Now set the address of each row or whatever stackoverflow says
struct cell * const firstrow = *board + nrows;
for(int i = 0; i < nrows; i++)
{
(*board)[i] = firstrow + i * ncols;
}
for(int i = 0; i < nrows; i++){ //fill the entire board with pieces
for(int j = 0; j < ncols; j++){
(*board)[i][j] = new_cell(i, j, 0, 0);
}
}
}
void print_board(struct cell **board, int nrows, int ncols){
printf("--------------------\n");
for(int i = 0; i<nrows; i++){
for(int j = 0; j<ncols; j++){
if(board[i][j].pop == 1){
printf("%d ", board[i][j].age);
}else if(board[i][j].pop == 0){
printf(" ");
}else{
printf("\n\nERROR!");
exit(0);
}
}
printf("\n");
}
printf("--------------------");
printf("\n");
}
struct cell **start_game(FILE *fp, int nrows, int ncols){ //x,y are no of rows/columns, fn is filename
int n; // n is the number of populated cells specified in the seed
struct coord *coords = read_init(fp, &n); // get the list of coords to populate board with
struct cell **board;
init_board(nrows, ncols, &board); // Set up the board
populate_board(coords, &board, &n); //populate the cells specified in the seed
return board;
}
void populate_board(struct coord *coords, struct cell ***board, int *n){
for(int i = 0; i < *n; i++){
(*board)[coords[i].x][coords[i].y].pop = 1; //populate the cell
}
}
struct cell new_cell(int x, int y, int pop, int age){ //Return new populated or non-populated cell with specified coordinates
struct cell c;
c.pop = pop;
c.age = age;
return c;
}
The seed file:
4
3 6
4 6
5 6
5 7
EDIT:
The error message: Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
I shall add that if I add a line line = malloc(sizeof(char)*MAX_COORDINATE_SIZE+1) after the declaration of line in read_init, I still get the same error.
In read_init() :
struct coord *read_init(FILE *fp, int *n){
//...
char *line;
//...
for(int i = 0; i<(*n); i++) {
read_line(fp, line, MAX_COORDINATE_SIZE);
coords[i] = read_coords(line); //Put coordinates in coords
line = '\0'; // <<--- you set line to NULL here.
*line = 0; // this is what you wanted to do, is not necessary...
}
// ....
}
I get a weird bug.
I suggest asking some questions about the compiler output. We should never blindly ignore warnings, after all. Speaking of reading things, I think you're spending too long reading StackOverflow and not long enough reading K&R2e and doing the exercises. We'll come back to that.
In the read_line function, the program crashes on line[i++] = ch statement ... But here is the (for me) strange part: ... The read_line function has already successfully execute twice and got me the first two lines (4\n and 3 6\n) from the seed file
The C and C++ standards rationalise the concept of "undefined behaviour" for this class of errors that are computationally difficult to diagnose. In other words, because you made an error, the behaviour of your program is undefined. It isn't required that your malfunctioning code crash every time, as that would be defining the undefined; instead they leave this "undefined" and the first two times your erroneously code works (whatever that means), purely by coincidence that the uninitialised variable points somewhere accessible. Later on you assign line = '\0';, which changes line to be a null pointer, and then you try to assign into whatever that null pointer points at. That's more undefined behaviour.
How is this possible? How can line be initialized without me initializing it and then suddenly not be initialized anymore?
line isn't initialised; you're using it uninitialised, which happens to coincidentally work (but needs fixing), then you assign it to be a null pointer and dereference a null pointer (more UB that needs fixing). Such is the nature of undefined behaviour. Such is the nature of learning C by guessing. You need a book!
I shall add that if I add a line line = malloc(sizeof(char)*MAX_COORDINATE_SIZE+1) after the declaration of line in read_init, I still get the same error.
You need to fix all of the errors, not just the one. For assistance you could see the warnings/errors your compiler emits. I see more uninitialised access here:
char gens_string[MAX_GENERATIONS]; // <--- where's the initialisation??
// Snip
strncat(gens_string, argv[2], MAX_GENERATIONS); // Boom
There's some really sus code around this comment: //Now set the address of each row or whatever stackoverflow says ... and on that note I want to point out that there are some subtly toxic know-it-alls who answer questions despite having as many misunderstandings as you, a humble person, and so you shouldn't hope to get the same quality of education from StackOverflow as you would from K&R2e... but apparently I'd be toxic for pointing out the egomaniacs and suggesting decent resources to learn from, so that's none of my business 🙄🤷‍♂️ let's just let the sociopaths sabotage the education of everyone huh?
(*board)[i] = firstrow + i * ncols;
Look, there is no guarantee that this even compiles let alone that the address on the right has a suitable alignment to store the type of value on the left. Misaligned access causes more undefined behaviour, which may also work coincidentally rather than logically. Just as you've never seen alignment violations before, so too has the person who suggested you use this code. Assuming the alignment requirements for your implementation are satisfied by this code, we then have the same questions to raise here:
(*board)[i][j] = new_cell(i, j, 0, 0);
Your whole program needs remodelling around the declaration of board changing from struct cell **board to struct cell (*board)[ncols];, for example. It'll become much simpler, and a whole class of bugs related to alignment requirements will disappear. To see the extent of the simplification, here's what your init_board ought to look like:
void init_board(int nrows, int ncols, struct cell (**board)[ncols]){
*board = malloc(nrows * sizeof(*board));
// NOTE: I snipped the erroneous StackOverflow logic around the comment mentioned above; you don't need that crud because of the more suitable choice of type
for(int i = 0; i < nrows; i++){ //fill the entire board with pieces
for(int j = 0; j < ncols; j++){
(*board)[i][j] = (struct cell){ 0, 0 };
}
}
}
Related
I have two identical arrays of struct , one in reverse order.
The problem is that i don't want duplicate the same data into the two arrays , i would a reversed array with elements pointing elements of the first array in a way that i can edit the members of struct of first array or from the reversed array taking effect in both.
you can view the source and run it online here https://onlinegdb.com/SJbepdWxS
#include <stdio.h>
typedef struct point{
int id;
float x,y,z;
} point;
void printPoints(point *pts,int len){
int i = 0;
while (pts !=NULL && i < len){
printf("id %d x %f y%f z %f\n",pts->id,pts->x,pts->y,pts->z);
pts++;
i++;
}
}
void translatePoints(point *pts,int len,float t){
int i = 0;
while (pts !=NULL && i < len){
pts->x = pts->x + t;
pts->y = pts->y + t;
pts->z = pts->z + t;
pts++;
i++;
}
}
void reversePoints(point *pts, int len, point *rev){
int i = 0;
int j = len;
while (i < len){
j=len-i-1;
rev[j]=pts[i];
i++;
}
}
int main()
{
int i;
int t1=200;
int t2=300;
int len=3;
point points[len];
point rev_points[len];
for(i=0; i<len ; i++){
points[i].id=i;
points[i].x=10+i;
points[i].y=20+i;
points[i].z=30+i;
}
//point * pts = points;
printf("\nprint points \n\n");
printPoints(points,len);
printf("\ntranslate points %d...\n\n",t1);
translatePoints(points,len,t1);
printf("\nprint points\n\n");
printf("\nreverse points to rev_points\n");
reversePoints(points,len,rev_points);
printf("\nprint rev_points \n\n");
printPoints(rev_points,len);
printf("\ntranslate rev_points %d...\n\n",t2);
translatePoints(rev_points,len,t2);
printf("\nprint rev_points\n\n");
printPoints(rev_points,len);
printf("\nprint points\n\n");
printPoints(points,len);
return 0;
}
I expect that struct values of both arrays change when i change value in one of the two array.
But changing values of struct in the first array , the second array not changes and the other way around.
One way to look at this is a set of points and two permutations on the set. This sets up a points array, which is used as a set, and forward_points and reverse_points as arrays of pointers to the point array that we are going to use as permutations.
#include <stdio.h>
struct Point {
int id;
float x,y,z;
};
/* Print a point. */
static void printPoint(struct Point *point) {
printf("id %d x %f y%f z %f\n",point->id,point->x,point->y,point->z);
}
/* These print out an array of pointers to point. */
static void printPointsRef(struct Point **ref, int len) {
struct Point **end = ref + len;
while(ref < end) printPoint(*(ref++));
}
/* This translates all the `pts` up to `len` by `(1,1,1)*t`. */
static void translatePoints(struct Point *pts, int len, float t) {
struct Point *end = pts + len;
while(pts < end) {
pts->x = pts->x + t;
pts->y = pts->y + t;
pts->z = pts->z + t;
pts++;
}
}
/* Helper function to `main`. */
static void printPoints(struct Point **forward_points,
struct Point **reverse_points, int len) {
printf("print points\nprint points forward:\n");
printPointsRef(forward_points,len);
printf("print points reverse:\n");
printPointsRef(reverse_points,len);
printf("\n");
}
int main(void)
{
const int len = 3;
/* This is the actual points structure. */
struct Point points[len];
/* These are arrays of pointers to points; they are
permutations of `points`. */
struct Point *forward_points[len], *reverse_points[len];
int i;
const int t1=200;
for(i=0; i<len; i++) {
/* Initialise element `i` of `points`. */
points[i].id=i;
points[i].x=10+i;
points[i].y=20+i;
points[i].z=30+i;
/* Initialise element `i` of `forward_points`
to point to `points[i]`, and `backward_points`
to point the other way (it doesn't matter that
the backwards points are uninitialised, they
will be.) */
forward_points[i] = &points[i];
reverse_points[i] = &points[len - 1 - i];
}
printPoints(forward_points, reverse_points, len);
/* Translation is a vector space operation and doesn't
care about order; we just do it on the original points. */
printf("translate points %d...\n\n",t1);
translatePoints(points,len,t1);
printPoints(forward_points, reverse_points, len);
return 0;
}
Of course, there is no integrity constraints on the pointers; nothing stopping one from pointing at anything, null, the same elements, or anything else.
I added an other struct with one element that is a pointer
typedef struct ptr_point{
point * p;
} ptr_point;
I edited the function reversePoints
void reversePoints(point *pts, int len, ptr_point *rev){
// This function is used only to test pointers
int i = 0;
int j = len;
while (i < len){
j=len-i-1;
rev[j].p = &pts[i];
i++;
}
}
and added another function to print ptr_points
void printPtrPoints(ptr_point *pts,int len){
int i = 0;
while (i < len){
printf("id %d x %f y%f z %f\n",pts->p->id,pts->p->x,pts->p->y,pts->p->z);
pts++;
i++;
}
}
and declaring the second array as ptr_point array
ptr_point rev_points[len];
In conclusion : now data in the second array are not replicated but pointing to element structure of the first array.
The need to not replicate data arise in presence of millions of coordinate points that if replicate more than one time , sorting it for example by x, y, z and so on , occupe much memory with the difficulty of managing .
This fix however forces me to use structures->type in order to change the access mode to read or set values.
I don't know if this is the best solution but it has solved the problem for not duplicate the data.
you can run the source with fixes here: https://onlinegdb.com/SknP_i-eS
Thank you all for the advice.
and thanks for your time. I apologize in advance I am new to C programming and posting on stack overflow. Any information I may have left out and questions you have please ask.
I have this lab I am working on for my class and I'm having trouble understanding how the dreaded pointers operate. First I will explain the lab instructions.
First, I am to create an array of 200 words with a max length of 30+1 for null.
Next, call functions I need to create which include:
A read function which reads words from a file into the array. I must use fopen and fscanf functions.
A function to convert a string to lowercase using the ASCII codes of each character. (Must use pointers)
A function to return the length of a string.(Can't use strlen function and must use pointers)
A function with three parameters(array of words, # of words in array, and an int length). Function returns the number words in the array that match the int length.
A print function to print all the words in the array.
The IDE I am using is Dev C++ its been wonky so I have also been using netbeans.
I have only attempted to create the read, print, and converting to lowercase functions. I first tried to read the file and print the array in main. The file I'm reading is created by me it contains a short sentence which follows exactly:
There ARE so MANY words in HERE
EDIT- Updated main code to current with working lowercase loop inside main.
#define rows 200 //How many words allowed in array.
#define cols 31 //How many characters allowed for each word.
void lowercase(char* words, int count);
int read(char (*words)[cols]);
void print(char (*words)[31], int count);
int main(int argc, char *argv[]){
char words[rows][cols];
int i, j;
int count = read(words);
print(words, count);
/*
//make words lowercase
for(i = 0;i<count;i++){
for(j = 0;j<cols;j++){
if(words[i][j]!=0){
if(words[i][j]<91 && words[i][0]>64)
words[i][j] = words[i][j]+32;
}
}
}*/
for(i = 0;i < count;i++){
lowercase(*words+i, count);
}
print(words, count);
return 0;
}
The code is poorly written and managed properly I'm just trying to get everything to work first then it will be more appropriate. The first printf output comes out how it should:
Array [0]: There
Array [1]: ARE
Array [2]: so
Array [3]: MANY
Array [4]: words
Array [5]: in
Array [6]: HERE
Then the print function I have prints out the words correctly in the array but it includes all 30 spaces for each word instead of just the word. This is how it is written I need to change it.
void print(void *array, int SIZE){
int i,
j;
char *charArray = (char *) array;
for( j = 0; j < SIZE; j++ ){
for( i = 0; i < SIZE; i ++){
printf( "%c ", charArray[j*SIZE + i] );
}
printf( "\n" );
}
}
The tolower function I created was partially working converting the first letter of each word to lowercase. Now it is broke and do not remember what I have changed.
EDIT- updated lowercase function. The lowercase in main works exactly but with this function it doesn't convert all the words to lowercase it stops at the third word the rest are the same.
void lowercase(char *words, int count){
int j;
for(j = 0;j<cols;j++){
if(words[j]!=0){
if(words[j]<91 && words[j]>64)
words[j] = words[j]+32;
}
}
}
I tried to move the read code in main to its own function also trying to mimic the print code with the pointers but when I run the program it stalls and the exe file stopped working window pops up with command prompt.
No errors or warnings in IDE.
int read(void *array){
FILE *file;
int i,
j;
char *words = (char *) array;
file = fopen("words.txt", "r");
//STORE IN ARRAY
for(i=0;i<7;i++)
fscanf(file,"%s", words[i]);
}
If you have not figured out I have no idea when or how to use pointers or addresses. I have been taught basically all of C in literally 12 hours which is in my opinion not enough time to learn the language at all especially understand it efficiently. Any help will be greatly appreciated. Thank You.
By casting a 2-dimensional array down to a char*, you have lost some information. If you read the words in correctly, then in memory, your array might look like this:
0 10 20 30
|.........|.........|.........|.
There
ARE
so
MANY
words
in
HERE
To access words[1] the compiler is automatically offsetting 31 bytes from the beginning of the array.
Your problem is that after you cast words to char*, then the compiler no longer knows about the 2D structure, and words[1] will now only offset 1 byte from the beginning of the array.
A simple solution is to redefine your read function:
int read(char words[][31])
{
FILE *file;
int i, j, count = 0;
file = fopen("words.txt", "r");
for (i=0; i<7; i++)
{
count += (1 == fscanf(file, "%s", words[i]));
}
return count;
}
Now the compiler knows that the memory stride size for words[i] is 31 char values.
Similar thing with print:
void print(char words[][31], int count)
{
int i;
for( i = 0; i < count; i ++)
{
printf( "%s\n", words[i] );
}
}
fix like this:
#include <stdio.h>
#include <stdlib.h>
//Stringification
#define S_(n) #n
#define S(n) S_(n)
//Information to be shared across the whole area
#define MAX_ROWS 200
#define MAX_WORD_LENGTH 30
#define COLS (MAX_WORD_LENGTH + 1)
#define DATA_FILE "words.txt"
int read(void *array);
void print(void *array, int rows);
int main(void){
char words[MAX_ROWS][COLS];
int rows;
rows = read(words);
print(words, rows);
return 0;
}
int read(void *array){
FILE *file = fopen(DATA_FILE, "r");
if(file == NULL){
perror("fopen:");
exit(EXIT_FAILURE);
}
char *words = array;
int rows;
for(rows = 0; rows < MAX_ROWS; ++rows, words += COLS){
if(fscanf(file, "%" S(MAX_WORD_LENGTH) "s", words) == EOF)
break;
}
fclose(file);
return rows;
}
void print(void *array, int rows){
char *words = array;
for(int r = 0; r < rows; ++r, words += COLS){
printf("Array [%d]: %s\n\n", r, words);
}
}
Am I mallocing my struct correctly? If so then why is a segfault occurring after the first element. I am quite stumped on the whole pointer and dereferencing with structs.
The problem:
2x2 matrix of struct a
file fp containing the lines
5 4 3 2 1 1
11 21 1 3 2 2
Relevant code:
struct
typedef struct num{
int s;
}num_t;
In main that uses a (note n and m are ints, where in my runtime were: n = 2, m = 3)
num_t **a;
a = malloc(sizeof(num_t *) * n);
for(i=0;i<n;i++)
a[i]= malloc(sizeof(num_t) * m);
//check if mallocs suceeded
if(a==NULL)
return -1;
//read matrix.dat, check if correct size
pb = matrix(n,m,&a,*(argv+3));
My function where segfault occurs(skip to the middle of inner loop):
int matrix(int n, int m, num_t ***a, char* file)
{
int i,j,k,count;
int d,e;
char z,w;
FILE *fp;
fp = fopen(file,"r");
//check if file opened
if(fp == NULL)
{
fclose(fp);
return -1;
}
else
{
i=0;
k=0;
count=0;
for(i=0;(k!=EOF)||i<n;i++)
{
printf("n=%d , m=%d\n",n,m);fflush(stdout);
for(j=0;j<m;j++)
{
//printf("Innit i=%d j=%d\n",i,j);fflush(stdout);
k=fscanf(fp,"%d",&d);
if(k!=1){
j++;break;
}
//printf("fscan1 passed\n");fflush(stdout);
k=fscanf(fp,"%d",&e);
if(k!=1){
j++;break;
}
printf("fscanf2 passed\n");fflush(stdout);//prints
a[i][j]->s = d; //segfaults here
printf("dpassed\n");fflush(stdout); //doesnt print
a[i][j]->t = e;
//printf("dpassed\n");fflush(stdout);
if(j==m-1){
count++;
//printf("enter break\n");fflush(stdout);
}
count++;
//printf("out j a[%d][%d] = %d and %d k=%d\n",i,j,a[i] [j]->s,a[i][j]->t,k);fflush(stdout);
}
//printf("enter out i=%d\n",i);fflush(stdout);
}
//printf("brokenout");fflush(stdout);
if((k = fscanf(fp,"%lf",&d)) >0)
count++;
fclose(fp);
//check matrix sizes
if((i!=n) || j!=m-1 || count!=(n * m))
return -1;
}
return 1;
}
EDIT:
Disregard w and z
At run time I had this:
n=1 , m=3
Innit i=0 j=0
fscan1 passed
fscanf2 passed
a[0][0] = 0 and 0 k=1 w='' z=''
dpassed
dpassed
out j a[0][0] = 5 and 4 k=1
Innit i=0 j=1
fscan1 passed
fscanf2 passed
[1] 13367 segmentation fault
EDIT2:
Sorry I had posted code from 2 different projects. num_t is the typedef struct, I had issues editing my code when I pasted because of how putty copied my files
EDIT3:
Format and the finished product
#include "header.h"//contains struct
int read_matrix(int row, int col, num_t** a, char* file)
{
int i,j,k,count,d,e;
FILE *fp;
fp = fopen(file,"r");
//check if file opened
if(fp == NULL)
{
fclose(fp);
return -1;
}
else
{
i=0;
k=0;
count=0;
for(i=0;(k!=EOF)||i<row;i++)
{
for(j=0;j<col;j++)
{
k=fscanf(fp,"%d%d",&d,&e);
if(k!=2){
break;
}
a[i][j].s = d;
a[i][j].t = e;
if(j==col-1){
count++;
}
count++;
}
}
fclose(fp);
//check matrix sizes
if((i!=row) || j!=col-1 || count!=(row * col))
return -1;
}
return 1;
}
In main:
pb = matrix(m,x,a,*(argv+3));
Conclusion
2D struct arrays are not like 2D int arrays. Don't pass the address of a struct inside this type of function.
instead of
a[i][j]->s = s
a code like this will work.
p = a[i];
p[j].s = s;
You should have:
malloc(sizeof(num_t));
The pointer symbol will only give you 4 bytes, hence the memory fault.
When you call matrix(), you pass the address of a. If you think of num_t ** as a Matrix_t type, then you are passing a pointer to Matrix_t.
num_t **a;
...
pb = matrix(n,m,&a,*(argv+3));
And inside matrix():
int matrix(int n, int m, num_t ***a, char* file)
{
...
a[i][j]->s = d; //segfaults here
Here, when i becomes 1, you are trying to access the next Matrix_t, but you only passed a pointer to a single Matrix_t. In order to make this work, you have to dereference the pointer before you index the elements. Eg.:
(*a)[i][j].s = d; //segfaults here
Or, better yet: Pass a instead of &a, and change your function definition to use a double pointer instead of a triple pointer. Note that you also need to change your ->'s to .'s. There may be other changes that are necessary; I didn't look too far into your code.
I have to make a program that gets as an input a and b, gets as input a number "a" of lines of the following form: "studentId studentName studentPhone" and b lines of input in the form "stId mark1 mark2 mark3". The program then outputs all the stid from the first input and if the same id exists in input b the program outputs the students marks besides its id.
I've gone through hell to getting the input properly and I think this is close but I get a strange behavior: after I input the marks in the second input it seems like some of the student ids in the first input are changed.
This is my code: (here i try to input just the student IDs. http://ideone.com/dBYzwe )
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
void chomp(char *s);
struct listA{
int stId;
char stName[19];
char stPhone[12];
};
struct listB{
int stId;
char marks[11];
};
int main(void) {
int i, j, m, n;
scanf("%d%d", &m, &n);
struct listA *a = malloc(sizeof(m * sizeof(struct listA)));
struct listB *b = malloc(sizeof(n * sizeof(struct listB)));
for(i = 0; i < m; i++)
{
scanf("%d", &a[i].stId);
fgets(a[i].stName, 19, stdin);
fgets(a[i].stPhone, 11, stdin);
chomp(a[i].stName);
chomp(a[i].stPhone);
}
for(i = 0; i < m; i++)
printf("%d ", a[i].stId);
for(i = 0; i < n; i++)
{
scanf("%d ", &b[i].stId);
fgets(b[i].marks, 12, stdin);
fflush(stdin);
}
printf("\n");
for(i = 0; i < n; i++)
{
printf("%d ", b[i].stId);
}
printf("\n");
for(i = 0; i < m; i++)
printf("%d ", a[i].stId);
return 0;
}
void chomp(char *s) {
while(*s && *s != '\n' && *s != '\r') s++;
*s = 0;
}
The problem is in the
struct listA *a = malloc(sizeof(m * sizeof(struct listA)));
struct listB *b = malloc(sizeof(n * sizeof(struct listB)));
The result of m * sizeof(struct listA) is an integer, so when you put that into sizeof you get the size of the integer, not the number you want. You should change this to:
struct listA *a = malloc(m * sizeof(struct listA));
struct listB *b = malloc(n * sizeof(struct listB));
The first problem is that your memory allocation is wrong (this may be the solution to your problem or may be not, but it's definetly something you must fix).
Malloc is taking as parameter number of bytes of memory to allocate and returns a pointer to the allocated memory, or null if it has failed.
As you do now, struct listA *a = malloc(sizeof(*a));, you allocate space for an object (you have declared a as a pointer to an object and you alloc size of a's object bytes). You need to allocate memory for an array of objects, which has n*sizeof(*a) bytes, keeping the way you wrote it. You should check if malloc returns null.
Also, be careful that you may exceed the stPhone/stName/marks size.
It's a bad practice to use fflush, unless you really need it and especially on input streams: http://www.gidnetwork.com/b-57.html
fgets(b[i].marks, 12, stdin);
Are you shure that a line with marks has at most 12 characters? I would recommend using another way of reading input, like described here: How to read from input until newline is found using scanf()?
You should allocate enough memory to a and b as
struct listA *a = malloc(sizeof(*a)* m);
struct listB *b = malloc(sizeof(*b) * n);
With your code you are allocating appropriately.
I'm trying to make a struct that generates a random matrix and am getting "error: expected â=â, â,â, â;â, âasmâ or â_attribute_â before âmatrixâ" when compiling. How can I get this to work effectively and efficiently?
I guess expected errors usually are caused by typos but I don't see any.
I'm very new to C so pointers and malloc are quite foreign to me. I really appreciate your help.
/* It's called RandomMatrixMaker.c */
#include <stdio.h>
#include <stdlib.h>
typdef struct {
char* name;
int MID;
int MRows;
int MCols;
long[][]* MSpace;
} matrix;
matrix makeRIDMatrix(char* name, int MID, int MRows, int MCols) {
matrix m;
static int i, j, r;
m.name = name;
m.MID = MID;
m.MRows = MRows;
m.MCols = MCols;
for (i=0; i<m.MRows; i++) {
for (j=0; i<m.MCols; j++) {
r = random(101);
*(m.MSpace[i][j]) = r;
}
}
return m;
}
int main(void) {
makeRIDMatrix("test", 1, 10, 10);
return 0;
}
There is indeed a typo. You misspelled typedef:
typdef struct {
should be:
typedef struct {
EDIT:
Also, there's no reason to use static here:
static int i, j, r;
You can just get rid of the static modifier.
int i, j, r;
As another poster mentioned, there's a typo, but even with that corrected, it wouldn't compile, due to the definition of matrix.MSpace.
Let's begin in makeRIDMatrix(). You've declared an automatic (stack) variable of type "matrix". At the end of the function, you return that object. Whilst this is permissible, it's not advisable. If the struct is large, you will be copying a lot of data unnecessarily. Better to pass a pointer to a matrix into makeRIDMatrix(), and have makeRIDMatrix() fill in the contents.
The test in the inner loop is against i, but should be against j.
Next, let's look at the definition of "matrix". The definition of "MSpace" is a mess, and wouldn't even compile. Even if it did, because you haven't defined the length of a row, the compiler would not be able to calcuate the offset to any given item in the array. You want a two-dimensional array without giving the row length, but you can't do that in C. You can in other languages, but not C.
There's a lot more I could point out, but I'd be missing the real point. The real point is this:
C Is Not Java.
(It's also not one of the interpreted languages such as JavaScript, PHP, Python, Ruby and so on.)
You don't get dynamically-expanding arrays; you don't get automatic allocation of memory; you don't get garbage collection of unreferenced memory.
What you need is something more like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct {
char* name;
int MID;
unsigned int MRows;
unsigned int MCols;
long *MSpace;
} matrix;
void makeRIDMatrix(matrix *pmx, char* name, int MID,
unsigned int MRows, unsigned int MCols) {
int i, j;
long *MSpace = malloc(sizeof(*MSpace)*MRows*MCols);
if (MSpace == NULL) {
return;
}
pmx->name = name;
pmx->MID = MID;
pmx->MRows = MRows;
pmx->MCols = MCols;
pmx->MSpace = MSpace;
srandom((unsigned int)time(NULL));
for (i=0; i<MRows; i++) {
for (j=0; i<MCols; j++) {
long int r = random() % 101L;
*(MSpace++) = r;
}
}
}
inline long * item_addr(const matrix *pmx,
unsigned int row, unsigned int col) {
if (pmx == NULL || pmx->MSpace == NULL
|| row >= pmx->MRows || col >= pmx->MCols) {
return NULL;
}
return &(pmx->MSpace[row * pmx->MCols + col]);
}
long get_item(const matrix *pmx, unsigned int row, unsigned int col) {
long *addr = item_addr(pmx, row, col);
return addr == NULL ? 0L : *addr;
}
void set_item(matrix *pmx,
unsigned int row, unsigned int col,
long val) {
long *addr = item_addr(pmx, row, col);
if (addr != NULL) {
*addr = val;
}
}
int main(void) {
matrix m;
makeRIDMatrix(&m, "test", 1, 10, 10);
return 0;
}
Note a few things here. Firstly, for efficiency, I fill the array as if it were one-dimensional. All subsequent get/set of array items should be done through the getter/setter functions, for safety.
Secondly, a hidden nasty: makeRIDMatrix() has used malloc() to allocate the memory - but it's going to be job of the calling function (or its successors) explciitly to free() the allocated pointer when it's finished with.
Thirdly, I've changed the rows/cols variables to unsigned int - there's little sense in definining an array with negative indices!
Fourthly: little error checking. For example, makeRIDMatrix() neither knows nor cares whether the parameter values are sensible (e.g. the matrix pointer isn't checked for NULLness). That's an exercise for the student.
Fifthly, I've fixed your random number usage - after a fashion. Another exercise for the student: why is the way I did it not good practice?
However - all of this is moot. You need to get yourself a good C textbook, or a good online course, and work through the examples. The code you've given here shows that you're punching above your weight at the moment, and you need to develop some more C muscles before going into that ring!
In relation to your question about "variable sized arrays", you could have something like:
/* can stick this into your struct, this is just an example */
size_t rows, cols;
long **matrix;
/* set the values of rows, cols */
/* create the "array" of rows (array of pointers to longs) */
matrix = (long**)malloc(rows * sizeof(long*));
/* create the array of columns (array of longs at each row) */
for (i = 0; i < rows; i++)
matrix[i] = (long*)malloc(cols * sizeof(long));
/* ... */
/* free the memory at the end */
for (i = 0; i < rows; i++)
free(matrix[i]);
free(matrix);
Then you can just access the dynamically allocated matrix similar to any other array of arrays.
ie. to set element at the first row (row 0) and fourth column (column 3) to 5:
matrix[0][3] = 5;