0

I initialized the array like so

CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, bounds);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

However, when I tried checking the count through an NSLog, I always get 4 (4/1, specifically).

int count = sizeof(rawData)/sizeof(rawData[0]);
NSLog(@"%d", count);

Yet when I NSLog the value of individual elements, it returns non zero values.

ex.
CGFloat f1 = rawData[15];

CGFloat f2 = rawData[n], where n is image width*height*4;

//I wasn't expecting this to work since the last element should be n-1

Finally, I tried

int n = lipBorder.size.width *lipBorder.size.height*4*2; //lipBorder holds the image's dimensions, I tried multiplying by 2 because there are 2 pixels for every CGPoint in retina
CGFloat f = rawData[n];

This would return different values each time for the same image, (ex. 0.000, 115.000, 38.000).

How do I determine the count / how are the values being stored into the array?

Mahir
  • 1,684
  • 5
  • 31
  • 59

2 Answers2

1

rawData is a pointer to unsigned char, as such its size is 32 bits (4 bytes)[1]. rawData[0] is an unsigned char, as such its size is 8 bits (1 byte). Hence, 4/1.

You've probably seen this done with arrays before, where it does work as you would expect:

unsigned char temp[10] = {0};
NSLog(@"%d", sizeof(temp)/sizeof(temp[0])); // Prints 10

Note, however, that you are dealing with a pointer to unsigned char, not an array of unsigned char - the semantics are different, hence why this doesn't work in your case.

If you want the size of your buffer, you'll be much better off simply using height * width * 4, since that's what you passed to malloc anyway. If you really must, you could divide that by sizeof(char) or sizeof(rawData[0]) to get the number of elements, but since they're chars you'll get the same number anyway.

Now, rawData is just a chunk of memory somewhere. There's other memory before and after it. So, if you attempt to do something like rawData[height * width * 4], what you're actually doing is attempting to access the next byte of memory after the chunk allocated for rawData. This is undefined behaviour, and can result in random garbage values being returned[2] (as you've observed), some "unassigned memory" marker value being returned, or a segmentation fault occurring.


[1]: iOS is a 32-bit platform
[2]: probably whatever value was put into that memory location last time it was legitimately used.
Mac
  • 14,615
  • 9
  • 62
  • 80
  • Okay, but why am I getting values for width*height*4? – Mahir Aug 18 '13 at 21:17
  • @Mahir: I'm not sure I follow. Calling `malloc` with an argument of, say, `n` means "allocate me `n` bytes of memory". If you get a valid pointer back from `malloc`, you can be assured that it points to a block of (at least) `n` bytes of memory. In your case, you call `malloc` with an `n` of `height * width * 4` and get a valid pointer back, so you know `rawData` must be `height * width * 4` bytes in length. There's no need for any other indirect means of determining its size. – Mac Aug 18 '13 at 21:22
  • Sorry, I thought the parameter was the number of elements, not the memory size. But if each element is 1 byte, aren't they going to be the same anyway, like you mentioned? – Mahir Aug 18 '13 at 21:35
  • I had assumed that if I did `malloc[n]`, I would receive an array that could hold `n` elements, meaning that the last element would be `rawData[n-1]`, which apparently isn't the case – Mahir Aug 18 '13 at 21:37
  • Nope, `malloc` simply returns a generic pointer (i.e. a `void*`) to a block of memory `n` bytes in size. In your case, your "elements" are also 1 byte in size, so the new memory block just happens to also be `n` elements in size. – Mac Aug 18 '13 at 21:48
  • The common idiom, if you want to create a block of `height * width * 4` elements of type `x`, is to call `malloc` as `malloc(height * width * 4 * sizeof(x))`. In your case, `x` is `unsigned char`, which has `sizeof` of 1. So that would become `malloc(height * width * 4 * 1)`, which is the same as what you had to begin with (once you drop the redundant `* 1`) - that's why it might seem like its assigning `height * width * 4` elements, even though its actually assigning `height * width * 4` bytes. – Mac Aug 18 '13 at 21:53
  • Right, but if there are n elements in the array, why I am getting values for `rawData[n]` and `rawData[n+1]`, etc.? Aren't these indexes beyond what the array holds? – Mahir Aug 18 '13 at 21:55
  • Ah, my apologies - I had completely missed that part of your question. `rawData` is just a chunk of memory somewhere - there's other memory before it and after it. When you do `rawData[n]`, it's actually accessing the first byte of data **after** rawData in memory. That could have been used for anything else previously, and will (most likely) contain some "junk" value - that's what you're seeing. Accessing out of bounds elements like this is undefined behaviour - see [this question](http://stackoverflow.com/questions/7818440) for more details. – Mac Aug 18 '13 at 22:06
0

The pointer returned by malloc is a void* pointer meaning that it returns a pointer to an address in memory. It seems that the width and the height that are being returned are 0. This would explain why you are only being allocated 4 bytes for your array.

You also said that you tried

int n = lipBorder.size.width *lipBorder.size.height*4*2; //lipBorder holds the image's dimensions, I tried multiplying by 2 because there are 2 pixels for every CGPoint in retina
    CGFloat f = rawData[n];    

and were receiving different values each time. This behavior is to be expected given that your array is only 4 bytes long and you are accessing an area of memory that is much further ahead in memory. The reason that the value was changing was that you were accessing memory that was not in your array, but in a memory location that was lipBorder.size.width *lipBorder.size.height*4*2 - 4 bytes passed the end of your array. C in no way prevent you from accessing any memory within your program. If you had accessed memory that is off limits to your program you would have received a segmentation fault.

You can therefore access n + 1 or n + 2 or n + whatever element. It only means that you are accessing memory that is passed the end of your array.

Incrementing the pointer rawdata would move the memory address by one byte. Incrementing and int pointer would increment move the memory address by 4 bytes (sizeof(int)).

Tyler Cloutier
  • 2,040
  • 2
  • 21
  • 31