Int value 'j' is getting weird results when i keep trying to debug it. I'm not sure if it's a problem with my school's compiler or if it's my code. Thanks for the help.
GCC Version:
Reading specs from /usr/local/lib/gcc-lib/sparc-sun-solaris2.7/2.95.2/specs
gcc version 2.95.2 19991024 (release)
My segment of code that's messing up:
void sFlag(DIR * dirp, int c, char *dirname)
{
struct dirent *dp;
struct stat statbuf;
struct stat statarray[c];
struct stat tempstat;
char fullfilename[MAXSZ];
int i;
int boo;
int j; /*<--------- variable that's messing up*/
while((dp = readdir(dirp)) != NULL)
{
snprintf(fullfilename, MAXSZ, "%s/%s", dirname, dp->d_name);
if(stat(fullfilename, &statbuf) == -1)
printf("Could not read file %s\n", dp->d_name);
else if(isHiddenFile(dp->d_name))
{
statarray[i] = statbuf;
i++;
}
}
/*As far as i know all the code above works fine*/
/*bubble sort that will sort array by logical file size*/
while(boo)
{
j = 0;
boo = 0;
while(j < c)
{
fprintf(stderr, "%d\n", j); /*print debug info*/
if(statarray[j].st_size < statarray[j+1].st_size)
{
tempstat = statarray[j];
statarray[j] = statarray[j+1];
statarray[j+1] = tempstat;
boo = 1;
}
j++;
}
}
for(j = 0; j < c; j++)
{
printf("%s\t%ld\t%s\n", dp->d_name, statarray[j].st_size, ctime(&statarray[j].st_mtime));
}
}
So everytime i run this the fprintf prints out the value for j as:
0
1
2
3
4
-12975991
???????Where did it get that number from???
Obviously I get a segmentation fault from an array index out of bounds
any thoughts?
You're most likely trampling memory and overwriting the content of j. This loop:
while(j < c)
{
fprintf(stderr, "%d\n", j); /*print debug info*/
if(statarray[j].st_size < statarray[j+1].st_size)
{
tempstat = statarray[j];
statarray[j] = statarray[j+1];
statarray[j+1] = tempstat;
boo = 1;
}
j++;
}
Notice that it accesses statarray[j+1], but statarray is defined as
struct stat statarray[c];
meaning that on the last iteration, j+1 == c, which is out of bounds. Writing to that index in the array will trample other things on the stack, which could include j, and explain why you get a wacky-sounding value.
There are some nifty tools that can make this easier to find that you might consider, like valgrind.
In this block,
while(j < c)
{
fprintf(stderr, "%d\n", j); /*print debug info*/
if(statarray[j].st_size < statarray[j+1].st_size)
{
tempstat = statarray[j];
statarray[j] = statarray[j+1];
statarray[j+1] = tempstat;
boo = 1;
}
j++;
}
You are accessing unauthorized memory when j is equal to c-1. That would mess things up. You can't expect predictable behavior after that.
You have a fresh variable j in the while loop, the outer j is never set.
Related
I am working on a program written in C that recursively walks a given directory in order to print out the Nth largest files and their sizes in bytes. I am using two arrays to account for the filesystem entry names and filesystem entry sizes respectively.
EDIT: I have updated my program to implement the suggestions shared in the comment section. My focus now is on correctly implementing a swap operation within my iSort function.
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
// number of files to display size information for
const int N = 10;
// a struct used to hold filesystem entry names and corresponding sizes in bytes
struct info {
char name[1024];
int size;
};
/* A simple implementation of insertion sort that will operate upon
an array of info structs, sorting them by their member size in
ascending order */
void iSort(struct info *fs_info[], int size, int info_size)
{
int i, j, key;
for (i = 1; i < size; i++)
{
key = fs_info[i]->size;
j = i - 1;
while (j >= 0 && fs_info[j]->size > key)
{
printf("info_size: %d\n", info_size);
// TODO complete a swap operation
memmove(fs_info[j + 1], fs_info[j], info_size);
j = j - 1;
}
fs_info[j + 1]->size = key;
}
}
void get_size(char *path, struct info fs_info[N], int info_size)
{
static int items_added = 0;
static int max_size = 0;
struct stat st;
if (stat(path, &st) == 0)
{
if (items_added < N) // if array capacity will not be exceeded
{
strcpy(fs_info[items_added].name, path);
fs_info[items_added].size = st.st_size;
if (st.st_size > max_size)
max_size = st.st_size;
items_added++;
}
else
{
// do a comparison to determine where to insert
// sort first
iSort(&fs_info, 10, info_size); // this function call results in a seqfault
}
}
else
{
printf("Error getting stat for entry %s: %d\n", path, stat(path, &st));
}
}
void walk(const char *currDir, struct info fs_info[N], int info_size)
{
DIR *dir = opendir(currDir);
struct dirent *entry;
if (dir == NULL)
{
// directory could not be opened
return;
}
while ((entry = readdir(dir)) != NULL)
{
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
{
// if directory is current dir or parent dir
continue;
}
char path_to_entry[1024];
snprintf(path_to_entry, sizeof(path_to_entry), "%s/%s", currDir, entry->d_name);
// use path_to_entry to call stats on the entry
get_size(path_to_entry, fs_info, info_size);
if (entry->d_type == DT_DIR)
{
// recursively visit subdirectories
walk(path_to_entry, fs_info, info_size);
}
}
closedir(dir);
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: %s <target directory>\n", argv[0]);
}
const char *target_dir = argv[1];
struct info fs_entries[N];
const int info_size = sizeof(struct info);
for (int i = 0; i < N; i++)
{
strcpy(fs_entries[i].name, "");
fs_entries[i].size = 0;
}
printf("Finding %d largest files in: %s\n", N, target_dir);
walk(target_dir, fs_entries, info_size);
for (int i = 0; i < N; i++)
{
printf("%s : %d\n", fs_entries[i].name, fs_entries[i].size);
}
return 0;
}
Currently, the memmove() invocation in iSot() results in a EXC_BAD_ACCESS error, I am working on improving this function now. Any suggestions in the interim are appreciated. Thanks also to those who commented on this question earlier.
This is not so much an answer as it is an example of how one might code this with a bit less fiddling about with every bit/byte.
I don't run a flavour of UNIX, so this offering is untested and may contain typos and even bugs. I hope not.
#include <stdio.h> // From 'generic' to 'specific'
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>
const int N = 10;
// Global array holding names and sizes
struct {
char name[ MAX_PATH ];
size_t size;
} biggies[ N ], wrk; // and a working buffer.
/* "Global" is bad for large projects.
* In this 'utility' program, global saves a LOT of typing/reading.
* Seek clarity, not conformity,
*
* "wrk" is used to buffer ALL paths encountered.
* Notice that each recursion is an EXTENSION of its parent.
* ONE working buffer to deal with.
*/
void walk() {
size_t len = strlen( wrk.name ); // The path so far...
DIR *dir;
if( ( dir = opendir( wrk.name ) ) == NULL )
return;
wrk.name[ len++ ] = '/'; // append a slash ahead of strcpy() below
struct dirent *entry;
while( ( entry = readdir( dir ) ) != NULL ) {
if( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0 )
continue;
// Notice how each 'local' name is written to "the right spot" in one buffer
strcpy( wrk.name + len, entry->d_name );
if( entry->d_type == DT_DIR ) // directory, so recursion...
walk();
else {
struct stat st;
if( stat( wrk.name, &st ) != 0 ) {
fprintf( stderr, "Error stat'ing '%s'\n", wrk.name );
exit( EXIT_FAILURE );
}
// Add this info to working buffer.
wrk.size = st.st_size;
// Find where to 'insert' this (if it is larger than descending ordered list
for( int i = 0; i < N && biggies[i].size > wrk.size; i++ ) {} // loop
if( i < N ) {
// Slide smaller ones down (don't go out of bounds)
memmove( biggies[i + 1], biggies[i], (N-i-1) * sizeof biggies[0] );
// Copy this one in place.
memcpy( biggies[i], &wrk, sizeof biggies[0] );
}
}
}
closedir(dir);
}
int main( int argc, char *argv[]) {
char *target_dir = argv[1]; // This is okay, so far...
if( argc != 2 ) {
printf( "Usage: %s <target directory>\n", argv[0] );
puts( "Using current directory..." );
target_dir = ".";
}
strcpy( wrk.name, target_dir );
printf( "Finding %d largest files in: %s\n", N, wrk.name );
walk();
for( size_t i = 0; i < N && biggies[i].size != 0; i++ )
printf( "%s : %d\n", biggies[i].name, biggies[i].size);
return 0;
}
You don't need to pass the address of fs_info to iSort() function. fs_info is a pointer to first element of fs_entries array, which is enough to sort the array if the size of array is known. Also, you don't need to pass the size of element of array to iSort().
iSort() function implementation:
void iSort(struct info *fs_info, int size) {
for (int i = 1; i < size; ++i) {
int key = fs_info[i].size;
struct info x = fs_info[i];
int j = i - 1;
while (j >= 0 && fs_info[j].size > key) {
fs_info[j + 1] = fs_info[j];
j--;
}
fs_info[j + 1] = x;
}
}
Use memmove() in iSort():
void iSort (struct info *fs_info, int size) {
for (int i = 1; i < size; ++i) {
int key = fs_info[i].size;
struct info x = fs_info[i];
int j = i - 1;
while (j >= 0 && fs_info[j].size > key) {
j--;
}
if (j != i - 1) {
memmove (&fs_info[j + 2], &fs_info[j + 1], sizeof(*fs_info) * (i - j - 1));
}
memmove (&fs_info[j + 1], &x, sizeof(*fs_info));
}
}
Call iSort() function like this:
iSort(fs_info, N);
There is a lot of scope of improvement in your code, like, it would be good to have array of pointers to struct info instead of array of struct info, so that, during sort simply swapping the pointers required instead of swapping whole structure. Leaving it up to you to identify the improvements and implement them.
I'm having trouble getting the last property in a struct.
This is my struct
#define MAX_FILE_NUM 64
#define MAX_FILE_NAME_LENGTH 15
typedef struct
{
char file_name[MAX_FILE_NAME_LENGTH]; //file name
int size; //size of the file
int used; //0 represents the file is not used and 1 represents the file is used
int block; //the index of data block that stores the file
int block_count; //the number of blocks the file used
int file_descriptor_count; //the number of file descriptors are referring to the file
}file_detail;
Assuming the code update the directory info at i = 0
file_detail * directory_block_ptr = (file_detail *)malloc(4096);
int create(char *name){
if(name == NULL || find_file(name) != -1 || strlen(name) > MAX_FILE_NAME_LENGTH || super_block_ptr->files >= MAX_FILE_NUM){
printf("%s\n", "ERROR: file create condition not satified");
return -1;
}
//create the file
for(int i = 0; i < MAX_FILE_NUM; i++){
//update directory info
if((directory_block_ptr+i)->used == 0){
strcpy((directory_block_ptr+i)->file_name, name);
(directory_block_ptr+i)->size = 0;
(directory_block_ptr+i)->file_descriptor_count = 0;
(directory_block_ptr+i)->used = 1;
(directory_block_ptr+i)->block = -1;
(directory_block_ptr+i)->block_count = 0;
printf("%d\n", (directory_block_ptr+i)->file_descriptor_count);//correct output 0
break;
}
}
printf("%d\n", (directory_block_ptr+0)->file_descriptor_count);//incorrect output
//open the file
int result = fs_open(name);
if(result != -1)
{
printf("%s: %s %s\n", "File", name, "created successfully");
return 0;
}
return -1;
}
But when I'm trying to access the file_decsriptor_count outside of the for loop, I get random numbers like 1868958256.
create("test");
I'm going to go out on a limb here and propose that the memory from malloc is uninitialized, and that's what's going on here.
In the loop (and rewriting into proper array format as suggested by #Gerhardh):
for (int i = 0; i < MAX_FILE_NUM; i++) {
//update directory info
if( directory_block_ptr[i].used == 0) {
// assign stuff
printf("%d\n", directory_block_ptr[i].file_descriptor_count);//correct output 0
break;
}
}
The OP says they get the correct value, but we don't know that it's the [0] index, right? If there's junk in position directory_block_ptr[0].used, then that position will not be touched and the loop will move onto the next one. We just don't know which one is being found.
Recommend to OP: change the printf in the loop to be:
printf("%d set in position [%d]\n", directory_block_ptr[i].file_descriptor_count, i);
and see if it's telling you position [0] or [1] or whatever. My guess is not-zero.
Recommend to OP, change your initialization at the top to be:
#define DIRECTORY_BLOCK_SIZE 4096
file_detail *directory_block_ptr = malloc(DIRECTORY_BLOCK_SIZE);
memset(directory_block_ptr, 0, DIRECTORY_BLOCK_SIZE);
This allocates the same amount of memory, but insures that it's all set to zero, then run it again.
Also, this is more a matter of style than of substance, I'd do the loop this way by defining a much simpler variable name for access throughout the loop, it's easier to follow.
for(int i = 0; i < MAX_FILE_NUM; i++){
file_detail *fd = &(directory_block_ptr[i]); // shorthand
//update directory info
if(fd->used == 0){
strcpy(fd->file_name, name);
fd->size = 0;
fd->file_descriptor_count = 0;
fd->used = 1;
fd->block = -1;
fd->block_count = 0;
printf("%d set in position [%d]\n", fd->file_descriptor_count, i);
break;
}
}
I have created a graph structure in C. The structural definition of the graph is given below.
typedef struct _GNODE_ {
int nodeNumber; // vertex/node number
int adjNum; // number of adjacent nodes
struct _GNODE_ **adjacent; // array of pointers to adjacent nodes
} _GNODE_ ;
typedef struct _GRAPH_ {
int vc; // number of vertices
char *name; // graph name
int **AM; // adjacency matrix
_GNODE_ **node; // array of pointer to each vertices
} _GRAPH_ ;
where _GNODE_ is the structure of a node in the graph.
The function readGraphFromTxt(char *fileName , _GRAPH_ *graph) reads a text file containing the adjacency matrix of the graph and call another function called createGraphFromAM(_GRAPH_ *graph) to create a graph structure.
int readGraphFromTxt(char *filename , _GRAPH_ *graph){
FILE *fp;
fp = fopen(filename, "r");
int vc;
int **AM;
char *gname;
if(fp != NULL){
char graphName[100];
fgets(graphName, 99, fp );
gname = (char *)malloc(sizeof(char)*101);
strncpy(gname , graphName, 99);
fgets(graphName, 99, fp );
fscanf(fp , "%d\n" , &vc);
AM = (int **) malloc(sizeof(int*) * vc);
for(int i=0; i<vc; i++){
AM[i] = (int *)malloc(sizeof(int) * vc);
if(fscanf(fp, "%s" , graphName) ==EOF) break;
for(int j=0; j<vc; j++){
AM[i][j] = graphName[j] - '0';
}
}
}
if(AM != NULL) graph->AM = AM;
if(gname != NULL ) graph->name = gname;
graph->vc = vc;
createGraphFromAM(graph);
fclose(fp);
return vc;
}
void createGraphFromAM(_GRAPH_ *graph) {
graph->node = (_GNODE_ **)malloc(sizeof(_GNODE_) * graph->vc) ; // array of pointers to different nodes
for (int i=0; i<graph->vc ; i++){
graph->node[i] = (_GNODE_ *)malloc(sizeof(_GNODE_));
graph->node[i]->adjNum=0;
graph->node[i]->nodeNumber = i; // node number
graph->node[i]->adjacent = (_GNODE_ **)malloc(sizeof(_GNODE_)) ; // because allocating 0 byte is tricky
}
for (int i=0; i<graph->vc ; i++){
for (int j=0; j<graph->vc ; j++){
if(graph->AM[i][j]==1){ // check for adjacency between i and j
graph->node[i]->adjNum++; // if adjacent increment number of adjacent nodes number
graph->node[i]->adjacent = (_GNODE_ **)realloc( graph->node[i]->adjacent , sizeof(_GNODE_) * (graph->node[i]->adjNum+1)); // reallocate the memory to hold new adjacent member
graph->node[i]->adjacent[graph->node[i]->adjNum-1] = graph->node[j]; // points to another node
}
}
graph->node[i]->adjacent[graph->node[i]->adjNum] = NULL; // set last adjacent node to NULL
}
}
The function freeGraph(_GRAPH_ *graph) is supposed to de-allocate all the memory allocated to graph. But while calling this function the programs runs into a SEGMENTATION FAULT.
void freeGraph(_GRAPH_ *graph){
// free graph
for (int i=0; i<graph->vc; i++) {
// free each node data
printf("\nLoop: %d\n", i);
// free each adjacent node
for (int k=0; k<graph->node[i]->adjNum; k++){
if(graph->node[i]->adjacent[k]!=NULL){
free(graph->node[i]->adjacent[k]);
}
}
if(graph->node[i]->adjacent!=NULL) {
free(graph->node[i]->adjacent);
}
free(graph->node[i]);
}
free(graph->node);
for(int i=0; i<graph->vc; i++)
if(graph->AM[i]!=NULL) free(graph->AM[i]);
free(graph->AM);
free(graph->name);
free(graph);
}
The function called printAllGraphNodeNumber(const _GRAPH_ *graph) which prints all the node number and their adjacent nodes number.
void printAllGraphNodeNumber(const _GRAPH_ *graph) {
printf("NODES IN THE GRAPH: ");
for (int i=0; i<graph->vc ; i++) {
printf("\nNode Number : %d\n|-----> Adjacent Nodes: ",graph->node[i]->nodeNumber);
for (int j=0; j<graph->node[i]->adjNum; j++){
printf("%d , ", graph->node[i]->adjacent[j]->nodeNumber );
}
}
}
The content of the sample file "Graph01.txt" is:
And the maximum number of vertices is : 20
one
AM
10
0100010011
1000110100
0000100001
0000000010
0110000100
1100000001
0000000001
0100100010
1001000101
1010011010
Here's an adaptation of your code. I've cleaned up many points that I made in the comments to the question. In particular:
The structure names are removed from the namespace reserved for the implementation.
Operations are checked and errors are reported.
If the file open fails, uninitialized pointers are not freed.
If the file open fails, the code doesn't try to close the unopened file stream.
Because failure to allocate exits the program, there are fewer checks for nullness.
The code counts how many adjacent nodes there are in a row and allocates the correct space all at once, rather than allocating the array one element at a time.
I've preserved the null terminator for the adjacency list, though it isn't really necessary.
By default, the program reads from a file data. You can override that by specifying the file to read on the command line.
I also modified the code so that readGraphFromTxt() returns an allocated pointer, so that freeGraph() can correctly free it.
The main fix is to ensure that each malloc() is freed once. As you diagnosed, you were trying to free the nodes pointed at by the adjacency lists, which was incorrect behaviour. Those nodes should be freed en masse since they were allocated en masse.
I've tidied up the printing (at least according to my standards).
I used the files stderr.c and stderr.h available from GitHub to simplify the error reporting. When error reporting is simple, it is less likely to be omitted. I've cheated and used assert() in a couple of places instead od a formal error test and message.
Code
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stderr.h"
typedef struct Node
{
int nodeNumber;
int adjNum;
struct Node **adjacent;
} Node;
typedef struct Graph
{
int vc;
char *name;
int **AM;
Node **node;
} Graph;
static void createGraphFromAM(Graph *graph);
static Graph *readGraphFromTxt(const char *filename)
{
FILE *fp = fopen(filename, "r");
if (fp == 0)
err_syserr("failed to open file %s for reading\n", filename);
Graph *graph = malloc(sizeof(*graph));
if (graph == 0)
err_syserr("failed to allocate %zu bytes\n", sizeof(*graph));
char line[100];
if (fgets(line, sizeof(line), fp) == 0)
err_error("premature EOF - no graph name\n");
line[strcspn(line, "\n")] = '\0';
char *gname = strdup(line);
if (fgets(line, sizeof(line), fp) == 0)
err_error("premature EOF - no auxilliary info\n");
int vc = -1;
if (fscanf(fp, "%d", &vc) != 1)
err_error("format error: didn't get an integer\n");
if (vc < 1 || vc > 20)
err_error("size of graph out of control: %d\n", vc);
int **AM = (int **)malloc(sizeof(int *) * vc);
if (AM == 0)
err_syserr("failed to allocate %zu bytes\n", sizeof(int *) * vc);
for (int i = 0; i < vc; i++)
{
AM[i] = (int *)malloc(sizeof(int) * vc);
if (AM[i] == 0)
err_syserr("failed to allocate %zu bytes\n", sizeof(int) * vc);
if (fscanf(fp, "%s", line) == EOF)
err_error("premature EOF - not enough lines of data for the adjacency matrix\n");
if (strlen(line) < (size_t)vc)
err_error("Adjacency matrix line is too short (got [%s] (%zu); wanted %d)\n",
line, strlen(line), vc);
for (int j = 0; j < vc; j++)
{
assert(line[j] == '0' || line[j] == '1');
AM[i][j] = line[j] - '0';
}
}
graph->AM = AM;
graph->name = gname;
graph->vc = vc;
graph->node = 0;
createGraphFromAM(graph);
fclose(fp);
return graph;
}
/* How many times does val appear in array arr of size num? */
static inline size_t val_count(size_t num, const int arr[num], int val)
{
assert(arr != 0);
size_t count = 0;
for (const int *end = arr + num; arr < end; arr++)
{
if (*arr == val)
count++;
}
return count;
}
static void createGraphFromAM(Graph *graph)
{
graph->node = (Node **)malloc(sizeof(*graph->node) * graph->vc);
if (graph->node == 0)
err_syserr("failed to allocate %zu bytes\n", sizeof(*graph->node) * graph->vc);
for (int i = 0; i < graph->vc; i++)
{
graph->node[i] = (Node *)malloc(sizeof(Node));
if (graph->node[i] == 0)
err_syserr("failed to allocate %zu bytes\n", sizeof(Node));
graph->node[i]->adjNum = val_count(graph->vc, graph->AM[i], 1);
graph->node[i]->nodeNumber = i;
size_t adj_size = sizeof(Node *) * (graph->node[i]->adjNum + 1);
graph->node[i]->adjacent = (Node **)malloc(adj_size);
if (graph->node[i]->adjacent == 0)
err_syserr("failed to allocate %zu bytes\n", adj_size);
}
for (int i = 0; i < graph->vc; i++)
{
Node *node = graph->node[i];
int adj = 0;
for (int j = 0; j < graph->vc; j++)
{
if (graph->AM[i][j] == 1)
node->adjacent[adj++] = graph->node[j];
}
node->adjacent[node->adjNum] = NULL;
}
}
static void freeGraph(Graph *graph)
{
for (int i = 0; i < graph->vc; i++)
{
free(graph->node[i]->adjacent);
free(graph->node[i]);
}
free(graph->node);
for (int i = 0; i < graph->vc; i++)
free(graph->AM[i]);
free(graph->AM);
free(graph->name);
free(graph);
}
static void printAllGraphNodeNumber(const Graph *graph)
{
assert(graph != 0);
printf("Nodes in the graph %s: %d\n", graph->name, graph->vc);
for (int i = 0; i < graph->vc; i++)
{
printf("Node: %d - Adjacent Nodes: ", graph->node[i]->nodeNumber);
const char *pad = "";
for (int j = 0; j < graph->node[i]->adjNum; j++)
{
printf("%s%d", pad, graph->node[i]->adjacent[j]->nodeNumber);
pad = ", ";
}
putchar('\n');
}
}
int main(int argc, char **argv)
{
err_setarg0(argv[0]);
const char *filename = "data";
if (argc == 2)
filename = argv[1];
Graph *graph = readGraphFromTxt(filename);
if (graph != 0)
{
printAllGraphNodeNumber(graph);
freeGraph(graph);
}
return 0;
}
Data
A copy of what was supplied in the question:
one
AM
10
0100010011
1000110100
0000100001
0000000010
0110000100
1100000001
0000000001
0100100010
1001000101
1010011010
Output
Nodes in the graph one: 10
Node: 0 - Adjacent Nodes: 1, 5, 8, 9
Node: 1 - Adjacent Nodes: 0, 4, 5, 7
Node: 2 - Adjacent Nodes: 4, 9
Node: 3 - Adjacent Nodes: 8
Node: 4 - Adjacent Nodes: 1, 2, 7
Node: 5 - Adjacent Nodes: 0, 1, 9
Node: 6 - Adjacent Nodes: 9
Node: 7 - Adjacent Nodes: 1, 4, 8
Node: 8 - Adjacent Nodes: 0, 3, 7, 9
Node: 9 - Adjacent Nodes: 0, 2, 5, 6, 8
Having upgraded to macOS High Sierra 10.13, I am (once again) without Valgrind. However, several other tools reassure me that this is probably correct.
I solved this problem by removing the piece of code commented as free each adjacent node in the function freeGraph(). Resulting code looks like:
void freeGraph(_GRAPH_ *graph){
// free graph
for (int i=0; i<graph->vc; i++) {
// free each node data
printf("\nLoop: %d\n", i);
// free each adjacent node
/*
for (int k=0; k<graph->node[i]->adjNum; k++){
if(graph->node[i]->adjacent[k]!=NULL){
free(graph->node[i]->adjacent[k]);
}
}*/
if(graph->node[i]->adjacent!=NULL) {
free(graph->node[i]->adjacent);
}
free(graph->node[i]);
}
free(graph->node);
for(int i=0; i<graph->vc; i++)
if(graph->AM[i]!=NULL) free(graph->AM[i]);
free(graph->AM);
free(graph->name);
// commenting this out : because this is not dynamically allocated
// free(graph);
}
The reason why I did this is because graph->node[i]->adjacent[k] are the pointers to different nodes of the graph which are going to be freed at last by free(graph->node[i]).
I checked using Valgrind, its working perfectly. Stats showing a clean bill of health. All allocations are freed. I think I am good to go.
PS: I still don't think its an optimal solution. But this is how I solved my problem. Hope anybody else can provide a better answer.
I'm trying to make a file system in C. I have trouble with this portion of my code when I'm printing my values in the code below:
for (int i = 0; i<NUM_POINTERS; i++) {
printf("before SB->root[%d]=%d\n", i, SB->root->pointers[i]);
}
write_blocks(0, 1, SB);
for (int i = 0; i<NUM_POINTERS; i++) {
printf("after SB->root[%d]=%d\n", i, SB->root->pointers[i]);
}
my write_blocks method:
int write_blocks(int start_address, int nblocks, void *buffer)
{
int i, e, s;
e = 0;
s = 0;
void* blockWrite = (void*) malloc(BLOCK_SIZE);
/*Checks that the data requested is within the range of addresses of the disk*/
if (start_address + nblocks > MAX_BLOCK)
{
printf("out of bound error\n");
return -1;
}
/*Goto where the data is to be written on the disk*/
fseek(fp, start_address * BLOCK_SIZE, SEEK_SET);
/*For every block requested*/
for (i = 0; i < nblocks; ++i)
{
/*Pause until the latency duration is elapsed*/
usleep(L);
memcpy(blockWrite, buffer+(i*BLOCK_SIZE), BLOCK_SIZE);
fwrite(blockWrite, BLOCK_SIZE, 1, fp);
fflush(fp);
s++;
}
free(blockWrite);
/*If no failure return the number of blocks written, else return the negative number of failures*/
if (e == 0)
return s;
else
return e;
}
And here's what gets printed:
before SB->root[0]=1
before SB->root[1]=2
before SB->root[2]=3
before SB->root[3]=4
before SB->root[4]=5
before SB->root[5]=6
before SB->root[6]=7
before SB->root[7]=8
before SB->root[8]=9
before SB->root[9]=10
before SB->root[10]=11
before SB->root[11]=12
before SB->root[12]=13
before SB->root[13]=14
after SB->root[0]=1234344888
after SB->root[1]=32688
after SB->root[2]=3
after SB->root[3]=4
after SB->root[4]=5
after SB->root[5]=6
after SB->root[6]=7
after SB->root[7]=8
after SB->root[8]=9
after SB->root[9]=10
after SB->root[10]=11
after SB->root[11]=12
after SB->root[12]=13
after SB->root[13]=14
I don't understand why my first and second pointer value change?
Some additional information: SB is a superBlock here's my structures:
typedef struct iNode
{
int id;
int size;
int pointers[NUM_POINTERS];
} iNode;
typedef struct superBlock
{
int magic_number;
int block_size;
int num_blocks;
int num_inodes;
iNode *root;
iNode jNodes[20];
} superBlock;
Is this single threaded?
Does the modified SB->root[0,1] contain the data you are trying to write?
What is your BLOCK_SIZE?
I suspect the problem is outside of write_blocks(). My best guess would be that you accidentally freed SB somewhere and malloc gave you the same address. After the malloc check (print or debugger) both buffer and blockWrite and make sure they are different and valid.
Unrelated Issues:
printf has more % than params
You should check the return of malloc
e is never set
s and i are equal. AKA redundant.
Out of bounds error causes a memory leak (since it is after the malloc)
usleep is strange perhaps you want fsync?
I am trying to write a Huffman encoding program to compress a text file. Upon completetion, the program will terminate at the return statement, or when I attempt to close a file I was reading from. I assume I have memory leaks, but I cannot find them. If you can spot them, let me know (and a method for fixing them would be appreciated!).
(note: small1.txt is any standard text file)
Here is the main program
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ASCII 255
struct link {
int freq;
char ch[ASCII];
struct link* right;
struct link* left;
};
typedef struct link node;
typedef char * string;
FILE * ofp;
FILE * ifp;
int writebit(unsigned char);
void sort(node *[], int);
node* create(char[], int);
void sright(node *[], int);
void Assign_Code(node*, int[], int, string *);
void Delete_Tree(node *);
int main(int argc, char *argv[]) {
//Hard-coded variables
//Counters
int a, b, c = 0;
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char*));
int *value = (int*) malloc(ASCII * sizeof(int*));
//File pointers
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "can't open %s\n", argv[1]);
return 0;
}
//Nodes
node* ptr;//, *head;
node* array[ASCII];
//
int u, carray[ASCII];
char str[ASCII];
//Variables
char car = 0;
int inList = 0;
int placeinList = -1;
int numofKeys;
if (argc < 2) {
printf("Usage: huff <.txt file> \n");
return 0;
}
for (a = 0; a < ASCII; a++) {
key[a] = -1;
value[a] = 0;
}
car = fgetc(fp);
while (!feof(fp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
inList = 1;
placeinList = a;
}
}
if (inList) {
//increment value array
value[placeinList]++;
inList = 0;
} else {
for (b = 0; b < ASCII; b++) {
if (key[b] == -1) {
key[b] = car;
break;
}
}
}
car = fgetc(fp);
}
fclose(fp);
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c;
c++;
}
}
string code_string[numofKeys];
while (numofKeys > 1) {
sort(array, numofKeys);
u = array[0]->freq + array[1]->freq;
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
ptr = create(str, u);
ptr->right = array[1];
ptr->left = array[0];
array[0] = ptr;
sright(array, numofKeys);
numofKeys--;
}
Assign_Code(array[0], carray, 0, code_string);
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
car = fgetc(ifp);
while (!feof(ifp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
for (b = 0; b < strlen(code_string[a]); b++) {
if (code_string[a][b] == 48) {
writebit(0);
} else if (code_string[a][b] == 49) {
writebit(1);
}
}
}
}
car = fgetc(ifp);
}
writebit(255);
fclose(ofp);
ifp = fopen("small1.txt", "r");
fclose(ifp);
free(key);
//free(value);
//free(code_string);
printf("here1\n");
return 0;
}
int writebit(unsigned char bitval) {
static unsigned char bitstogo = 8;
static unsigned char x = 0;
if ((bitval == 0) || (bitval == 1)) {
if (bitstogo == 0) {
fputc(x, ofp);
x = 0;
bitstogo = 8;
}
x = (x << 1) | bitval;
bitstogo--;
} else {
x = (x << bitstogo);
fputc(x, ofp);
}
return 0;
}
void Assign_Code(node* tree, int c[], int n, string * s) {
int i;
static int cnt = 0;
string buf = malloc(ASCII);
if ((tree->left == NULL) && (tree->right == NULL)) {
for (i = 0; i < n; i++) {
sprintf(buf, "%s%d", buf, c[i]);
}
s[cnt] = buf;
cnt++;
} else {
c[n] = 1;
n++;
Assign_Code(tree->left, c, n, s);
c[n - 1] = 0;
Assign_Code(tree->right, c, n, s);
}
}
node* create(char a[], int x) {
node* ptr;
ptr = (node *) malloc(sizeof(node));
ptr->freq = x;
strcpy(ptr->ch, a);
ptr->right = ptr->left = NULL;
return (ptr);
}
void sort(node* a[], int n) {
int i, j;
node* temp;
for (i = 0; i < n - 1; i++)
for (j = i; j < n; j++)
if (a[i]->freq > a[j]->freq) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
void sright(node* a[], int n) {
int i;
for (i = 1; i < n - 1; i++)
a[i] = a[i + 1];
}
If your program is crashing on what is otherwise a valid operation (like returning from a function or closing a file), I'll near-guarantee it's a buffer overflow problem rather than a memory leak.
Memory leaks just generally mean your mallocs will eventually fail, they do not mean that other operations will be affected. A buffer overflow of an item on the stack (for example) will most likely corrupt other items on the stack near it (such as a file handle variable or the return address from main).
Probably your best bet initially is to set up a conditional breakpoint on writes to the file handles. This should happen in the calls to fopen and nowhere else. If you detect a write after the fopen calls are finished, that will be where your problem occurred, so just examine the stack and the executing line to find out why.
Your first problem (this is not necessarily the only one) lies here:
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c; // DANGER,
c++; // WILL ROBINSON !!
}
}
string code_string[numofKeys];
You can see that you set the number of keys before you increment c. That means the number of keys is one less than you actually need so that, when you access the last element of code_string, you're actually accessing something else (which is unlikely to be a valid pointer).
Swap the numofKeys = c; and c++; around. When I do that, I at least get to the bit printing here1 and exit without a core dump. I can't vouch for the correctness of the rest of your code but this solves the segmentation violation so anything else should probably go in your next question (if need be).
I can see one problem:
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
the ch field of struct link is a char array of size 255. It is not NUL terminated. So you cannot copy it using strcpy.
Also you have:
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
If small1.txt.huff does not exist, it will be created. But if small1.txt it will not be created and fopen will return NULL, you must check the return value of fopen before you go and read from the file.
Just from counting, you have 4 separate malloc calls, but only one free call.
I would also be wary of your sprintf call, and how you are actually mallocing.
You do an sprintf(buf, "%s%d", buf, c[i]) but that can potentially be a buffer overflow if your final string is longer than ASCII bytes.
I advise you to step through with a debugger to see where it's throwing a segmentation fault, and then debug from there.
i compiled the program and ran it with it's source as that small1.txt file and got "can't open (null)" if the file doesn't exist or the file exist and you give it on the command like ./huf small1.txt the program crashes with:
Program terminated with signal 11, Segmentation fault.
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
195 if (a[i]->freq > a[j]->freq) {
(gdb) backtrace
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
#1 0x080489ba in main (argc=2, argv=0xbfd79b64) at huf.c:99
to get this from gdb you run
ulimit -c 100000000
./huf
gdb --core=./core ./huf
and type backtrace
You have various problems in your Code:
1.- mallocs (must be):
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char));
int *value = (int*) malloc(ASCII * sizeof(int));
sizeof(char) == 1, sizeof(char *) == 4 or 8 (if 64 bits compiler is used).
2.- Buffer sizes 255 (ASCII) is too short to receive the contents of array[0]->ch + array[1]->ch + '\0'.
3.- Use strncpy instead of strcpy and strncat instead of strcat.
4.- key is an array of individuals chars or is a null terminated string ?, because you are using this variable in both ways in your code. In the characters counting loop you are using this variables as array of individuals chars, but in the creation of nodes you are passing the pointer of the array and copying as null terminated array.
5.- Finally always check your parameters before used it, you are checking if argc < 2 after trying to open argv[1].