I have spent last few hours trying to debug my code but I failed to do so. I think the problem lies in me not fully understanding dynamic memory allocation, however I could've made some other mistakes aswell. The question here is a bit more of a personal problem and I'm sorry if someone finds this question not fulfilling "Make it relevant to others".
I've been given the next assigment:
Create an array A[] out of n random elements from interval 0-100. Create a function which splits two arrays such as: array B[] contains elements > 50 while C[] contains rest of the elements. Create arrays A and B using dynamic memory allocation. Arguments of the function have to be all three arrays and their respective lengths.
#include <stdio.h>
#include <stdlib.h>
void Array(int *A, int *nA, int *B, int *nB, int *C, int*nC){
int i;
int nB1 = 0;
int nC1 = 0;
int *tmpB;
int *tmpC;
B = malloc((nB1+1)*sizeof(int));
C = malloc((nC1+1)*sizeof(int));
printf("\n");
for(i = 0 ; i < nA ; i++){
if(A[i] <= 50){
C[i] = A[i];
nC1++;
// The idea here is to have a new array with basically
// no length so that each time one element passes to either
// B or A array that array gets increased at the same
// time as nC or nB
tmpC = realloc(C, sizeof(int) * nC1);
if(tmpC == NULL){
printf("ERROR: realloc failed.\n");
}
C = tmpC;
// C = realloc(C, nC1 + 1);
}
else{
B[i] = A[i];
nB1++;
tmpB = realloc(B, sizeof(int) * nB1);
if(tmpB == NULL){
printf("ERROR: realloc failed.\n");
}
B = tmpB;
// B = realloc(B, nB1 + 1);
}
}
printf("\n");
printf("Array B: ");
nB = nB1;
for(i = 0 ; i < nB ; i++){
printf("%d ", B[i]);
}
printf("\n");
printf("Number of elements in array B: %d\n", nB);
printf("\n");
printf("Array C: ");
nC = nC1;
for(i = 0 ; i < nC ; i++){
printf("%d ", C[i]);
}
printf("\n");
printf("Number of elements in array C: %d\n", nC);
}
void main(){
int *A;
int *B;
int *C;
int nA, nB, nC, i, r, j;
nB = 0;
nC = 0;
printf("Enter the length of array A: ");
scanf("%d", &nA);
printf("\n");
A = malloc(nA * sizeof(int));
if (A == NULL){
printf("ERROR: malloc failed.\n");
return 1;
}
time_t t;
srand((unsigned)time(&t));
printf("Array A: ");
for(i = 0 ; i < nA ; i++){
r = rand() % 101;
A[i] = r;
printf("%d ", r);
}
printf("\n");
Array(A, nA, B, nB, C, nC);
}
So far my code is breaking when:
User input from nA is higher than 6.
Code is working fine while array A has all elements which can be put in one single array such as B or C. But if elements can be split, last element of the array A is not shown properly on the screen while being in B or C array.
EDIT: Updated code so it's easier to keep track of my mistakes.
#include <stdio.h>
#include <stdlib.h>
void Array(int *A, int nA, int *B, int nB, int *C, int nC){
int i;
int nB1 = 0;
int nC1 = 0;
int *tmpB;
int *tmpC;
B = malloc(1*sizeof(int));
if(B == NULL){
printf("ERROR: malloc B failed.\n");
return 1;
}
C = malloc(1*sizeof(int));
if(C == NULL){
printf("ERROR: malloc C failed.\n");
return 1;
}
printf("\n");
for(i = 0 ; i < nA ; i++){
if(A[i] <= 50){
// C[nC1] = A[i];
// nC1++;
// if( nC1 > 1){
// tmpC = realloc(C, sizeof(int) * nC1);
// if(tmpC == NULL){
// printf("ERROR: realloc C failed.\n");
// return 1;
// }
// C = tmpC;
// }
tmpC = realloc(C, sizeof(int) * nC1);
if(tmpC == NULL){
printf("ERROR: realloc C failed.\n");
return 1;
}
C = tmpC;
C[nC1++] = A[i];
// nC1++;
}
else{
// B[nB1] = A[i];
// nB1++;
// if(nB1 > 1){
// tmpB = realloc(B, sizeof(int) * nB1);
// if(tmpB == NULL){
// printf("ERROR: realloc B failed.\n");
// return 1;
// }
// B = tmpB;
// }
tmpB = realloc(B, sizeof(int) * nB1);
if(tmpB == NULL){
printf("ERROR: realloc B failed.\n");
return 1;
}
B = tmpB;
B[nB1++] = A[i];
// nB1++;
}
}
printf("\n");
printf("Array B: ");
nB = nB1;
for(i = 0 ; i < nB ; i++){
printf("%d ", B[i]);
}
printf("\n");
printf("Number of elements in array B: %d\n", nB);
printf("\n");
printf("Array C: ");
nC = nC1;
for(i = 0 ; i < nC ; i++){
printf("%d ", C[i]);
}
printf("\n");
printf("Number of elements in array C: %d\n", nC);
}
int main(){
int *A;
int *B;
int *C;
int nA, nB, nC, i, r;
printf("Enter the length of array A: ");
scanf("%d", &nA);
printf("\n");
A = malloc(nA * sizeof(int));
if (A == NULL){
printf("ERROR: malloc A failed.\n");
return 1;
}
time_t t;
srand((unsigned)time(&t));
printf("Array A: ");
for(i = 0 ; i < nA ; i++){
r = rand() % 101;
A[i] = r;
printf("%d ", r);
}
printf("\n");
Array(A, nA, B, nB, C, nC);
return 0;
}
I'll try to take a crack at this I guess. I found some notable issues with your code that would be resolved with some structural changes towards your implementation. Some of the lesser ones:
void main() {
// Code above here left out.
A = malloc(nA * sizeof(int));
if (A == NULL){
printf("ERROR: malloc failed.\n");
return 1; // Might be a problem here.
}
You return an integer in main when there is not enough memory to allocate for A. This could cause issues on some compilers - or if you hate warnings in general a simple fix from void main to int main will clear this up.
2nd, in your function Array you have
void Array(int *A, int *nA, int *B, int *nB, int *C, int *nC)
however, you begin to use nA nB and nC as just int instead of int * which is a mismatch of types. You could change to
void Array(int *A, int nA, int *B, int nB, int *C, int nC)
Lastly and most importantly, you run into heap errors by using uninitialized ints to create memory for the arrays passed in:
int nB1;
int nC1;
B = malloc((nB1 + 1)*sizeof(int));
C = malloc((nC1 + 1)*sizeof(int));
It would be better to instead add just one int to the arrays without using nB1 and nC1.
B = malloc(1*sizeof(int));
C = malloc(1*sizeof(int));
You should first make checks as to whether B and C are empty arrays when they are passed in, then add memory to them as you see fit.
if (A[i] <= 50){
C[i] = A[i];
nC1++;
// The idea here is to have a new array with basically
// no length so that each time one element passes to either
// B or A array that array gets increased at the same
// time as nC or nB
tmpC = realloc(C, sizeof(int) * nC1);
if (tmpC == NULL){
printf("ERROR: realloc failed.\n");
}
C = tmpC;
}
tmpC is realloc'd but C is never given more space thus when assigning the value C[i] = A[i] it crashes. You need to expand on maintaining the arrays C and B.
EDIT
Disregard the comment about reallocation, the realloc operation you have is fine.
You iterate through each element in the array A, if the value is less than or equal to 50 you insert into array C otherwise B. However, you might have a few elements that satisfy the first condition but then the next condition could be satisfied. You then assign like so B[i] = A[i] but as stated you are at element 3 in A but you insert at element 1 in B, so using i to iterate for both is not correct. Follow SGM1's advice for reading into each array using nB1 and nC1, and also ryyker for the comment on reallocation.
I was able to compile and run this every time, however now you have to deal with actually assigning the values now:
for (i = 0; i < nA; i++){
if (A[i] <= 50){
C[nC1] = A[i];
nC1++;
tmpC = (int *)realloc(C, sizeof(int) * nC1);
if (tmpC == NULL){
printf("ERROR: realloc failed.\n");
}
C = tmpC;
// C = realloc(C, nC1 + 1);
}
Logic error B[i] = A[i]; in your condition should be B[nB1] = A[i];. Same thing with C array, should be C[nC1] = A[i] (both of this in the loop).
Also, from KillaBytes bytes analysis. Yes, you should definitely realloc BEFORE adding the new value. Probably is the reason your program crashes eventually:
else {
nB1++; // since you started at size 0
tmpB = realloc(B, sizeof(int) * nB1); //realloc to 0 = (sizeof(int) * 0) is bad
if(tmpB == NULL){
printf("ERROR: realloc failed.\n");
}
B = tmpB;
B[nB1 - 1] = A[i];///*****KEY POINT, This happens at the very end***
}
Still, there are many other problems, but not with the core logic. Especially with the the understanding of pointers. I'll try to list a few you need to think about:
Nothing comes out of the Array function (besides the printed output), the original B and C remain the same.
A, B (in the function), C (in the function) is not freed at the end. The idea is, for every malloc/calloc (realloc would free when successful), you would have to call free on the pointer to avoid any memory leakage :) just a trivial point, but very good habit.
Error conditions shouldn't continue logic, simplest solution would be to kill the program
if(tmpC == NULL){
printf("ERROR: realloc failed.\n");
}
should be
if(tmpC == NULL){
free(C);//unnecessary because the program is going to exit
// I would argue to get used to this, to force the habit
// to alway free all mallocs
printf("ERROR: realloc failed.\n");
exit(-1);// -1 is a generic value, error code that is checked
// if the caller cares to look (you can make it any int)
}
Now, if you wanted to "return" the value outside of for B and C Array() there are a few approaches, I'll demo one way.
void Array(int *A, int nA, int **B, int *nB, int **C, int *nC){ // funciton signature
Any time you use B, nB, C, nC in this function, use *B, *nB, *C, *nC. Eg:
*B = malloc((*nB1+1)*sizeof(int));
*C = malloc((*nC1+1)*sizeof(int));
And the call to the function from main should be:
printf("\n");
Array(A, nA, &B, &nB, &C, &nC);
There were a few bugs. There were a lot of errors/warnings flagged by the compiler (i.e. always compile with -Wall)
In the Array prototype, nA was defined as *nA [a pointer] but you used nA throughout the body.
*nB and *nC were correct, but near the bottom you needed:
*nB = nB1;
*nC = nC1;
There was a lot of replication code for all arrays. Each array had a custom loop to print it. To simplify, create a "print array" function.
The reallocation of B and C are similar, so, once again, create a common function.
Other bugs aside, while Array created B and C, it had no way to pass the updated values back to main. So, in the prototype, we need int **Bp and int **Cp.
In all, Array had two input values and four return values.
Here's the corrected code [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// prtarray -- print an array
void
prtarray(int *arr,int count,const char *sym)
{
printf("\n");
printf("Array %s:",sym);
for (int i = 0; i < count; i++)
printf(" %d ", arr[i]);
printf("\n");
printf("Number of elements in array %s: %d\n",sym,count);
}
// growarray -- grow array
int *
growarray(int *arr,int *pcount,int val)
{
int off;
off = *pcount;
arr = realloc(arr,sizeof(int) * (off + 1));
arr[off] = val;
*pcount = off + 1;
return arr;
}
// BUGFIX: we need "int nA" and _not_ "int *nA"
#if 0
void
Array(int *A, int *nA, int *B, int *nB, int *C, int *nC)
#else
void
Array(int *A, int nA, int **Bp, int *nB, int **Cp, int *nC)
#endif
{
#if 1
int *B = NULL;
int *C = NULL;
#endif
int i;
printf("\n");
for (i = 0; i < nA; i++) {
if (A[i] <= 50)
C = growarray(C,nC,A[i]);
else
B = growarray(B,nB,A[i]);
}
*Bp = B;
*Cp = C;
}
int
main()
{
int *A;
int *B;
int *C;
int nA;
int nB;
int nC;
nB = 0;
nC = 0;
printf("Enter the length of array A: ");
scanf("%d", &nA);
printf("\n");
A = malloc(nA * sizeof(int));
if (A == NULL) {
printf("ERROR: malloc failed.\n");
return 1;
}
time_t t;
srand((unsigned) time(&t));
for (int i = 0; i < nA; i++)
A[i] = rand() % 101;
prtarray(A,nA,"A");
#if 0
Array(A, nA, B, nB, C, nC);
#else
Array(A, nA, &B, &nB, &C, &nC);
#endif
prtarray(B,nB,"B");
prtarray(C,nC,"C");
return 0;
}
But, dynamic arrays "cry out" for using a struct to control things. The code becomes even simpler:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// array control
struct array {
int *arr_data; // data
int arr_count; // array count
const char *arr_sym; // array name
};
// arrinit -- initialize array
void
arrinit(struct array *arr,const char *sym)
{
arr->arr_data = NULL;
arr->arr_count = 0;
arr->arr_sym = sym;
}
// arrprint -- print an array
void
arrprint(struct array *arr)
{
printf("\n");
printf("Array %s:",arr->arr_sym);
for (int i = 0; i < arr->arr_count; i++)
printf(" %d ", arr->arr_data[i]);
printf("\n");
printf("Number of elements in array %s: %d\n",arr->arr_sym,arr->arr_count);
}
// arrgrow -- grow array
void
arrgrow(struct array *arr,int val)
{
int off;
off = arr->arr_count;
arr->arr_data = realloc(arr->arr_data,sizeof(int) * (off + 1));
arr->arr_data[off] = val;
arr->arr_count = off + 1;
}
// BUGFIX: we need "int nA" and _not_ "int *nA"
void
Array(struct array *A, struct array *B, struct array *C)
{
int i;
int val;
printf("\n");
for (i = 0; i < A->arr_count; i++) {
val = A->arr_data[i];
if (val <= 50)
arrgrow(C,val);
else
arrgrow(B,val);
}
}
int
main()
{
struct array A;
struct array B;
struct array C;
int nA;
arrinit(&A,"A");
arrinit(&B,"B");
arrinit(&C,"C");
printf("Enter the length of array A: ");
scanf("%d", &nA);
printf("\n");
time_t t;
srand((unsigned) time(&t));
for (int i = 0; i < nA; i++)
arrgrow(&A,rand() % 101);
arrprint(&A);
Array(&A, &B, &C);
arrprint(&B);
arrprint(&C);
return 0;
}
UPDATE:
Here's a clean wrapper for realloc to use in the above.
// qrealloc -- reallocate with null check
void *
qrealloc(void *ptr,size_t len)
{
#if 1
// what is normally sufficient
ptr = realloc(ptr,len);
if (ptr == NULL) {
fprintf(stderr,"qrealloc: realloc failure -- len=%lu\n",len);
exit(1);
}
#else
// what valgrind needs
void *tmp = realloc(ptr,len);
if (tmp == NULL) {
free(ptr);
fprintf(stderr,"qrealloc: realloc failure -- ptr=%p len=%lu\n",ptr,len);
exit(1);
}
ptr = tmp;
#endif
return ptr;
}
Related
I have been given a school assignment in C to create a program that multiplies matrices. I will list assignment constraints below so people don't respond with questions as to why I am doing things this way.
Constraints from instructor:
Cannot use square brackets anywhere in code (use pointer notation instead)
Matrices A, B, C must be single integer pointer variables (int *A, *B, *C)
Can only use main function and those specified by header
Must compile with "gcc -ansi -Wall -o p2 p2.c"
I have not implemented the matrix multiplication function yet, as the issues I am having relate to either file reading or memory allocation.
The specific problem I am having is when I allocate space to the pointer matrix with either malloc OR calloc (tried both), the program inserts 33 in some places in the output instead of 0. I've tried everything at this point and am convinced my knowledge of pointers is fundamentally flawed.
p2.h (given by instructor)
#include <stdio.h>
#include <stdlib.h>
/* This function reads m, n, and p from the datafile.
It then allocates the correct amount of memory required for matrices
A, B, and C.
Then matrices A and B are filled from the datafile.
The values for m, n, and p are passed by reference, and are
thus filled in by this function
PARAMETERS in order are:
int ** matrix A
int ** matrix B
int ** matrix C
int * m The number of rows in matrix A
int * n The number of columns in matrix A and
The number of rows in matrix B
int * p The number of columns in matrix B
char * The name of the datafile, from the command line
*/
void read_matrices(int **, int **, int **, int *, int *, int *, char *);
/* This function prints a matrix. Rows and columns should be preserved.
PARAMETERS in order are:
int * The matrix to print
int The number of rows in the matrix
int The number of columns in the matrix
*/
void print_matrix(int *, int, int);
/* The two matrices A and B are multiplied, and matrix C contains the
result.
PARAMETERS in order are:
int * Matrix A
int * Matrix B
int * Matrix C
int m
int n
int p
*/
void mult_matrices(int *, int *, int *, int, int, int);
p2.c (sorry for the mess a lot of debugging went on)
#include <stdio.h>
#include <stdlib.h>
#include "./p2.h"
/* constants for testing */
#define cM 3
#define cN 2
#define cP 5
int main(int argc, char **argv) {
if (argc < 2) {
printf("Must include an argument.\n");
exit(1);
}
char *path = *(argv + 1);
int *m = (int *) malloc(sizeof(int));
int *n = (int *) malloc(sizeof(int));
int *p = (int *) malloc(sizeof(int));
*m = cM; *n = cN; *p = cP;
int i,j; /* loop counters */
/* allocate space for 2d pointer arrays */
int **A = NULL;
A = (int **) malloc(*m * sizeof(int *));
for (i = 0; i < *m; i++) {
*(A+i) = (int *) malloc(*n * sizeof(int));
}
int **B = NULL;
B = (int **) malloc(*n * sizeof(int *));
for (i = 0; i < *n; i++) {
*(B+i) = (int *) malloc(*p * sizeof(int));
}
int **C = NULL;
C = (int **) malloc(*m * sizeof(int *));
for (i = 0; i < *m; i++) {
*(C+i) = (int *) malloc(*p * sizeof(int));
}
/* write data to A */
for (i = 0; i < *m; i++) {
for (j = 0; j < *n; j++) {
*(*(A+i)+j) = 0;
}
}
/* testing a */
for (i = 0; i < *m; i++) {
for (j = 0; j < *n; j++) {
if (*(*(A+i)+j) != 0) {
printf("[x]");
} else {
printf("[0]");
}
}
}
printf("\n");
/* write data to B */
for (i = 0; i < *n; i++) {
for (j = 0; j < *p; j++) {
*(*(B+i)+j) = 0;
}
}
/* testing b */
for (i = 0; i < *n; i++) {
for (j = 0; j < *p; j++) {
if (*(*(B+i)+j) != 0) {
printf("[x]");
} else {
printf("[0]");
}
}
}
printf("\n");
/* write data to C */
for (i = 0; i < *m; i++) {
for (j = 0; j < *p; j++) {
*(*(C+i)+j) = 0;
}
}
/* testing c */
for (i = 0; i < *m; i++) {
for (j = 0; j < *p; j++) {
if (*(*(C+i)+j) != 0) {
printf("[x]");
} else {
printf("[0]");
}
}
}
printf("\n");
printf("Matrix A: \n");
print_matrix(*A, *m, *n);
printf("Matrix B: \n");
print_matrix(*B, *n, *p);
printf("Matrix C: \n");
print_matrix(*C, *m, *p);
return 0;
}
void read_matrices(int **A, int **B, int **C, int *m, int *n, int *p, char *path) {
FILE *fptr;
fptr = fopen(path, "r");
if (fptr == NULL) {
printf("Cannot open file: ./p2 [filename].txt\n");
exit(1);
}
/* get first 3 numbers from file, set m,n,p */
*m = fgetc(fptr);
fgetc(fptr);
*n = fgetc(fptr);
fgetc(fptr);
*p = fgetc(fptr);
fgetc(fptr);
/* read first matrix */
/* 1) calculate matrix size m x n
* 2) loop through malloc'ed matrix
* 3) each loop, insert char in loc
* 4) if next char NOT 10/32, add nextchar*10 to value in loc
*/
char cur;
while ( (cur = fgetc(fptr)) != EOF ) {
if (cur == 10 || cur == 32) {
/* do nothing :) */
} else {
*m = cur;
*n = cur;
*p = cur;
break;
}
}
printf("m: %c\n", *m);
printf("n: %c\n", *n);
printf("p: %c\n", *p);
printf("next: %c\n", fgetc(fptr));
fclose(fptr);
}
void print_matrix(int *X, int rows, int cols) {
int r, c;
int k = 0;
for (r = 0; r < rows; r++) {
for (c = 0; c < cols; c++) {
printf("\t%d", *(X+k));
k++;
}
printf("\n");
}
}
void mult_matrices(int *A, int *B, int *C, int m, int n, int p) {
}
d2.txt (data file)
3
2
4
1 2
3 4
5 6
7 8 9 10
11 12 13 14
Output: ./p2 d2.txt
[0][0][0][0][0][0]
[0][0][0][0][0][0][0][0][0][0]
[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]
Matrix A:
0 0
0 0
0 0
Matrix B:
0 0 0 0 0
0 33 0 0 0
Matrix C:
0 0 0 0 0
0 33 0 0 0
0 0 0 0 33
If you notice, I have some debug code that checks whether or not the current item in the array is 0. It seems to indicate that they are all 0, making me think it is a printing problem, but I am even more lost on what would be causing that. The ascii code for 33 is an exclamation point, but I am not sure what relevance it has.
Based on the function signatures you're supposed to use, you need to implement your 2D arrays as 1D with the correct index math. This will result in all memory being laid out contiguously, which is not at all guaranteed with the way you're allocating memory now (two calls to malloc for each matrix). For example:
#include <stdio.h>
#include <stdlib.h>
void print_matrix(int* A, int rows, int cols)
{
for (int r=0; r<rows; r++)
{
for (int c=0; c<cols; c++)
{
// If you want to treat A as a 2D matrix, this is where we have to do a bit of
// fancy index math to give you what double bracket notation [][] does for you
// r * cols gives you the index of the right row
// + c give you the column offset in that row
// add that offset to A then dereference
printf("%d\t", *(A + (r * cols + c)));
}
printf("\n");
}
}
int main(void)
{
// matrix A is supposed to be m by n
int* A;
// read these from file, or where ever they're supposed to come from
int m = 2;
int n = 10;
// Allocate the memory in one chunk. This makes the memory all contiguous, just the
// same as if you had done A[m][n]. However, the double call malloc for each int**
// matrix probably will not give you contiguous memory for the entire matrix. Each
// call to malloc is independent.
A = malloc(m * n * sizeof(int)); // or sizeof(*A) would be even better
if (A == NULL)
{
// handle error
}
// We can initialize values for A at this point, still not needing to care about
// rows or columns
for (int i=0; i<m*n; i++)
{
*(A + i) = i; // using i for a better visual when we print
}
print_matrix(A, m, n);
free(A);
return 0;
}
Demo
You are ovecomplicating simple things. Use pointers to arrays and allocate 2D array.
Use the correct type of your size variables.
Try to avoid side effects. Use parameters and function return values.
//this function is for the test purposes only
int writefile(const char *fn)
{
FILE *fo = fopen(fn, "w");
fprintf(fo,
"3\n"
"2\n"
"4\n"
"1 2\n"
"3 4\n"
"5 6\n"
"7 8 9 10\n"
"11 12 13 14\n");
fclose(fo);
}
void *allocIntMatrix(size_t rows, size_t cols)
{
int (*m)[cols] = malloc(rows * sizeof(*m));
return m;
}
void printIntMatrix(size_t rows, size_t cols, int (*m)[cols])
{
for(size_t row = 0; row < rows; row++)
{
for(size_t col = 0; col < cols; col++)
{
printf("[%5d] ", m[row][col]);
}
printf("\n");
}
}
int readData(FILE *fi, size_t rows, size_t cols, int (*m)[cols])
{
for(size_t row = 0; row < rows; row++)
{
for(size_t col = 0; col < cols; col++)
{
fscanf(fi, "%d", &m[row][col]);
}
}
return 0;
}
int main(int argc, char **argv)
{
size_t n,m,p;
writefile("a.aaa");
FILE *fi = fopen("a.aaa", "r");
fscanf(fi, "%zu", &m);
fscanf(fi, "%zu", &n);
fscanf(fi, "%zu", &p);
printf("n = %zu, m = %zu, p = %zu\n", n, m, p);
int (*A)[n] = allocIntMatrix(m, n);
int (*B)[p] = allocIntMatrix(n, p);
readData(fi, m, n, A);
readData(fi, n, p, B);
fclose(fi);
printIntMatrix(m, n, A);
printf("\n");
printIntMatrix(n, p, B);
return 0;
}
https://godbolt.org/z/adoEx1r4f
You need to check for errors (file, memory etc). I skipped it for the sake of simplicity of the example.
I am trying to practice with C by making a bubble sort program. The problem until now seems to be that the for loop that is giving values to the cells of the array is stuck after the condition is no longer fulfilled but it doesn't seem to be executing the commands in the loop. I don't know what is happening exactly and I have added some extra lines to see what is happening an these were my conclusions. Here is the code:
#include <stdio.h>
#include <stdlib.h>
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int *sort(int *array)
{
int finish = 1;
while (finish = 1)
{
finish = 0;
for (int i = 0; i <= sizeof(array); i++)
{
if ((array + i) > (array + i + 1))
{
swap(array + i, array + i + 1);
finish = 1;
}
}
}
return array;
}
int main()
{
int s, res;
printf("Give me the size of the array being sorted(larger than 1) : ");
do
{
res = scanf("%d", &s);
if (res != 1)
{
printf("Wrong Input!\n");
exit(1);
}
if (s < 2)
printf("Only numbers equal or larger than 2\n");
} while (s < 2);
int array[s];
for (int i = 0; i < s; i += 1)
{
scanf("%d", array + i);
printf("%d %d %d\n\n", *(array + i), i, i < s); // I used this to check if my values were ok
}
printf("end of reading the array"); //I added this line to see if I would exit the for loop. I am not seeing this message
sort(array);
printf("\n");
for (int i = 0; i < sizeof(array); i++)
printf("%d\n\n", array + i);
printf("Array has been sorted! Have a nice day!\n\n************************************************************");
return 0;
}
See the annotations in the code:
#include <stddef.h> // size_t 1)
#include <stdio.h>
#include <stdlib.h>
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int *sort(int *array, size_t size) // needs an extra parameter to know the size of the array
{
int finish = 1;
while (finish /* = 1 * you don't want assignment, you want comparison: */ == 1)
{
finish = 0;
for (int i = 0; i /* <= sizeof(array) */ < size - 1; i++) // i should be of type size_t
{
// if ((array + i) > (array + i + 1)) you are not dereferencing:
if(array[i] > array[i + 1])
{
// swap(array + i, array + i + 1); // easier to read imho:
swap(&array[i], &array[i + 1]);
finish = 1;
}
}
}
return array; // why does this function return anything? it is never used.
}
int main()
{
int s; /* , res; no need for an extra variable res */
printf("Give me the size of the array being sorted(larger than 1) : ");
do
{
// res = scanf("%d", &s);
// if (res != 1)
if (scanf("%d", &s) != 1)
{
printf("Wrong Input!\n");
// exit(1); // should be EXIT_FAILURE. Use return instead of exit() when in main().
return EXIT_FAILURE;
}
if (s < 2)
printf("Only numbers equal or larger than 2\n");
} while (s < 2);
int array[s];
for (int i = 0; i < s; /* i += 1* idiomatic: */ ++i) // size_t would be the correct type for s and i.
{
scanf("%d", /* array + i use indexes: */ &array[i]);
printf("%d %d %d\n\n", array[i], i, i < s); // again: indexes. i < s is allready ensured by the condition of the for-loop
}
printf("end of reading the array");
// sort(array); // sort will have no idea about the size of array use
sort(array, s); // instead.
printf("\n");
for (int i = 0; i < /* sizeof(array) 2) */ s; i++)
printf("%d\n\n", /* array + i * again you don't dereference */ array[i]);
printf("Array has been sorted! Have a nice day!\n\n************************************************************");
return 0;
}
1) size_t is the type that is guaranteed to be big enough to hold all sizes of objects in memory and indexes into them. The conversion specifier for scanf() is "%zu".
2) sizeof(array) in main() will yield the number of bytes in array, but you want the number of elements so you'd have to use sizeof(array) / sizeof(*array). But thats not needed since you already know its size. It is s.
This line
printf("end of reading the array");
has no line feed at the end of the string. This is a problem because printf is part of the family of functions called "buffered IO". The C library maintains a buffer of the things you want to print and only sends them to the terminal if the buffer gets full or it encounters \n in the stream of characters. You will not see, end of reading the array on your screen until after you have printed a line feed. You only do this after calling sort(). So all you know is your program is getting into an infinite loop at some point before the end of sort.
So there are actually three loops that could be infinite: the for loop you identified, the while loop in sort and the for loop inside the while loop. As the other answers point out, you have made the classic mistake of using assignment in the while conditional
while (finish = 1)
// ^ not enough equals signs
Unless your C compiler is really old, it is probably outputting a warning on that line. You should heed warnings.
Also, you should learn to use a debugger sooner rather than later. Believe me, it will save you a lot of time finding bugs.
In the sort function sizeof(array) returns the size of the pointer. (you can check it by yourself using printf("%d", sizeof(array).
The solution is to change your function to:
int sort(int* array, size_t size) { ... }
and call it with the correct array size:
sort(array, s);
Im writing a program with a function add(a , i, n) which will add 'i' as an element to 'a', but if the array 'a' runs out of space, then I need to realloc more memory to the array. Im stuck here:
#include <stdlib.h>
#include <stdio.h>
int add(int* a, int i, int n);
int main(){
int n = 20;
int *a = (int*) malloc(n*sizeof(int));
int i;
for (i = 0; i < 100000; i++){
n = add(a, i, n);
printf("a[%d]=%d\n",i,(int)a[i]);
}
return 0;
}
int add(int *a, int i, int n){
if (i >= n){
n++;
int* b = (int*) realloc(a, n*sizeof(int));
a[i]=i;
return n;
}else{
}
}
Im not very experienced so please be gentle...
realloc tries to reallocate the given memory, but sometimes, it can't and gives you a new memory pointer.
It must be used like:
int *b;
b = realloc(a, <newsize>);
if (b) {
/* realloc succeded, `a` must no longer be used */
a = b;
/* now a can be used */
printf("ok\n");
} else {
/* realloc failed, `a` is still available, but it's size did not changed */
perror("realloc");
}
Now, you still have some trouble in your code:
The idea of function add() is to reallocate a when needed, but a is given by copy, so its value won't be changed in main.
#include <stdlib.h>
#include <stdio.h>
int add(int** a, int i, int n);
int main(){
int n = 20;
int *a = malloc(n*sizeof(int));
int i;
for (i = 0; i < 100000; i++) {
/* note how `a` is passed to `add` */
n = add(&a, i, n);
printf("a[%d]=%d\n",i,a[i]);
}
/* and finally free `a`*/
free(a);
return 0;
}
/* changed `a` type to make its new value visible in `main` */
int add(int **a, int i, int n){
if (i >= n){
/* want to write a[i], so size must be at least i+1*/
n = i+1;
/* realloc memory */
int *b = realloc(*a, n*sizeof(int));
/* check that `realloc` succeded */
if (!b) {
/* it failed!*/
perror("realloc");
exit(1);
}
/* store new memory address */
*a = b;
}
/* update memory */
(*a)[i]=i;
/* return new size */
return n;
}
Note: I removed malloc/realloc cast, see: Do I cast the result of malloc?
To have an automatically growing array in C you would normally need a helper function ensure_capacity to take care of the array reallocation.
The helper function would preferrably reallocate using 2x grow policy, so you have an amortized constant time of the append operation.
The code would look somewhatlike the below.
Note that the code is using the first 2 elements of the array to keep its capacity/size. You can use a struct of pointer + size instead, but you need to keep the two close two each other as otherwise the code won't be easy to read.
int* ensure_capacity(int* vec, int new_cap) {
if (vec == 0) {
vec = (int*) malloc(18 * sizeof(int));
vec [0] = 16;
vec [1] = 0;
} else {
int cap = vec[0];
if (cap < new_cap) {
do {
cap *= 2;
} while (cap < new_sz);
int* new_vec = (int*) realloc(vec, cap * sizeof(int));
if (new_vec != null) {
vec = new_vec;
vec[0] = cap;
} else {
// reallocation failed, handle the error
}
}
}
return vec;
}
And you would use it in your add() function like:
int* push_back(int* vec, int val) {
vec = ensure_capacity(vec, vec[1] + 1);
vec[vec[1]++] = val;
return vec;
}
I'm new to C and have been trying to tackle this question. It's a continuation of the last thread I made. I made some progress but still have so much to learn and fix.
In short:
In this question a "vector" is a one dimensional array of integers. Therefore an array of vectors would be a two dimensional array that holds one dimensional arrays inside him.
I need to use these variables:
int** vectors- the 2D array
int size -an integer that represents how many vectors exist inside **vectors
int* sizes-a 1D array of integers that represents the length of the vectors
I need to write the following functions:
int init(int ***vectors, int **sizes, int size)
the function allocated memory to **vectors and *sizes with size and initializes vectors to be full of NULLs,and sizes to be full of zeros.
int set(int **vectors, int *sizes, int index, int *tmp, int tmp_size)
the function receives an array of nulls (**vectors)), frees the vector inside **vectors whose index is index and allocates memory for a new vector, whose length is tmp_size and places inside it *tmp's elements.
This is my code:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int init(int*** vectors, int** sizes, int size)
{
int i, k,j;
*sizes = (int*)malloc(size * sizeof(int));
if (*sizes == NULL)
return 0;
for (j = 0; j < size; j++)
{
(*sizes)[j] = 0;
}
*vectors = (int**)malloc(size * sizeof(int*));
if (*vectors == NULL)
return 0;
for (i = 0; i < size; i++)
{
(vectors)[i] = NULL;
}
return 1;
}
int set(int **vectors, int *sizes, int index, int *tmp, int tmp_size)
{
if ((vectors)[index] != NULL)
{
free((vectors)[index]);
}
(vectors)[index] = (int*)malloc(tmp_size * sizeof(int));
if ((vectors)[index] == NULL)
return 0;
for (int b = 0; b < tmp_size; b++)
{
(vectors)[index][b] = tmp[b];
}
sizes[index] = tmp_size;
return 1;
}
int main()
{
int size, i, length, indexhere;
int** vectors = NULL;
int* sizes = NULL;
int* tmp = NULL;
int* p = &vectors;
int tempindex;
printf("\nPlease enter an amount of vectors:\n");
scanf("%d", &size);
init(p, &sizes, size);
printf("Enter index\n");
scanf("%d", &indexhere);
printf("Enter Length\n");
scanf("%d", &length);
tmp = (int*)malloc(length * sizeof(int));
printf("Enter elements:\n");
for (int g = 0; g < length; g++)
scanf("%d", &tmp[g]);
set(&vectors, sizes, indexhere, tmp, length);
system("pause");
return 0;
}
Could someone explain please why the program always crashes?
In init function (vectors)[i] = NULL; should actually be (*vectors)[i] = NULL;
When calling set function from main you should pass vectors instead of &vectors.
There also seems to be several pointer type mismatches in your code, so you should really pay attention to compiler's warnings. This is because C unfortunately allows implicit conversions between incompatible pointers, unlike C++ for example.
You call set like this
set(&vectors, sizes, indexhere, tmp, length);
but the first argument is declared as an int **. By passing &vector you're passing a pointer to vector, i.e. something of type int ***. This mismatch will lead to undefined behavior and probable crashes.
Here is a complete working example.
#include <stdio.h>
#include <stdlib.h>
void destroyVectors(int **vectors, int size)
{
for (int i = 0; i < size; i++)
{
free(vectors[i]);
}
}
int init(int*** vectors, int** sizes, int size)
{
int i, j;
*sizes = (int*)malloc(size * sizeof(int));
if (*sizes == NULL)
return 0;
for (j = 0; j < size; j++)
{
(*sizes)[j] = 0;
}
*vectors = (int**)malloc(size * sizeof(int*));
if (*vectors == NULL)
return 0;
for (i = 0; i < size; i++)
{
(*vectors)[i] = NULL;
}
return 1;
}
int set(int **vectors, int *sizes, int index, int *tmp, int tmp_size)
{
if ((vectors)[index] != NULL)
{
free((vectors)[index]);
}
(vectors)[index] = (int*)malloc(tmp_size * sizeof(int));
if ((vectors)[index] == NULL)
return 0;
for (int b = 0; b < tmp_size; b++)
{
(vectors)[index][b] = tmp[b];
}
sizes[index] = tmp_size;
return 1;
}
int main()
{
int size = 0, length = 0, indexhere = 0;
int** vectors = NULL;
int* sizes = NULL;
int* tmp = NULL;
printf("\nPlease enter an amount of vectors:\n");
scanf("%d", &size);
init(&vectors, &sizes, size);
printf("Enter index\n");
scanf("%d", &indexhere);
printf("Enter Length\n");
scanf("%d", &length);
tmp = (int*)malloc(length * sizeof(int));
printf("Enter elements:\n");
for (int g = 0; g < length; g++)
scanf("%d", &tmp[g]);
set(vectors, sizes, indexhere, tmp, length);
for(int i = 0; i < length; ++i)
printf("byte: %d\n", vectors[indexhere][i]);
printf("sizes index: %d\n", sizes[indexhere]);
free(tmp);
free(sizes);
destroyVectors(vectors, size);
return 0;
}
I am trying to sort a 2D array of doubles using qsort() in C. The arrays contain 3D point data, which is read in from a file using fscanf. My programming skills are rather limited, but I have really large datasets that I need to deal with. Sorry in advance if my code sucks.
23127.947, 23127.947, 23127.947
523127.790, 523127.790, 523127.790
523127.747, 523127.747, 523127.747
523127.761, 523127.761, 523127.761
523127.768, 523127.768, 523127.768
(...for 3,158,632 points)
I've used printf to isolate that the problem in my code seems to be the qsort() line, which causes a segmentation fault. From other questions on Stack Overflow that I read, it could be a problem with my "compare" function. Examples for doing a 1D array seemed easy, but the examples I saw for 2D arrays didn't go into comparing the other dimensions (first X, then if X1 = X2, compare Y, then if Y1 = Y2, compare Z).
int main(int argc, char *argv[]) {
int i,j,c;
double x,y,z;
int ROWS = 3158632;
int COLS = 3;
char buffer[100];
double** data = Make2DDoubleArray(ROWS, COLS);
//Open the plot file to read in, and have an output write file
FILE *fp = fopen("Plot_1-2.txt","r");
if(fp == NULL) {
printf("Can't open file\n");
exit;
}
fgets(buffer, 100, fp); //Ignore header
for(i=0; ; i++){
if ((c = fgetc(fp)) == EOF){
break;
}
fscanf(fp,"%lf, %lf, %lf",&x, &y, &z);
data[i][0] = x;
data[i][1] = y;
data[i][2] = z;
}
printf("First 5 unsorted numbers:\n");
for(j=0;j<5;j++){
printf("Line %d: %.3lf, %.3lf, %.3lf\n",j, data[j][0], data[j][0], data[j][0]);
}
printf("Last 5 unsorted numbers:\n");
for(j=ROWS-5;j<ROWS;j++){
printf("Line %d: %.3lf, %.3lf, %.3lf\n",j, data[j][0], data[j][0], data[j][0]);
}
/* Sort array using Quicksort algorithm: */
printf("Sorting...\n");
qsort(data, ROWS, COLS*sizeof(double), &compare);
printf("First 10 sorted numbers:\n");
for(j=0;j<10;j++){
printf("Line %d: %.3lf, %.3lf, %.3lf\n",j, data[j][0], data[j][0], data[j][0]);
}
fclose(fp);
for (i=0; i<ROWS; i++){
free(data[i]);
}
free(data);
return 0;
}
double** Make2DDoubleArray(int arraySizeX, int arraySizeY) {
double** theArray;
int i;
theArray = (double**) malloc(arraySizeX*sizeof(double*));
for (i = 0; i < arraySizeX; i++)
theArray[i] = (double*) malloc(arraySizeY*sizeof(double));
return theArray;
}
int compare(const void *arg1, const void *arg2) {
//double a, b, c, d, e, f;
double *a = (double*)arg1;
double *b = (double*)arg2;
double *c = ((double*)arg1 + 1);
double *d = ((double*)arg2 + 1);
double *e = ((double*)arg1 + 2);
double *f = ((double*)arg2 + 2);
if(a > b)
return 1;
else if(a < b)
return -1;
else {
if(c > d)
return 1;
else if(c < d)
return -1;
else {
if(e > f)
return 1;
else if(e < f)
return -1;
else
return 0;
}
}
}
I'm wondering if telling qsort to go "COLS * sizeof(double)" is the wrong way to do it with how I allocated the memory for the 2D array? Would treating this problem as a 1D array make the rest of it work? I'd prefer to keep it as a 2D array, if possible.
qsort expects the sorted elements to come in a contiguous block of memory. You can still keep your data in a 2D array, if all your cells constitute a contiguous block of memory that can be interpreted as an 1D array and used with qsort.
Instead of allocating memory separately for each row as you do in Make2DDoubleArray, allocate memory for all rows at once. Then, in addition to what you return now: an array of pointers to rows; you will also have to return (using argument-by-pointer) the memory block containing all of your rows.
You are allocating memory for each row
for (i = 0; i < arraySizeX; i++)
theArray[i] = (double*) malloc(arraySizeY*sizeof(double));
while you could allocate the memory in one step
double *cells = malloc(sizeof(double) * arraySizeX * arraySizeY);
if (cells == NULL) { ... }
for (i = 0; i < arraySizeX; i++)
theArray[i] = &cells[arraySizeY * i];
Then you will have two arrays: an array of pointers to rows which you have now (called theArray in your code); and a new 1D array that keeps all rows (not pointers to rows, but the arrays of cells) (and in effect, all cells where each row, a triplet, is one datapoint) and can be used for qsort (in my code called cells).
Then, pass the latter one - cells (and not data) to qsort
qsort(cells, ROWS * COLS, sizeof(double), &compare);
Also note that the call in the code in the question
qsort(data, ROWS, COLS*sizeof(double), &compare);
is wrong, because you are not sorting an amount of ROWS rows, each with a size of COLS*sizeof(double).
EDIT: Uh, my apologies. I misunderstood that you have a 2D array of entries, but now I see that COLS represents fields of one cell. In which case you would be better of with #SpacedMonkey's solution.
Just for reference, my answer would also work, then you would call qsort like you did, but on cells
qsort(cells, ROWS, COLS*sizeof(double), &compare);
None of this means anything without headers like <stdio.h>, <stdlib.h>, etc...
Please explain exit;. I think you mean exit(0);.
There are a few problems in your main. Because of that fgetc, your code potentially loses the most significant digit of your first value, which is a subtle bug. If you want to test for EOF, test the return value of scanf (Jee! I didn't think of that! I wish they wrote these things in manuals! Duh, they do...). The example at the end of the file is better than this, because that example ensures that three values are actually parsed by fscanf.
for(size_t i=0; fscanf(fp,"%lf, %lf, %lf",&x, &y, &z) != EOF; i++){
data[i][0] = x;
data[i][1] = y;
data[i][2] = z;
}
There's a problem in your Make2DDoubleArray function. It allocates many disjoint arrays, which qsort can't handle. Isn't it much cleaner to allocate your array in one step?
void *Make2DDoubleArray(size_t x) {
double (*theArray)[3] = malloc(x * sizeof *theArray);
return theArray;
}
theArray is declared as a pointer to an array of 3 doubles. You don't even need a Make2DDoubleArray for this.
There's a problem in the compare function.
double *a = (double*)arg1;
double *b = (double*)arg2;
a and b are pointers,
if(a > b)
return 1;
else if(a < b)
return -1;
... yet your code compares them as integers, rendering the sort malfunctional. The address of array[0] will always be less than the address of array[1].
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
int main(int argc, char *argv[]) {
int j,c;
double x,y,z;
size_t ROWS = 3158632;
size_t COLS = 3;
char buffer[100];
double (*theArray)[COLS] = malloc(ROWS * sizeof *theArray);
//Open the plot file to read in, and have an output write file
FILE *fp = fopen("Plot_1-2.txt","r");
if(fp == NULL) {
printf("Can't open file\n");
exit(0);
}
fgets(buffer, 100, fp); //Ignore header
for(size_t i=0; fscanf(fp,"%lf, %lf, %lf", &x, &y, &z) == 3; i++){
data[i][0] = x;
data[i][1] = y;
data[i][2] = z;
}
printf("First 5 unsorted numbers:\n");
for(size_t j=0; j<5; j++){
printf("Line %zu: %.3lf, %.3lf, %.3lf\n", j, data[j][0], data[j][0], data[j][0]);
}
puts("Last 5 unsorted numbers:");
for(size_t j=ROWS-5; j<ROWS; j++){
printf("Line %zu: %.3lf, %.3lf, %.3lf\n", j, data[j][0], data[j][0], data[j][0]);
}
/* Sort array using Quicksort algorithm: */
puts("Sorting...");
qsort(data, ROWS, sizeof *data, compare);
puts("First 10 sorted numbers:");
for(size_t j=0;j<10;j++){
printf("Line %zu: %.3lf, %.3lf, %.3lf\n", j, data[j][0], data[j][0], data[j][0]);
}
fclose(fp);
free(data);
return 0;
}
int compare(const void *arg1, const void *arg2) {
double (*x)[3] = arg1;
double (*y)[3] = arg2;
if ((*x)[0] > (*y)[0])
return 1;
else if ((*x)[0] < (*y)[0])
return -1;
else if ((*x)[1] > (*y)[1])
return 1;
else if ((*x)[1] < (*y)[1])
return -1;
else if ((*x)[2] > (*y)[2])
return 1;
else if ((*x)[2] < (*y)[2])
return -1;
else
return 0;
}
Try using a struct for the data instead:
typedef struct {
double x;
double y;
double z;
} point_data;
Then you only need a 1 dimensional array of this new type:
point_data *array = malloc(linesRead * sizeof *array);
And your comparison function remains fairly similar:
int compare(const void *arg1, const void *arg2) {
point_data *point1 = arg1,
*point2 = arg2;
if ( point1->x > point2->x ) {
return 1;
else if ( point1->x < point2->x ) {
return -1;
} else {
if ( point1->y > point2->y ) {
return 1;
else if ( point1->y < point2->y ) {
return -1;
} else {
if ( point1->z > point2->z ) {
return 1;
else if ( point1->z < point2->z ) {
return -1;
} else {
return 0;
}
}
}
}
Also, please don't hardcode the number of points, instead count the number you read in.