Checking Endianness of RISC-V machine using C-code - c

Can someone please help me out with this. There is a C-code which most of you are familiar with, it checks the endian-ness of a machine.
What would be the result if it runs on a RISC-V machine?
Code is mentioned as below:
#include <cstdio>
int main()
{
int x = 1;
char* p = (char*)&x;
printf("%d\n",(int)*p);
return 0;
}

The program is valid regardless of the platform.
The output is 1 for a little-endian computer or a computer where sizeof (int) == sizeof (char). It will be 0 for all other platforms.
Since RISC-V is little-endian then the output should be 1.

Related

Program is Big-Endian even though system is Little-Endian

Somehow, my program is treating variables as big endian, even though my system is little endian.
When I execute "lscpu | grep Endian", it returns
Byte Order: Little Endian
But when I run a debug gcc (x86_64 linux) executable with following code:
int x = 1235213421;
printf("%x", x);
It returns 0x499FDC6D, while for little endian it should return 0x6DDC9F49
You told printf to print an integer so it does so according to the endianess, making the code portable. If you wish to view how the data is stored in memory, you have to do it byte by byte, like this:
int x = 1235213421;
unsigned char* ptr = (unsigned char*)&x;
for(size_t i=0; i<sizeof(int); i++)
{
printf("%.2x", ptr[i]);
}

Understanding hexadecimal output from C pointers

I ran some C code in CodeBlocks and printed the memory address. The result was a 16 character hexadecimal. This was an exercise in the youtube tutorial on C from freeCodeCamp at 3:14:05.
https://www.youtube.com/watch?v=KJgsSFOSQv0&list=FLWOrEQtSUgIDNHMCI3yfvnQ&index=4
The youtube presenter came up with an 8 character hexadecimal result. Does this mean I have twice the system ram vs the presenter? My Windows 10 laptop has 32 GB of ram.
Edit, I was instructed to post the relevant C code, here it is.
int main()
{
int age = 30;
int * pAge = &age;
double gpa = 3.4;
double * pGpa = &gpa;
char grade = 'A';
char * pGrade = &grade;
printf("age's memory address: %p\n", &age);
return 0;
}
Older Windows versions had a 32 bit address bus. Newer versions support 64 bit data access and 64 bit address buses (given that the CPU is 64 bit too). It has absolutely nothing to do with how much physical RAM you have. Although it was problematic to expand old PC:s beyond 4GB of addressable memory, which was one of the main reasons for migrating to 64 bit.

Casting uint8_t array into uint16_t value in C

I'm trying to convert a 2-byte array into a single 16-bit value. For some reason, when I cast the array as a 16-bit pointer and then dereference it, the byte ordering of the value gets swapped.
For example,
#include <stdint.h>
#include <stdio.h>
main()
{
uint8_t a[2] = {0x15, 0xaa};
uint16_t b = *(uint16_t*)a;
printf("%x\n", (unsigned int)b);
return 0;
}
prints aa15 instead of 15aa (which is what I would expect).
What's the reason behind this, and is there an easy fix?
I'm aware that I can do something like uint16_t b = a[0] << 8 | a[1]; (which does work just fine), but I feel like this problem should be easily solvable with casting and I'm not sure what's causing the issue here.
As mentioned in the comments, this is due to endianness.
Your machine is little-endian, which (among other things) means that multi-byte integer values have the least significant byte first.
If you compiled and ran this code on a big-endian machine (ex. a Sun), you would get the result you expect.
Since your array is set up as big-endian, which also happens to be network byte order, you could get around this by using ntohs and htons. These functions convert a 16-bit value from network byte order (big endian) to the host's byte order and vice versa:
uint16_t b = ntohs(*(uint16_t*)a);
There are similar functions called ntohl and htonl that work on 32-bit values.
This is because of the endianess of your machine.
In order to make your code independent of the machine consider the following function:
#define LITTLE_ENDIAN 0
#define BIG_ENDIAN 1
int endian() {
int i = 1;
char *p = (char *)&i;
if (p[0] == 1)
return LITTLE_ENDIAN;
else
return BIG_ENDIAN;
}
So for each case you can choose which operation to apply.
You cannot do anything like *(uint16_t*)a because of the strict aliasing rule. Even if code appears to work for now, it may break later in a different compiler version.
A correct version of the code could be:
b = ((uint16_t)a[0] << CHAR_BIT) + a[1];
The version suggested in your question involving a[0] << 8 is incorrect because on a system with 16-bit int, this may cause signed integer overflow: a[0] promotes to int, and << 8 means * 256.
This might help to visualize things. When you create the array you have two bytes in order. When you print it you get the human readable hex value which is the opposite of the little endian way it was stored. The value 1 in little endian as a uint16_t type is stored as follows where a0 is a lower address than a1...
a0 a1
|10000000|00000000
Note, the least significant byte is first, but when we print the value in hex it the least significant byte appears on the right which is what we normally expect on any machine.
This program prints a little endian and big endian 1 in binary starting from least significant byte...
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void print_bin(uint64_t num, size_t bytes) {
int i = 0;
for(i = bytes * 8; i > 0; i--) {
(i % 8 == 0) ? printf("|") : 1;
(num & 1) ? printf("1") : printf("0");
num >>= 1;
}
printf("\n");
}
int main(void) {
uint8_t a[2] = {0x15, 0xaa};
uint16_t b = *(uint16_t*)a;
uint16_t le = 1;
uint16_t be = htons(le);
printf("Little Endian 1\n");
print_bin(le, 2);
printf("Big Endian 1 on little endian machine\n");
print_bin(be, 2);
printf("0xaa15 as little endian\n");
print_bin(b, 2);
return 0;
}
This is the output (this is Least significant byte first)
Little Endian 1
|10000000|00000000
Big Endian 1 on little endian machine
|00000000|10000000
0xaa15 as little endian
|10101000|01010101

Test big endian [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Little vs Big Endianess: How to interpret the test
Is there an easy method to test code with gcc or any online compiler like ideone for big endian? I don't want to use qemu or virtual machines
EDIT
Can someone explain the behavior of this piece of code on a system using big endian?
#include <stdio.h>
#include <string.h>
#include <stdint.h>
int main (void)
{
int32_t i;
unsigned char u[4] = {'a', 'b', 'c', 'd'};
memcpy(&i, u, sizeof(u));
printf("%d\n", i);
memcpy(u, &i, sizeof(i));
for (i = 0; i < 4; i++) {
printf("%c", u[i]);
}
printf("\n");
return 0;
}
As a program?
#include <stdio.h>
#include <stdint.h>
int main(int argc, char** argv) {
union {
uint32_t word;
uint8_t bytes[4];
} test_struct;
test_struct.word = 0x1;
if (test_struct.bytes[0] != 0)
printf("little-endian\n");
else
printf("big-endian\n");
return 0;
}
On a little-endian architecture, the least significant byte is stored first. On a big-endian architecture, the most-significant byte is stored first. So by overlaying a uint32_t with a uint8_t[4], I can check to see which byte comes first. See: http://en.wikipedia.org/wiki/Big_endian
GCC in particular defines the __BYTE_ORDER__ macro as an extension. You can test against __ORDER_BIG_ENDIAN__, __ORDER_LITTLE_ENDIAN__, and __ORDER_PDP_ENDIAN__ (which I didn't know existed!) -- see http://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
See also http://en.wikipedia.org/wiki/Big_endian
As for running code in an endianness that doesn't match your machine's native endianness, then you're going to have to compile and run it on an architecture that has that different endianness. So you are going to need to cross-compile, and run on an emulator or virtual machine.
edit: ah, I didn't see the first printf().
The first printf will print "1633837924", since a big-endian machine will interpret the 'a' character as the most significant byte in the int.
The second printf will just print "abcd", since the value of u has been copied byte-by-byte back and forth from i.

C program to check little vs. big endian [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C Macro definition to determine big endian or little endian machine?
int main()
{
int x = 1;
char *y = (char*)&x;
printf("%c\n",*y+48);
}
If it's little endian it will print 1. If it's big endian it will print 0. Is that correct? Or will setting a char* to int x always point to the least significant bit, regardless of endianness?
In short, yes.
Suppose we are on a 32-bit machine.
If it is little endian, the x in the memory will be something like:
higher memory
----->
+----+----+----+----+
|0x01|0x00|0x00|0x00|
+----+----+----+----+
A
|
&x
so (char*)(&x) == 1, and *y+48 == '1'. (48 is the ascii code of '0')
If it is big endian, it will be:
+----+----+----+----+
|0x00|0x00|0x00|0x01|
+----+----+----+----+
A
|
&x
so this one will be '0'.
The following will do.
unsigned int x = 1;
printf ("%d", (int) (((char *)&x)[0]));
And setting &x to char * will enable you to access the individual bytes of the integer, and the ordering of bytes will depend on the endianness of the system.
This is big endian test from a configure script:
#include <inttypes.h>
int main(int argc, char ** argv){
volatile uint32_t i=0x01234567;
// return 0 for big endian, 1 for little endian.
return (*((uint8_t*)(&i))) == 0x67;
}
Thought I knew I had read about that in the standard; but can't find it. Keeps looking. Old; answering heading; not Q-tex ;P:
The following program would determine that:
#include <stdio.h>
#include <stdint.h>
int is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} e = { 0x01000000 };
return e.c[0];
}
int main(void)
{
printf("System is %s-endian.\n",
is_big_endian() ? "big" : "little");
return 0;
}
You also have this approach; from Quake II:
byte swaptest[2] = {1,0};
if ( *(short *)swaptest == 1) {
bigendien = false;
And !is_big_endian() is not 100% to be little as it can be mixed/middle.
Believe this can be checked using same approach only change value from 0x01000000 to i.e. 0x01020304 giving:
switch(e.c[0]) {
case 0x01: BIG
case 0x02: MIX
default: LITTLE
But not entirely sure about that one ...

Resources