0

I'm trying to allocate memory in a function in C, then process the memory in main() and other functions. I have Visual Studio 2022 running on a Windows 10 platform. I found relevant information at the link C Programming: malloc() inside another function but could not get anything to work correctly.

Basically what I'm trying to do is to read a BMP image file into two buffers, one holding the header which is fixed, and one holding the pixel data which I want to manipulate. After the pixel data have been manipulated, I want to write both buffers to a new output file.

I have the following setup: In the header a couple of defines are:

#define FNAME_LEN 31 // Length of filename  
#define IMG_HEADR 54 // Length of image header

In the main function I want to copy the header into buf1, which will not be changed, and the pixel data in buf2 which will be modified. The code is as follows:

printf("Type the input filename:");
char imgFn[FNAME_LEN] = { 0 };
scanf_s("%s",imgFn, FNAME_LEN);
BYTE **buf1, **buf2;
buf1 = buf2 = NULL; // Is this necessary?
int numBytes1 = readBinDiv(imgFn, FNAME_LEN, IMG_HEADER, &buf1, &buf2);
printf("%s%d\n", "numBytes1: ", numBytes1);
if (buf1 == NULL) printf("buf1 is NULL\n"); // Debug
if (buf2 == NULL) printf("buf2 is NULL\n"); // Debug

// Code for processing buf2 will be added here.

printf("Type the output image filename:");
char imgFnOut[FNAME_LEN] = { 0 };
scanf_s("%s", imgFnOut, FNAME_LEN);
int numBytes2 = writeBinCom(imgFnOut, FNAME_LEN, IMG_HEADER, numBytes1 - IMG_HEADER, buf1, buf2);
printf("%s%d\n", "numBytes2: ", numBytes2);
if (buf1 == NULL) printf("buf1 is NULL\n"); // Debug
if (buf2 == NULL) printf("buf2 is NULL\n"); // Debug

After each of these functions are invoked, I test if the buffers are NULL for debugging.

In the function that reads from a binary file, it first open the file, reads the data into buf1 for the header and buf2 for the pixel data, then closes the file. I need this data to be available from outside the function. The code is a follows:

int readBinDiv(char* fn, int fl, int buf1Len, BYTE** buf1, BYTE** buf2) {
  if (buf1 != NULL || buf2 != NULL) return -1;
  FILE* tempFp = NULL;
  if (fopen_s(fp, fn, "rb") != 0) return -2;
  fseek(tempFp, 0, SEEK_END);
  size_t imgLen = ftell(tempFp);
  if (imgLen <= buf1Len) { // File length is not larger than the header.
      fclose(tempFp);
      return -3;
  }
  rewind(tempFp);

  // Allocate buffers.
  *buf1 = (BYTE*)malloc(buf1Len); // C6011 Dereferencing NULL pointer buf1.
  *buf2 = (BYTE*)malloc(imgLen - buf1Len); // Same as above
  if (buf1 == NULL || buf2 == NULL) {   // Unable to allocate a buffer.
      fclose(tempFp);
      return -4;
  }

  // Read the file into the two buffers, the close the file.
  fread(&buf1, 1, buf1Len, tempFp);
  fseek(tempFp, buf1Len, SEEK_SET);
  size_t imgRest = fread(&buf2, 1, imgLen - buf1Len, tempFp);
  fclose(tempFp);
  if (imgRest != (imgLen - buf1Len)) return -(int)imgRest;
  return (int)imgLen;
}

If successful it returns a positive value for the number of bytes read, otherwise a negative value is returned indicating an error.

In the function that writes to a binary file, it does the reverse. It opens a new file for writing, writes buf1 with the header to the beginning of the file, buf2 for the pixel data, then closes the file and frees the buffers. The code is as follows:

int writeBinCom(char* fn, int fl, int buf1Len, int buf2Len, BYTE* buf1, BYTE* buf2) {
  if (buf1 == NULL || buf2 == NULL) return -1;
  FILE* tempFp = NULL;

  // Open output file
  if (fopen_s(fp, fn, "wb") != 0) return -2;
  size_t len1 = fwrite(buf1, 1, buf1Len, tempFp);
  fseek(tempFp, buf1Len, SEEK_SET);
  size_t len2 = fwrite(buf2, 1, buf2Len, tempFp);

  fclose(tempFp);
  free(buf1);
  free(buf2);
  buf1 = buf2 = NULL; // Reset to NULL - is this needed?

  if (len1 != buf1Len || len2 != buf2Len) return -3;

  return len1 + len2;
}

As before a positive return value is the number of bytes written to the file if successful, and should be the same as the number of bytes read.

In both functions f_open() is in fact not invoked directly, but from other function to perform various checks that the file can be read from or written to. This part of the code works and is omitted here.

All attempts to get this to work fail, either the compiler complains, I receive messages such as the warning C6011 by the IDE as above, or NULL or a negative value is returned by the first function. In some cases the first function appears to run correctly returning a valid number of bytes read from the file, but the buffers are NULL on return and the write function cannot work.

I would be very grateful in getting this matter resolved.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
csharp
  • 464
  • 2
  • 10
  • 19
  • 1
    C and C++ are different languages. Tag only the one you're actually using, which appears to be (and you say is) C. – John Bollinger Apr 27 '22 at 22:00
  • `fread(&buf1, 1, buf1Len, tempFp)` is a recipe for disaster. That should be `fread(*buf1, 1, buf1Len, tempFp)`. Likewise with `fread(&buf2, 1, imgLen - buf1Len, tempFp)`, which should be `fread(*buf2, 1, imgLen - buf1Len, tempFp)` – WhozCraig Apr 27 '22 at 22:03
  • 1
    You have mismatching pointer types at your function calls. Your compiler ought to be warning you about that. It reflects a serious problem that you need to fix. – John Bollinger Apr 27 '22 at 22:03
  • 1
    @csharp, save time. Enable all warnings. – chux - Reinstate Monica Apr 27 '22 at 22:05
  • Also, `BYTE *buf1, *buf2;` – जलजनक Apr 27 '22 at 22:07
  • `if (buf1 != NULL || buf2 != NULL) return -1;` what? – n. m. could be an AI Apr 27 '22 at 22:10
  • I tried the suggestions above, but still can't get the code to work. However, if I use BYTE *buf1, *buf2; in main as above, then call int numBytes1 = readBinDiv(imgFn, FNAME_LEN, IMG_HEADER, buf1, buf2); which is defined as int readBinDiv(char* fn, int fl, int buf1Len, BYTE* buf1, BYTE* buf2), then use buf1 = malloc(buf1Len); buf2 = malloc(imgLen - buf1Len); and fread(buf1, 1, buf1Len, tempFp); etc. this works and returns the number of bytes read, but on exit the two buffers are still NULL, so the write function won't work. The header file has typedef unsigned char BYTE; to make that clear. – csharp Apr 28 '22 at 02:03
  • You need a refresher on pointers. `numBytes1 = readBinDiv(imgFn, FNAME_LEN, IMG_HEADER, &buf1, &buf2);` & `*buf1 = malloc(buf1Len); *buf2 = malloc(imgLen - buf1Len);` Also, `fread(*buf1, 1, buf1Len, tempFp);` Comeback with a simple example where we can verify the answer to suggest you more complete answer. If `BYTE` is an alias for `char` just use `char`. – जलजनक Apr 28 '22 at 07:10
  • I tried using numBytes1 = readBinDiv(imgFn, FNAME_LEN, IMG_HEADER, &buf1, &buf2); & *buf1 = malloc(buf1Len); *buf2 = malloc(imgLen - buf1Len); & fread(*buf1, 1, buf1Len, tempFp); etc., but I get compilation error "warning C4047: '=': 'BYTE' differs in levels of indirection from 'void *'" for *buf1 = malloc(buf1Len); etc. If I replace this by *buf1 = (BYTE*)malloc(buf1Len); etc., I now get the error "'BYTE' differs in levels of indirection from 'BYTE *'". Additionally, with BYTE *buf1, *buf2; and int numBytes1 = readBinDiv(imgFn, FNAME_LEN, IMG_HEADER, &buf1, &buf2); in main ... – csharp Apr 28 '22 at 18:42
  • I get the compilation error "'BYTE *' differs in levels of indirection from 'BYTE **'" . I also tried declaring readBinDiv(... BYTE** buf1, BYTE** buf2); but that still fails. – csharp Apr 28 '22 at 18:45

1 Answers1

0

It looks as if I've solved the issue, so many thanks for the advice. The main function now contains the following code:

BYTE *buf1, *buf2;
buf1 = buf2 = NULL;
int numBytes1 = readBinDiv(imgFn, FNAME_LEN, IMG_HEADER, &buf1, &buf2);
printf("%s%d\n", "numBytes1: ", numBytes1);

printf("Type the output image filename: ");
char imgFnOut[FNAME_LEN] = { 0 };
scanf_s("%s", imgFnOut, FNAME_LEN);
int numBytes2 = writeBinCom(imgFnOut, FNAME_LEN, IMG_HEADER, numBytes1 - IMG_HEADER, buf1, buf2);
printf("%s%d\n", "numBytes2: ", numBytes2);

and the two functions contain the following code:

int readBinDiv(char* fn, int fl, int buf1Len, BYTE** buf1, BYTE** buf2) {
  if (*buf1 != NULL || *buf2 != NULL) return -1;
  FILE* tempFp = NULL;
  if (openFileToRead(false, true, fn, fl, &tempFp)) return -2;  // Unable to open file.
  fseek(tempFp, 0, SEEK_END);
  size_t imgLen = ftell(tempFp);
  if (imgLen <= buf1Len) {          // File length is not larger than the header.
      fclose(tempFp);
      return -3;
  }
  rewind(tempFp);

  // Allocate buffers.
  *buf1 = (BYTE*)malloc(buf1Len);
  *buf2 = (BYTE*)malloc(imgLen - buf1Len);
  if (*buf1 == NULL || *buf2 == NULL) { // Unable to allocate a buffer.
    fclose(tempFp);
    return -4;
  }
  // Read the file into the two buffers, the close the file.
  fread(*buf1, 1, buf1Len, tempFp);
  fseek(tempFp, buf1Len, SEEK_SET);
  size_t imgRest = fread(*buf2, 1, imgLen - buf1Len, tempFp);
  fclose(tempFp);
  if (imgRest != (imgLen - buf1Len)) return -(int)imgRest;
  return (int)imgLen;
}

int writeBinCom(char* fn, int fl, int buf1Len, int buf2Len, BYTE* buf1, BYTE* buf2) {
  if (buf1 == NULL || buf2 == NULL) return -1;
  FILE* tempFp = NULL;

  // Open output file
  if (openFileToWrite(false, true, true, fn, fl, &tempFp)) return -2;   // Unable to open file.
  size_t len1 = fwrite(buf1, 1, buf1Len, tempFp);
  fseek(tempFp, buf1Len, SEEK_SET);
  size_t len2 = fwrite(buf2, 1, buf2Len, tempFp);

  fclose(tempFp);
  free(buf1);
  free(buf2);
  buf1 = buf2 = NULL;

  if (len1 != buf1Len || len2 != buf2Len) return -3;

  return (int)(len1 + len2);
}

It now compiles and runs without any errors, and the output image file is an exact copy of the input image by running a bit-wise file comparison. Note that I had to use BYTE** buf1, BYTE** buf2 in the argument list of the first function definition. This and the fact that I have to test:

if (*buf1 != NULL || *buf2 != NULL) return -1;

instead of

if (buf1 != NULL || buf2 != NULL) return -1;

at the beginning of the first function to ensure that both buffers are initially NULL caused problems which are fixed. The second function that combines the two buffers and writes to an output file needed very few changes, as I'm just taking the existing buffers, writing them out, then freeing them, etc.

I still have a couple of simple questions:

  1. At the beginning I initialize the two buffers to NULL with buf1 = buf2 = NULL; is this necessary? What is the default value of an initialized buffer?
  2. The same also applies to file pointers used in both functions.
  3. Near end of the second function after freeing the two buffers they are set to NULL. Is this recommended or is it redundant?

One other note, जलजनक (I can't read Hindi) suggested that I could just use char instead of BYTE, but this is an alias for an unsigned char, which I always use for non-character binary data, such as image files. incidentally, if BYTE b is an unsigned char and char c is a signed char, then:

b = (256 + c) % 256;

will do the conversion without having to test if c is negative.

csharp
  • 464
  • 2
  • 10
  • 19