-2

I want to write a C program to read a png's pixel values without any external libraries (excluding zlib for decompression). I have studied the Wikipedia page for png's and have seen some python tutorials which do the same.

Currently, I am trying to read the bytes of the png into an array. Currently, I am using this picture which has some black dots that I would use to verify the pixel coordinates in the final array. However, when printing the retrieved array I only get the first four bytes of the png:

ëPNG

I verified that it was not in fact printing the bytes that is the problem when I asked for the sizeof() the array and it returned 4. I don't understand what is wrong with this code:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    FILE *fp = fopen(argv[1], "rb");
    unsigned char* file_data;
    fseek(fp, 0, SEEK_END);
    long int length = ftell(fp);
    rewind(fp);

    file_data = (char *)malloc((length+1)*sizeof(char));
    fread(file_data, length, 1, fp);
    printf("%s\n", file_data);
    printf("%d\n", sizeof(file_data)/sizeof(file_data[0]));

    fclose(fp);
    return 0;
}
PythonNoob
  • 41
  • 3
  • 1
    `sizeof` a pointer is not the same as a `sizeof` on a real array. The former only gives the pointer size and not the size of the memory buffer it points to. – kaylum Oct 15 '20 at 22:33
  • 5
    Even after hitting that up, what makes you think the raw file format of a [PNG](http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html) is printable as a terminated C string? – WhozCraig Oct 15 '20 at 22:34
  • 1
    You ignore the return value from `fread()` at your peril. It tells you exactly how much (how many elements) was read, if you switch arguments round so that it is `fread(file_data, 1, length, fp);` – Weather Vane Oct 15 '20 at 22:42

1 Answers1

0

WhozCraig is right. All the bytes are there, but you're attempting to view it as a string, which in C is terminated by a null byte ('\0'). Running this code on a PNG on my system and in LLDB reveals that file_data is in fact loading more than four bytes:

(lldb) expr file_data
(unsigned char *) $0 = 0x0000000100205e40 "\x89PNG\r\n\x1a\n"

But the next byte is a null byte as revealed in a hex editor (the selected bytes are what were printed):

Hex editor

So the "string" stops there.

Changing line 14 to the following:

for (int i = 0; i < length; i++) {
    printf("%x", file_data[i]);
}
printf("%s\n", "");

Reveals all the bytes:

89504e47da1aa000d494... [truncated]

Something else that you should know is that sizeof doesn't work on arrays whose size is not known at compile-time (with a handful of exceptions for VLAs, none of which apply here).

printf("%d\n", sizeof(file_data)/sizeof(file_data[0]));

In this line, sizeof(file_data) ends up querying the size of the type of file_data, which is unsigned char *. On my and any 64-bit system, this resolves to 8 (you may be on a 32-bit system, in which case it'd resolve to 4). sizeof(file_data[0]) queries the size of the first element of the array, which is of type unsigned char. (unsigned) char is defined to always evaluate to 1 in a sizeof call, so this entire expression resolves to:

printf("%d\n", 8 / 1);
            // 4 on your system

This is why your attempt to find the size of the array prints 4.

atirit
  • 1,492
  • 5
  • 18
  • 30
  • A great answer to the wrong question :-) Add a bit at the start explaining why `printf("%d\n", sizeof(file_data)/sizeof(file_data[0]));` is wrong and it'll be worth an upvote – John3136 Oct 15 '20 at 23:17
  • @John3136 Done! I'm really unsure why people were downvoting. My understanding was that they thought their program was only fetching the first 4 bytes, so I mainly sought to clarify that. – atirit Oct 16 '20 at 00:59