I recently learnt nios II SOPC, and I encountered the process of writing into and reading from memory. The use of pointer int* and char* lets me have two different results. The code is as follow.
#include "system.h"
#include "stdio.h"
int main()
{
char* n = (char*) MEMORY_0_BASE; //the address for first block of memory
int i;
for(i=0;i<16;i++)
{
*(n+i)=i;
}
for(i=0;i<16;i++)
{
printf("Du lieu tai o nho thu %d la %d\n", i , *(n+i));
}
while(1);
}
The code for "int*" is as follow
#include "system.h"
#include "stdio.h"
int main()
{
char* n = (char*) MEMORY_0_BASE; //the address for first block of memory
int i;
for(i=0;i<16;i++)
{
*(n+i)=i;
}
for(i=0;i<16;i++)
{
printf("Du lieu tai o nho thu %d la %d\n", i , *(n+i));
}
while(1);
}
The result for using "int*" is 0,1,2,...,15 while the result for "char*" is 3,3,3,3,7,7,7,7,11,11,11,11,15,15,15,15. I cannot explain why this is the case.
The following is my code for the memory block
module memory
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=4)
(
input iClk, iReset_n,
input iRead_n, iWrite_n,
input [ADDR_WIDTH-1:0] iAddress,
input [DATA_WIDTH-1:0] iData,
output [DATA_WIDTH-1:0] oData
);
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH-1:0];
reg [ADDR_WIDTH-1:0] addr_reg;
always#(posedge iClk)
begin
if(!iWrite_n)
mem[iAddress] = iData;
if(!iRead_n)
addr_reg = iAddress;
end
assign oData = mem[addr_reg];
endmodule
This is my speculation based on the observed behaviour.
Your memory device consists of 16 x 32-bit integers. My speculation is that the compiler which likes to address things in bytes is effectively masking out the lowest two bits of the address. The zeroth register has the address MEMORY_0_BASE + 0 * 4, the first register has the address MEMORY_0_BASE + 1 * 4, the second register has the address MEMORY_0_BASE + 2 * 4 etc.
If you store ints to the registers using an int pointer, each time you increment the int pointer, the C pointer arithmetic actually adds sizeof(int) = 4 to the address in the pointer, so the sequence of addresses to which ints are stored is as above.
If you store chars using a char pointer, each time you increment the char pointer, the C pointer arithmetic adds sizeof(char) = 1 to the address in the pointer. The code attempts to store the first four chars (0, 1, 2, 3) to MEMORY_0_BASE + 0, MEMORY_0_BASE + 1, MEMORY_0_BASE + 2, MEMORY_0_BASE + 3. If, as I believe, the bottom two bits of the pointer are being masked, all of those addresses store to MEMORY_0_BASE and, by the time you are done, the value in it is 3.
Similarly for the second four chars (4, 5, 6, 7). They get stored to MEMORY_0_BASE + 4, MEMORY_0_BASE + 5, MEMORY_0_BASE + 6, MEMORY_0_BASE + 7, which all map to MEMORY_0_BASE + 4 after masking, leaving it containing the number 7 and so on.
That's how you get the sequence 3,3,3,3,7,7,7,7,11,11,11,11,15,15,15,15.
Related
I have this small piece of code:
uint64_t test[] = {1, 2, 3, 4, 5};
printf("test value: %llu\n", test);
I try to print the test array, and it gives me this number:
test value: 140732916721552
Can someone explain this and how an uint64_t array works? Thank you
In your code
uint64_t test[] = {1, 2, 3, 4, 5};
printf("test value: %llu\n", test);
%llu tells printf that it shall print a long long unsigned integer. The test part of the printf statement pass a pointer to the first element of the array to printf. In other words, there is a mismatch between what you are passing (a pointer) and what you tell printf to print (long long unsigned).
In C such a mismatch leads to "undefined behavior". So in general it's not possible to say what will be printed. Any print out will be legal from a C standard point of view. No print out would also be legal. A program crash would be legal. Anything... would be legal.
It's impossible to say what goes on in general. On a specific system, it's possible to dig into the low level things and figure out what is going on. On my system the printed value corresponds to the address of the first array element interpreted as a long long unsigned integer. But don't rely on that. Other systems may do something completely different.
The code below shows how to correctly print the address of the array and the array elements.
#include <stdio.h>
#include <inttypes.h>
int main(void)
{
uint64_t test[] = {1, 2, 3, 4, 5};
// Print the address where the array is located
printf("Address of test value is %p\n", (void*)test);
// Print the values of the array elements
size_t sz = sizeof test / sizeof test[0];
for (size_t i = 0; i < sz; ++i)
printf("test[%zu] is %" PRIu64 "\n", i, test[i]);
return 0;
}
Output (note: address may differ in every invocation):
Address of test value is 0x7ffc4ace5730
test[0] is 1
test[1] is 2
test[2] is 3
test[3] is 4
test[4] is 5
When you define an array like this in C, what you are actually doing is storing each of these values sequentially on the stack as separate uint64_ts. The value assigned to the test identifier is then a pointer to the first of these values, a uint64_t* rather than a uint64_t. When you print test, you are printing the pointer rather than any of the elements, i.e. the memory address of the first element in your array.
The [] notation is equivalent to
*(test + i)
i.e. it dereferences a pointer to the ith element.
So I was doing an exercise to see if I was using memset correctly.
Here's the original code I wrote which was supposed to memset some addressese to have value 50:
int main(){
int *block1 = malloc(2048);
memset(block1, 50, 10);
// int count = 0;
for (int *iter = block1; (uint8_t *) iter < (uint8_t *)block1 + 10; iter = (int *) ((uint8_t *)iter + 1) ){
printf("%p : %d\n", iter, *iter);
}
return 0;
}
I expected every address in memory to store the value 50. HOWEVER my output was:
(Address : Value)
0x14e008800 : 842150450
0x14e008801 : 842150450
0x14e008802 : 842150450
0x14e008803 : 842150450
0x14e008804 : 842150450
0x14e008805 : 842150450
0x14e008806 : 842150450
0x14e008807 : 3289650
0x14e008808 : 12850
0x14e008809 : 50
I was stuck on the problem for a while and tried a bunch of things until I randomly decided that maybe my pointer is the problem. I then tried a uint8_t pointer.
int main(){
uint8_t *block1 = malloc(2048);
memset(block1, 50, 10);
for (uint8_t *iter = block1; iter < block1 + 10; iter++ ){
printf("%p : %d\n", iter, *iter);
}
return 0;
}
All I did was change the type of the block1 variable and my iter variable to be uint8_t pointers instead of int pointers and I got the correct result!
0x13d808800 : 50
0x13d808801 : 50
0x13d808802 : 50
0x13d808803 : 50
0x13d808804 : 50
0x13d808805 : 50
0x13d808806 : 50
0x13d808807 : 50
0x13d808808 : 50
0x13d808809 : 50
My question is then, why did that make such a difference?
My question is then, why did that make such a difference?
Because the exact type of a pointer is hugely important. Pointers in C are not just memory addresses. Pointers are memory addresses, along with a notion of what type of data is expected to be found at that address.
If you write
uint8_t *p;
... p = somewhere ...
printf("%d\n", *p);
then in that last line, *p fetches one byte of memory pointed to by p.
But if you write
int *p;
... p = somewhere ...
printf("%d\n", *p);
where, yes, the only change is the type of the pointer, then in that exact same last line, *p now fetches four bytes of memory pointed to by p, interpreting them as a 32-bit int. (This assumes int on your machine is four bytes, which is pretty common these days.)
When you called
memset(block1, 50, 10);
you were asking for some (though not all) of the individual bytes of memory in block1 to be set to 50.
When you used an int pointer to step over that block of memory, fetching (as we said earlier) four bytes of memory at a time, you got 4-byte integers where each of the 4 bytes contained the value 50. So the value you got was
(((((50 << 8) | 50) << 8) | 50) << 8) | 50
which just happens to be exactly 842150450.
Or, looking at it another way, if you take that value 842150450 and convert it to hex (base 16), you'll find that it's 0x32323232, where 0x32 is the hexadecimal value of 50, again showing that we have four bytes each with the value 50.
Now, that all makes sense so far, although, you were skating on thin ice in your first program. You had int *iter, but then you said
for(iter = block1; (uint8_t *) iter < (uint8_t *)block1 + 10; iter = (int *) ((uint8_t *)iter + 1) )
In that cumbersome increment expression
iter = (int *) ((uint8_t *)iter + 1)
you have contrived to increment the address in iter by just one byte. Normally, we say
iter = iter + 1
or just
iter++
and this means to increment the address in iter by several bytes, so that it points at the next int in a conventional array of int.
Doing it the way you did had three implications:
You were accessing a sort of sliding window of int-sized subblocks of block1. That is, you fetched an int made from bytes 1, 2, 3, and 4, then an int made from bytes 2, 3, 4, and 5, then an int made from bytes 3, 4, 5, and 6, etc. Since all the bytes had the same value, you always got the same value, but this is a strange and generally meaningless thing to do.
Three out of four of the int values you fetched were unaligned. It looks like your processor let you get away with this, but some processors would have given you a Bus Error or some other kind of memory-access exception, because unaligned access aren't always allowed.
You also violated the rule about strict aliasing.
The function memset sets each byte of the supplied memory with the specified value.
So in this call
memset(block1, 50, 10);
10 bytes of the memory addressed by the pointer block1 were set with the value 50.
But using the pointer iter that has the type int * you are outputting at once sizeof( int ) bytes pointed to by the pointer.
On the other hand if to declare the pointer as having the type
uint8_t *iter;
then you will output only one byte of memory.
Consider the following demonstration program.
#include <stdio.h>
int main( void )
{
int x;
memset( &x, 50, sizeof( x ) );
printf( "x = %d\n", x );
for ( const char *p = ( const char * )&x; p != ( const char * )&x + sizeof( x ); ++p )
{
printf( "%d", *p );
}
putchar( '\n' );
}
The program output is
x = 842150450
50505050
That is each byte of the memory occupied by the integer variable x was set equal to 50.
If to output each byte separately then the program outputs the values 50.
To make it even more clear consider one more demonstration program.
#include <stdio.h>
int main( void )
{
printf( "50 in hex is %#x\n", 50 );
int x = 0x32323232;
printf( "x = %d\n", x );
}
The program output is
50 in hex is 0x32
x = 842150450
That is the value 50 in hexadecimal is equal tp 0x32.
Thus this initialization
int x = 0x32323232;
yields the same result as the call of the function memset
memset( &x, 50, sizeof( x ) );
that you could equivalently rewrite like
memset( &x, 0x32, sizeof( x ) );
In the first case you are de-referencing the int* iter so it prints the (misaligned) int value at the address, not the byte value.
It is clear what is happening when you look at the value 842150450 in hexadecimal - 0x32323232 - that is each byte of the integer is 0x32 (50 decimal). The bytes after the tenth byte are undefined, but happen to be zero in this case and the machine is little-endian, so it tails off with 0x323232, 0x3232, and finally 0x32.
Clearly the second case is the more "correct" solution, but you can fix the first case thus;
printf("%p : %d\n",
(void*)iter,
*(uint8_t*)iter);
I was finding the output of the following C program, which I found on GeeksforGeeks. Here's the program:
#include <stdio.h>
void fun(int ptr[])
{
int i;
unsigned int n = sizeof(ptr)/sizeof(ptr[0]);
for (i=0; i<n; i++)
printf("%d ", ptr[i]);
}
// Driver program
int main()
{
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
fun(arr);
return 0;
}
The output of this code was "1 2". But according to me, the output should be just 1. Here is my interpretation of this code:
Firstly, the main function will run, in which after declaring the array "arr", next statement will execute which contains the statement fun(arr).
In that statement, the function "fun" will be called with the argument arr, which contains the address of the first element of the array.
After that, under the function fun, there is a pointer ptr as a parameter. When this function will execute, then the value of n will be calculated as 1 since here the size of ptr is 4 and the size of ptr[0] is also 4.
Next, the loop will run only once since the value of n is 1 and that's why only '1' will get printed since it is the value of ptr[0].
Please help me to find out where I am wrong.
[....] the value of n will be calculated as 1 since here the size of ptr is 4 and the size of ptr[0] is also 4.
Well, that's common, but not guaranteed.
sizeof(ptr) could very well be, result in 8, which is likely in your case, while sizeof(int) can evaluate to 4, resulting a value of 2 for n. This depends on (and varies with) your environment and used implementation.
Try printing them separately, like
printf("Pointer size :%zu\n", sizeof(ptr));
printf("Element size: %zu\n", sizeof(ptr[0]));
and see for yourself.
The size of a pointer on modern platforms is commonly either 4 or 8 bytes.
On a 32-bit platform it's likely that sizeof(ptr) == 4 and n == 1.
On a 64-bit platform it's likely that sizeof(ptr) == 8 and n == 2.
I am studying for my midterm. this was an example code
#include <stdio.h>
void doubleArray(int array[], int length)
{
for (int i = 0; i < length-2; i++) {
array[i] += array[i];
}
length += 5;
printf(“%d\n”, length); // Question 29
}
int main(int argc,char *argv[]) {
int integers[6] = { 3, 4, 5, 6, 7, 8};
int length = 6;
printf(“%d\n”, integers[4]); // Question 28
doubleArray(integers, length);
printf(“%d\n”, *(integers + 3)); // Question 30
printf(“%d\n”, *(integers + 4)); // Question 31
printf(“%d\n”, length); // Question 32
}
for questions 30 and 31
the answer is that it prints 12 (30) and 7 (31)
can someone explain to me why and what that "*(integers + 3)" means?
* is a dereference operator on a pointer.
This means that it will "get" the value that's stored at the pointer address of the item right after it ((integers + 3)).
It will interpret this value as the dereferenced type of the item after it (int since (integers + 3) is of type int*)
(integers + 3)
integers is a pointer to the address of the first element of the integers array.
That means that if integers contained [1, 2, 3, 4, 5] then it would point to where 1 is stored in memory.
integers + 3 takes the address of integers (i.e. where 1 is stored in memory) and adds the amount of address space required to store 3 ints (since the pointer is of type int*). Advancing it by one space would give you the address of 2 in memory, advancing it by two spaces would give you the address of 3 in memory, and advancing it by three spaces gives you the address of 4 in memory.
How this applies to your example
(integers + 3) gives you the address of the 4th item in the integers array since it's the first element's address plus the size of three elements.
Dereferencing that with the * operator, gives you the value of the 4th element, 12 (since the value of 6 was doubled by doubleArray)
The same applies to *(integers + 4) except that doubleArray didn't double the 5th element so it gives you 7.
How doubleArray works
for (int i = 0; i < length-2; i++) means start the variable i at 0 and advance it until it is length - 2.
This means it takes the value of everything from 0 to the value of length - 2 but executes the body of the loop for values from 0 to length - 3 since the < is exclusive (the conditional is evaluated BEFORE executing the body of the loop so when i == length - 2 the condition is false and the loop terminates without further execution.
So, for each element, excluding the last two, the element in array is added to itself.
I was wondering why I get these memory addresses in this simple program.
#include <stdio.h>
int main() {
char *a = "buffera";
char *b = "bufferbb";
printf("%p %p\n", a, b);
return 0;
}
Output I get is.
00403064 0040306C
Supposedly each character occupies one byte in memory (two hex numbers), then if the string a occupy 7 + 1 = 8 bytes in memory and the address of a starts at 0x00403064, then according to me it should end at 0x00403079 and not at 0x0040306B.
0043064 + 8 = 0040306C; I don't know where you get 00403079 from.
0x00403064 + 0x8 == 0x0040306C
Note that these numbers are in hexadecimal.
But either way, while these strings can't overlap, they don't need to be placed anywhere near each other in memory.