Say, I have code like this:
int foo() {
...
int buff_size;
scanf("%d", &buff_size);
FILE * fp = fopen("./file", "a+");
char *buff = malloc(buff_size*sizeof(char));
char *buff2 = malloc(buff_size*sizeof(char));
char *buff3 = malloc(buff_size*sizeof(char));
while (!feof(fp)) {
/*do something, say read, write etc.*/
if (/*error case 1*/) {
free(buff);
free(buff1);
free(buff3);
fclose(fp);
return -1;
}
else if (/*error case 2*/) {
free(buff);
free(buff1);
free(buff3);
fclose(fp);
return -2;
}
...
else if (/*error case k*/) {
free(buff);
free(buff1);
free(buff3);
fclose(fp);
return -k;
}
}
...
free(buff);
free(buff1);
free(buff2);
fclose(fp);
return 0;
}
for C doesn't provide try...throw...finally syntax, I have to close file descriptors and free heap memories pointer that I created before I return a error integer code. It produces some duplicated code that makes the code ugly.
Do anyone know how should I modifly this kind of code to make it looks more brief?
Your code, as written is equivalent to
FILE * fp = fopen("./file", "a+");
int return_value = 0; /* assume zero means no error */
char *buff = malloc(1024*sizeof(char));
char *buff2 = malloc(1024*sizeof(char));
char *buff3 = malloc(1024*sizeof(char));
while (feof(fp))
{
/*do something, say read, write etc.*/
if (/*error case 1*/)
{
return_value = -1;
}
else if (/*error case 2*/)
{
return_value = -2;
}
// ...
else if (/*error case k*/)
{
return_value = -k;
}
}
if (return_value == 0) /* if no error has occurred, we can still do stuff */
{
...
}
/* I have assumed all buffers need to be released
and files closed as the function returns
*/
free(buff);
free(buff1); /* you probably intend buff2 and buff3 here, to match the malloc() calls */
free(buff2);
fclose(fp);
return return_value;
}
I have not, however, addressed two critical errors in your code. You need to.
fopen() can fail, and return NULL. If it does, passing it to feof() or to fclose() gives undefined behaviour. Your code is not checking that at all, and needs to both before call of feof() and fclose().
A loop of the form while (!feof(fp)) {read_something_from(fp); use_something();} is a bad idea. Have a look at Why is “while ( !feof (file) )” always wrong? for more info.
Less critical - for fixed size arrays (size fixed at compile time) you probably don't need to use malloc() which also means you don't need to use free(). However, you still need to deal with potential failures of fopen().
For this sort of thing with common cleanup code, I'd use a return value variable and a goto:
int foo() {
...
// append extended mode ... what are you doing with this?
FILE * fp = fopen("./file", "a+");
char buff[1024];
char buff2[1024];
char buff3[1024];
int ret = 0;
while (/* file I/O is valid on fp */) {
/*do something, say read, write etc.*/
if (/*error case 1*/) {
ret = -1;
goto cleanup;
}
else if (/*error case 2*/) {
ret = -2;
goto cleanup;
}
...
else if (/*error case k*/) {
ret = -k;
goto cleanup;
}
}
...
cleanup:
free(buff);
free(buff2);
free(buff3);
fclose(fp);
return ret;
}
This also prevents having to copy-paste code, or retyping with errors (as your current code has).
Related
I have got the following code:
#include <stdio.h>
#include "config.h"
#include <errno.h>
char buffer[50];
long long bufSize = 50;
FILE* fptr;
char* readConfig(char* buffer, size_t bufSize) {
fptr = fopen("config.txt", "rt");
if (fptr == NULL) {
return "error opening config file: %s", strerror(errno);
} else {
if ((fgets(buffer, bufSize, fptr)) == NULL) {
fclose(fptr);
return "error reading config file: %s", strerror(errno);
}
else {
fclose(fptr);
return buffer;
}
}
}
For test purposes I deleted the config.txt file so that the fopen() function should return NULL.
What wonders me is that fopen("config.txt", "rt"); fails, but when debugging the code it just skips the "if (fptr == NULL) {...}" part and directly jumps out of the function.
When going on with the debugging process, I get the following error when trying to work with the return Value of readConfig() "0xC0000005: Access violation reading location 0xFFFFFFFFA4E0EB70"
Cannot compile your code as you shared a snippet (no main()) and didn't include config.h.
#include <string.h> for strerror().
Suggest caller passes in local variables instead of global variables.
Instead of hard-coding the size in both buffer[50] and bufSize = 50; use sizeof to determine the size of the array. The other good alternative is to define a constant.
The fopen() mode "t" isn't standard, so either leave it out or tag your program with windows or whatever.
As you return on error, eliminate the unnecessary else & indentation.
The expression return "error opening config file: %s", strerror(errno); doesn't work the way you expect, it will evaluate the first part in void context then return the 2nd part strerror(errno). I was not able to otherwise reproduce any ill effects.
fgets() return NULL on eof or error but not appear to set errno. You can use feof() and/or ferror() to tell which if needed.
After the call to fgets() you call fclose() prior to inspecting errno, so it have the status of the fclose() call instead.
It's a bad design to return either error message or the value you read from the file as you cannot tell them apart. Changed to return NULL on success.
#include <errno.h>
#include <stdio.h>
#include <string.h>
char *readConfig(char *buffer, size_t bufSize) {
FILE* fptr = fopen("config.txt", "r");
if(!fptr)
return strerror(errno);
if(!fgets(buffer, bufSize, fptr)) {
fclose(fptr);
return "fgets eof/error";
}
fclose(fptr);
return NULL;
}
int main(void) {
char b[50];
const char *error = readConfig(b, sizeof b);
if(error) {
printf("error: %s\n", error);
return 1;
}
printf("%s", b);
}
Consider having caller open the file and pass in the FILE *. It gives you the flexibility, for instance, to use stdin as the file handle.
I prefer using goto instead of the multiple returns when resource cleanup is required. It's a bit more verbose here but each error case is handled the same way. When you swap the arguments you can document how they are related with recent compilers:
char *readConfig(size_t bufSize, char buffer[bufSize]) {
char *r = NULL;
FILE* fptr = fopen("config.txt", "r");
if(!fptr) {
r = strerror(errno);
goto out;
}
if(!fgets(buffer, bufSize, fptr)) {
r = "fgets eof/error";
goto out;
}
out:
fptr && fclose(fptr);
return r;
}
char *wcat(char *str, size_t n, FILE *fp){
if (fp == NULL) {
printf("wcat cannot open file\n");
fclose(fp);
perror("File cannot be opened");
return NULL;
exit(1);
}else{
if ((str = fgets(str,n,fp)) != NULL){
printf("%s",str);
return str;
exit(0);
}
}
}
Terminal:
gcc -o wcat wcat.c
Error: wcat.c:36:1: warning: control may reach end of non-void function [-Wreturn-type]
The fp already equals fopen(...).
I am not sure why this is happening. I wanted to create this wcat file to work like:
./wcat file1.c file2.c
Your else clause also needs an else, or at least a default return. Your ifs don't cover every possible case. The warning says exactly what the problem is.
char *wcat(char *str, size_t n, FILE *fp){
if (fp == NULL) {
printf("wcat cannot open file\n");
fclose(fp);
perror("File cannot be opened");
return NULL;
//exit(1);
}
else if (fgets(str,n,fp))
{
printf("%s",str);
return str;
// exit(0);
}
return NULL; /// or whatever it is that you expect to happen here.
}
Neither of the calls to exit makes sense. They'll never be executed. It looks like you're trying to use those to return some sort of success/failure flag, but:
they never execute because they follow a return
exit terminates the program.
The parameter is passed back to the calling process. In my experience, this is basically never used unless you're writing a console utility.
Do you really understand what exit does? And return?
There's a lot wrong with this one. I suggest stepping through in your debugger.
the following changes, with comments, is the correct way to handle this function:
char *wcat(char *str, size_t n, FILE *fp){
// note: this check should have been handled
// where/when 'fopen()' was called
if (fp == NULL) {
// this changes the value in 'errno'
// so 'perror() will not display the right message
//printf("wcat cannot open file\n");
//fclose(fp); // <-- never open'd so cannot close
perror("File cannot be opened");
return NULL;
//exit(1); // <-- will never be executed
}else{
if (fgets(str,n,fp)){
printf("%s",str);
return str;
//exit(0); // <-- will never be executed
}
return NULL; // <-- need to handle when 'fgets()' fails
}
}
after applying the corrections AND moving the check for a failure of 'fopen()', the code would look like the following:
char *wcat(char *str, size_t n, FILE *fp)
{
if ( fgets( str, n, fp ) )
{
printf("%s",str);
return str;
}
return NULL;
}
I'm new in this forum and i hope to do it right.
My problem is the following:
I'm writing a program that has this function:
void* s_malloc(int _size, int dim){
printf("%d %d\n", _size, dim);
char* pointer;
pointer = (char*)malloc(_size * dim);
printf("Malloc eseguita \n");
if(pointer == NULL){
ExitFailure(strerror(errno));
}else{
for(int i = 0; i < dim * _size; i++){
pointer[i] = '\0';
}
printf("Allocata\n");
return (void*)pointer;
}
return (void*) NULL;
}
I tested this function a lot ( In the truth i wrote this function a month ago and since that moment i have used it a lot ). I'm pretty sure that the function works fine.
However in this other function i use 's_malloc' and it crash
#define LDTOCAT "C:\\Users\\admin\\logdrstrace\\"
void startProcess(){
char* logfile, *logfilefullpath;
int run = TRUE, lenth = 0;
/*
for(int i = 0; i < 16; i ++){
s_malloc(60, 1);
}*/ commented
while(run){
logfile = checkLogFileDirectory();
Sleep(1000);
#ifdef DEBUG_MODE
printf("\nValue returned: %s\n", logfile);
fflush(stdout);
#endif // DEBUG_MODE
if(strcmp(logfile, NOFILEFOUND) != 0){
run = FALSE;
}
}
lenth = (strlen(LDTOCAT)+strlen(logfile)+1);
#ifdef DEBUG_MODE
printf("Let's go forward allocating memory: size %d\n", lenth);
printf("LDTOCAT size: %d, logfile size: %d + 1\n", strlen(LDTOCAT), strlen(logfile));
fflush(stdout);
#endif // DEBUG_MODE
logfilefullpath =(char*) s_malloc(lenth, sizeof(char));
#ifdef DEBUG_MODE
printf("Memory created!\n");
fflush(stdout);
#endif // DEBUG_MODE
memcpy(logfilefullpath, LDTOCAT, sizeof(LDTOCAT));
#ifdef DEBUG_MODE
printf("Created logfilefullpath with directory: %s\n", logfilefullpath);
fflush(stdout);
printf("File found: %s\n", strcat(logfilefullpath, logfile));
#endif // DEBUG_MODE
int fd = open(strcat(LDTOCAT, logfile), O_RDONLY);
if(fd <= OK){
ExitFailure(strerror(errno));
}
}
As you can see there is a for loop at the begginning commented. Now we have three possibility:
1) If i compile and run the program it fails in s_malloc function when it executes the command 'loginfulpath = (char*)s_malloc(lenth, sizeof(char))' it enters in s_malloc and crash when it calls the original malloc. Sometimes the errno varible is setted on "Not enough memory" but sometimes crashes and stop.
2) if i decomment the loop at the begginning and the stop condition is 'i<15' it crashes like in the point one
3)if i put the stop condition 'i<16', magically, it works fine.
there is a third function that opens a directory and looks for a file,
infact as you can see in the code it avoids the first 2 files found becouse
they are . and ..
#define NOFILEFOUND "Nothing"
char* checkLogFileDirectory(){
HANDLE hfile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fdata, nullValue;
char* file = NULL;
int counter=0;
while(hfile == INVALID_HANDLE_VALUE){
hfile = FindFirstFileA(LOGDIR, &nullValue);
}
#ifdef DEBUG_MODE
printf("-FileFound: %s\n", nullValue.cFileName);
#endif // DEBUG_MODE
while(FindNextFile(hfile, &fdata) && counter != 2){
#ifdef DEBUG_MODE
printf("-File found: %s\n", fdata.cFileName);
#endif // DEBUG_MODE
if(counter == 1){
#ifdef DEBUG_MODE
printf("Third file it's my file\n");
#endif // DEBUG_MODE
file = s_malloc(strlen(fdata.cFileName), sizeof(char));
memcpy(file, fdata.cFileName, strlen(fdata.cFileName)+1);
if(file == NULL){
ExitFailure("Error reading file");
}
file[strlen(fdata.cFileName)] = '\0';
}
counter ++;
}
#ifdef DEBUG_MODE
printf("\n\=>File selected: %s", file==NULL | file == "" ? NOFILEFOUND:file);
#endif // DEBUG_MODE
return counter == 2? file : NOFILEFOUND;
}
NB: I printed the vaule of any varibles and they were correct,
I'm on Virtual machine with vmware workstation.
I'm sorry for my english i hope you will able to understand it
This is the output when it crashes in cases 1 or 2
-FileFound: .
-File found: ..
-File found: drstrace.calc.exe.00532.0000.log
Third file it's my file
32 1
Malloc eseguita
Allocata
=>File selected: drstrace.calc.exe.00532.0000.log
Value returned: drstrace.calc.exe.00532.0000.log
Let's go forward allocating memory: size 60
LDTOCAT size: 27, logfile size: 32 + 1
60 1
Malloc eseguita
Error: Not enough space!!
Thank you!!
Your allocation function is okay (it's a poor man's version of calloc with error checking, maybe using calloc would save some manual code and would be more efficient)
But in checkLogFileDirectory this code is wrong:
file = s_malloc(strlen(fdata.cFileName), sizeof(char));
memcpy(file, fdata.cFileName, strlen(fdata.cFileName)+1);
there's not enough space for file (nul-terminating char). You're missing 1 byte, which can corrupt the rest of your application by undefined behaviour (allocating 15 or 16 useless blocks sometimes makes it "work" because it changes the memory layout).
Why not just do:
file = strdup(fdata.cFileName);
Also, as noted in comments, if you reach a further point of your program you'll have a big issue here:
int fd = open(strcat(LDTOCAT, logfile), O_RDONLY);
LDTOCAT is a string literal, you cannot apply strcat on it. And even if you could, you wouldn't have enough room.
HANDLE hfile = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fdata, nullValue;
while(hfile == INVALID_HANDLE_VALUE)
{
hfile = FindFirstFileA(LOGDIR, &nullValue);
}
Note that this loop will run forever if something is wrong. Then you put the whole function in another while loop.
If the function call is valid then you have success the first time, otherwise you cannot fix a problem with brute force.
You are not closing hfile. You have many more errors in this section. There are plenty of examples and documentation for FindFirstFile you can consult.
You already have strcat, you should have strcpy too. strcpy will add null terminator at the end of the string. Just use the standard malloc and strcpy
You have to free the malloc allocation, so it's simpler if your function always returns a pointer and NULL on failure.
char* checkLogFileDirectory()
{
char* file = NULL;
WIN32_FIND_DATA fdata;
HANDLE hfile = FindFirstFile("C:\\Users\\admin\\logdrstrace\\*.log", &fdata);
if(hfile != INVALID_HANDLE_VALUE)
{
do
{
if(strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0)
continue;
int len = strlen(fdata.cFileName);
file = malloc(len + 1);
strcpy(file, fdata.cFileName);
break;
} while(FindNextFile(hfile, &fdata));
FindClose(hfile);
}
return file;
}
int main()
{
char *file = checkLogFileDirectory();
if(file) {
printf("%s\n", file);
free(file);
}
return 0;
}
I'm using the following code to read a binary file and use a struct to output the data. However, I know my data consist of only one record, and it seems to be printing out many records. I'm wondering why this might be?
FILE *p;
struct myStruct x;
p=fopen("myfile","rb");
//printf("Rcords of file:\n");
while(1)
{
fread(&x,sizeof(x),1,p);
if(feof(p)!=0)
break;
printf("\n\nID:%ld",x.ID);
}
fclose(p);
return 0;
The struct is fairly normal like this:
struct myStruct
{
int ID;
char name[100];
}
Use %d instead of %ld to print an int
And take a look to Why is “while ( !feof (file) )” always wrong?
A struct haves a fixed size, you can use ftell to get the size of the file and then divide using the size of the struct in order to get the number of records, also, always check the result of those functions.
Something like:
FILE *file;
long size;
size_t count, records;
file = fopen("myfile", "rb");
if (file == NULL) {
perror("fopen");
return 0;
}
if (fseek(file, 0, SEEK_END) == -1) {
perror("fseek");
return 0;
}
size = ftell(file);
if (size == -1) {
perror("ftell");
return 0;
}
if (fseek(file, 0, SEEK_SET) == -1) {
perror("fseek");
return 0;
}
records = size / sizeof(x);
for (count = 0; count < records; count++) {
if (fread(&x, sizeof(x), 1, file) == 1) {
printf("\n\nID:%d",x.ID); /* %d instead of %ld */
} else {
break;
}
}
But notice that you are always writing to the same variable on the stack.
EDIT:
How do you store the struct in the file?
I'm not storing it, a program is.
If it is not yours (you don't build the file using the same struct) then you can't know which is the sizeof(x) inside the file, read about structure padding and packing.
Use more protection. Test the result of functions.
FILE *p;
struct myStruct x;
p=fopen("myfile","rb");
assert(p); // Insure file opened
while(1) {
size_t n = fread(&x, sizeof(x), 1, p);
// feof() is insufficient,
// fread() can fail due to input errors too and not set end-of-file condition
// if(feof(p)!=0)
if (n == 0) {
break;
}
// printf("\n\nID:%ld",x.ID);
printf("\n\nID:%d", x.ID); // Use matching specifier
fflush(stdout); // Insure output occurs promptly
}
fclose(p);
return 0;
Since OP's code had a mis-matched printf specifier, it indicates that either warnings are not fully enabled or OP is using a weak compiler. Suggest fixing that to save time.
I am using fscanf() function to read data line by line from a text file. It was functioning fine but suddenly I don't know what mistake I made and now the function returns a negative value.
below is my code snippet:
FILE *fp;
char ip[16];
int port;
fp = fopen("ClientInformation.txt", "r");
int size = -1;
while (!feof(fp))
{
fgetc(fp);
size++;
}
char buff[1000];
sprintf(buff,"%i",size);
MessageBox(NULL,
buff,
"Size",
MB_ICONINFORMATION);
if(size > 0)
{
while (fscanf(fp, " %s %d", ip, &port) > 0)
{
MessageBox(NULL,"fscanf() Successful","SUCCESS!", MB_ICONINFORMATION);
}
}
You might like to add this call
rewind(fp);
just before
while (fscanf(fp, " %s %d", ip, &port) > 0)
{
Also one should always check the result of system calls. In your case mainly whether fopen() really did return something different from NULL.
Addtionally the while(!feof(fp)) construct mostly likely wouldn't always behave as expected (Why is “while ( !feof (file) )” always wrong?). You'd be better off going the way proposed by WhozCraig in the comment(s) below.
You don't need to determine the file size in advance. Just fscanf() the file and check the return value:
int ret;
while ((ret = fscanf(fp, " %s %d", ip, &port)) == 2) {
MessageBox(NULL,"fscanf() Successful","SUCCESS!", MB_ICONINFORMATION);
}
switch (ret) {
case EOF:
/* EOF or error, check errno */
break;
case 0:
case 1:
/* bogus file contents */
break;
default:
fprintf(stderr, "Philip says this cannot happen, but it did.\n");
exit(EXIT_FAILURE);
}
Also: always check the return value of function calls.