learn_C_deep_4 (type and variable naming, what sizeof(int) *p means, the concept of original code, inverse code and complement code, why should it be converted to binary system, unsigned and signed keywords when calculating data in the computer)

Directory

Type and variable naming

Why does C language have types?

Why are there so many data types

Variable Naming Rules

variable naming convention

Define variables must be initialized

What does sizeof(int) *p mean

The concept of original code, complement code and complement code

Why is it necessary to switch to the secondary system when calculating data in the computer?

unsigned and signed keywords

Understanding of unsigned and signed keywords

Convenience method for calculating binary – 2^n

Method of Computing Two’s Complement

The method of calculating the original code

Endian concept

When the inverse code is performing + 1 operation, the sign bit also needs to participate in the operation


Type and variable naming

Why does C language have types

Types are a very fundamental concept in programming languages. It is used to describe the type or data type of data, which can be basic types, such as integers, floating point numbers, characters, etc., or composite types composed of basic types, such as arrays, structures, etc. The presence of a type has several effects:

1. Memory allocation: Different types of data need to occupy different sizes of memory space, and the type can tell the compiler how much memory space to allocate for variables.

2. Data validation: Types help the compiler verify the integrity and correctness of data. For example, if a variable is declared as an integer type, it can only store integer values, and if we try to assign it to a string or other data types, the compiler will give an error message.

3. Data processing: Different types of data have different processing methods, such as integer and floating-point numbers, the operation methods are different. So the type tells the compiler what operation or operation to perform on the data.

In short, types allow programmers to control and manipulate data more precisely. At the same time, the type can also help the compiler to perform static type checking at compile time, prevent some errors from being discovered at runtime, and improve the reliability and stability of the program.

Why are there so many data types

There are so many different data types in the C language to meet different needs and more accurately represent various types of data. Different data types occupy different memory spaces when storing data, and also have different precision and range, so programmers need to choose the appropriate data type according to the specific situation.

For example, when you need to store an integer, if you use char type, although it can store integers, it can only store very small integers. If you need to store larger integers, you need to use int type or long type. In the same way, if you need to store decimals, you need to use float type or double type because they have higher precision.

Also, different data types serve different purposes when writing programs. For example, pointer types are used for dynamic memory management, structure types are used to organize complex data, enumeration types are used to define specific constants in programs, and so on.

Variable Naming Rules

In C language, the naming of variables must follow the following rules:

1. The variable name can only be composed of letters, numbers and underscores, and other symbols cannot be used.

2. Variable names must start with a letter or underscore.

3. Variable names cannot use keywords in C language (for example, if, for, while, etc.).

4. There is no limit to the length of the variable name, but in actual programming, it is recommended to control the variable name within 20 characters for easy reading and maintenance.

5. Variable names are case-sensitive, therefore, the variable names abc and ABC are different variables.

6. The variable name should choose a meaningful name that can clearly reflect the meaning of the variable.

Variable naming convention

1. Use meaningful variable names: You should use names that clearly reflect the meaning of the variable, for example, use count to represent the counter variable.

2. Use lowercase letters: It is recommended to use lowercase letters for variable names. This naming style makes the code easier to read and maintain. 3. Use camel case naming: For variable names consisting of multiple words, you can use camel case naming, that is, the first word starts with a lowercase letter, and the first letter of subsequent words is capitalized. For example, studentName can be used to represent the student name.

4. Do not use abbreviations and abbreviations: Try to avoid using abbreviations and abbreviations to represent variable names. Such a naming method can easily cause ambiguity and reduce the readability of the code.

5. Avoid using numbers at the beginning: Variable names cannot start with numbers, because in C language, names beginning with numbers will be interpreted as constants.

6. Use underscores as separators between words: In CamelCase, you need to use underscores as separators between words. For example, student_name.

7. Pay attention to the namespace: the variable name should be related to the function to which the variable belongs to avoid naming conflicts.

8. Use “g_” or “s_” prefix: It is recommended to add “g_” or “s_” prefix before the global variable name to distinguish it from local variables. Among them, “g_” means global variables, and “s_” means static global variables.

9. All macro definitions, enumeration constants, and read-only variables are named with capital letters and separated by underscores.

Define variables must be initialized

#include<stdio.h>
int main()
{
int a;
printf("%d", a);
//vs compiler directly prohibits the use of uninitialized variables
//error C4700: use of uninitialized local variable "a"
return 0;
}

If you declare a local variable but do not initialize it, the variable’s initial value is undefined and its contents may be arbitrary. Printing the value of an uninitialized variable is an undefined behavior, since its contents are undefined and may contain garbage, 0, or other values.

What does sizeof(int) *p mean

Following the sizeof keyword in the previous chapter, let’s explain the sizeof keyword, first recall what sizeof is for

#include<stdio.h>
int main()
{
int* p = NULL; //Declare a pointer to an integer variable and assign the initial value to NULL.
int arr[10]; //Declare an array containing 10 integer variables.
int* test[3]; //Declare an array of pointers to 3 integer variables.

printf("%d\\
", sizeof(p));//4
printf("%d\\
", sizeof(arr));//40
printf("%d\\
", sizeof(test));//12
return 0;
}

`sizeof` is an operator in C and C++ that gets the size in bytes of its operand (variable, type, expression, etc.).

So now let’s explain what sizeof(int) *p means.

`sizeof(int)` is an operator for calculating the size of an integer variable, which returns the number of bytes occupied by the type `int` in memory. Normally, an `int` type variable occupies 4 bytes (32-bit system) or 8 bytes (64-bit system), depending on the implementation of the compiler and operating system.

`*p` is a pointer operator used to access the value at the memory address pointed to by the pointer. `p` is a pointer to an integer variable.

Therefore, `sizeof(int) *p` means to use a pointer `p` pointing to an integer variable to store the size of an integer variable. Since an `int` type variable usually occupies 4 bytes, this statement is equivalent to `4 *p`.

Note that `sizeof` is a keyword, not a function, and does not need to enclose its parameters in parentheses. In this example, the parentheses would be treated as a pointer to a function, not as a size operator. Therefore, the correct way of writing should be `sizeof(int) * p`.

The concept of original code, inverse code and complement

Original code (Sign-Magnitude): The original code is the simplest way to represent signed integers in binary. Its representation method is to use binary numbers. negative number. For example, the original code of +5 is 00000101, and the original code of -5 is 10000101. The advantage of the original code is that it is simple and intuitive, and easy to understand, but there is a problem of inconsistency in the treatment of the sign bit and the value bit.

One’s Complement (One’s Complement): One’s Complement is designed on the basis of the original code to solve the problem of inconsistency between the sign bit and the value bit. For a positive number, its inverse code is the original code itself; for a negative number, the sign bit of the inverse code must be 1, and the value part takes the value part of the original code and inverts it bit by bit (0 becomes 1, 1 becomes 0), The obtained binary code is the inverse representation of the negative number. For example, the complement of +5 is 00000101 and the complement of -5 is 11111010. The advantage of the inverse code is that the calculation of the numerical part is still a direct addition, but the sign bit still needs additional judgment.

Two’s Complement: Two’s Complement is based on the inverse code, in order to solve the problem of inconsistency between the sign bit and the value bit. When the complement code represents a negative number, first convert it to its inverse code, and then add 1 to the complement code to obtain the complement code; when the complement code represents a positive number, it is the same as the original code. For example, the complement of +5 is 00000101 and the complement of -5 is 11111011. The advantage of the complement code is that the processing of the sign bit and the value bit are unified, and no special processing is required. In the complement code representation, the complement code of a negative number is the inversion of the original code of its absolute value plus 1, and the complement code of a positive number is the same as the original code.

Generally speaking, original code, inverse code and complement code are all ways to represent integers, but they deal with negative numbers in different ways. Complement code is the most commonly used representation method, and it is also the method used for addition and subtraction in computers. The default method, because the complement code can realize the unified processing of the sign bit and the value bit, and it is also very convenient for addition and subtraction in the computer.

When calculating data in a computer, why should it be converted to a secondary system

Binary is a base system that contains only two numbers (0 and 1). This base system can be used to represent all numbers, characters, graphics, etc. And all operations in the computer are done by electronic components, these components can only represent two states (on/off state of the electrical signal), that is, 0 and 1, so using binary in the computer can be more directly related to Interaction between hardware circuits. Specifically, the computer converts binary bit strings (sequences composed of a series of 0s and 1s) into numbers, text, graphics, etc., and after processing, converts them into binary bit strings and stores them in the computer memory. Read it, do something with it, and finally convert it to human-readable output. Although this conversion process may seem cumbersome, various operations, logical judgments, image processing and other operations in the computer are performed in the form of binary values, so converting to binary is helpful for computers to perform efficient operations and storage. Therefore, any data in the calculation must be converted to the secondary system.

unsigned and signed keywords

unsigned and signed keyword understanding

In computers, the representation and storage of integer types is usually done by two’s complement. For signed integer type (signed type), the highest bit is the sign bit, 0 means positive number, 1 means negative number. If you want to represent the number -1, you need to use two’s complement to represent it, that is, invert the binary code of 1 (to become 0), add 1, and get 11111111 (representing the complement of -1), the original negative complement of the positive number same code.

As for unsigned integer type (that is, unsigned type), there is no sign bit, and all its binary bits represent values, and the minimum value is 0. Its original inverse complement code is the same, therefore, for unsigned type integers, it can only represent non-negative integers, but cannot represent negative numbers.

Because signed and unsigned types are represented differently, they need to be careful when using them:

1. The range is different: the range of integers that can be represented by a signed type is from a negative maximum value to a positive maximum value, while an unsigned type can only represent a maximum value from 0 to a positive value.

2. Overflow problem: When performing calculations, if the value of the signed type exceeds its range, overflow will occur. If the value of the unsigned type exceeds its range, it will “wrap around”, that is, start again from the minimum value.

3. Different calculation methods: When performing calculations, the calculation methods of the signed type and the unsigned type are also different. For example, when performing bitwise operations, signed values are sign-extended, while unsigned values are zero-extended.

Convenience method to calculate binary – 2^n

#include<stdio.h>
int main()
{
//The integer stored in the computer must be complement
int a = 10;
// Any data in the calculation must be converted to a secondary system
//sign bit(0,1) + data bit
//Signed positive number, the original inverse complement is the same
//0000 0000 0000 0000 0000 0000 0000 1010 - original code, inverse code, complement code
//16 base
//0x00 00 00 0a
int b = -20;
//16 + 4 16 = 2^4 4 = 2^2
//2^nth power, there are n 0s after the secondary system 1
//16 = 2^4 10000
//4 = 2^2 00100
// add
// because -20 is negative int - 4 bytes - 32 bits
//The highest bit is the sign bit - negative number - 1
//1000 0000 0000 0000 0000 0000 0001 0100 - original code
//1111 1111 1111 1111 1111 1111 1110 1011 - Inverse code, the sign bit remains unchanged, and the other bits are reversed
//1111 1111 1111 1111 1111 1111 1110 1100 - Complement code, add 1 to inverse code
//16 base
//0xff ff ff ec
return 0;
}

The method of calculating the complement

The way to calculate the complement is as follows:

1. For positive numbers, the original code, complement code and complement code are the same.

2. For a negative number, first find the one’s complement of the binary representation of its absolute value (that is, invert all bits except the sign bit), and then add 1 to its complement to get its complement.

For example, assuming the complement of -5 is required, the steps are as follows:

1. The binary representation of 5 is 0000 0101, because it is a positive number, so the complement code, inverse code and original code are the same, all are 0000 0101.

2. Convert it to a negative number, that is, the sign bit becomes 1, resulting in 1000 0101.

3. Invert all bits except the sign bit to get 1111 1010, which is the inverse of -5.

4. Add 1 to the inverse code to get 1111 1011, which is the complement of -5.

So the complement of -5 is 1111 1011. It should be noted that in computers, signed integers are usually represented by complement code, which can avoid special processing of operations such as addition, subtraction, and logical operations.

The method of calculating the original code

The original code is a representation method of binary numbers, the sign bit is 0 for positive numbers, and 1 for negative numbers. The method of calculating the original code is as follows:

1. For positive numbers, directly convert them to binary numbers to get the original code.

2. For a negative number, first convert it into a binary number of absolute value, then invert each bit of the binary number (0 becomes 1, 1 becomes 0), and finally change the sign bit into 1 to get original code.

3. Find the original code by complementing the code

3.1.-20’s complement -> original code

Method 1:
1111 1111 1111 1111 1111 1111 1110 1100 - Complement
1111 1111 1111 1111 1111 1111 1110 1011 - complement = complement - 1
1000 0000 0000 0000 0000 0000 0001 0100 - original code = inverse code bit by bit, the sign bit remains unchanged

3.2.-20’s complement -> original code

Method 2:
1111 1111 1111 1111 1111 1111 1110 1100 - original code
1000 0000 0000 0000 0000 0000 0001 0011 - Negative code, the sign bit remains unchanged, and the other bits are reversed
1000 0000 0000 0000 0000 0000 0001 0100 - complement code, add 1 to complement code

Converting the complement of 10 and -20 above to hexadecimal is 0x00 00 00 0a and 0xff ff ff ec respectively, then let’s see how it is stored in the computer’s memory.

But we found that there are some differences from the hexadecimal we calculated. The order of the computer seems to be here. Here we need to understand the way the computer stores data – big and small endian

Endian concept

Endianness is used to describe how multi-byte data is stored in different types of computers. Simply put, it specifies the storage order of big and small bytes. A byte usually consists of 8 binary bits, while multibyte data is composed of multiple bytes. When storing multi-byte data, we need to determine the order in which these bytes are arranged in memory. Specifically, Big Endian stores the most significant bit at the lowest memory address, while Little Endian stores the least significant bit Bits are stored at the lowest memory address. In big-endian mode, byte sequences are stored in the same order as they are in the numeric sequence; in little-endian mode, byte sequences are stored in the reverse order of their numerical sequence. For example, a 32-bit integer with a hexadecimal value of 0x12345678 is stored in the order 12 34 56 78 in big-endian mode and 78 56 34 12 in little-endian mode. Different processors and operating systems may use different endian modes, so you need to pay attention to the differences in endian modes when exchanging and transmitting data.

So the hexadecimal we just solved is big-endian mode, and the way our computer stores data is little-endian mode.

When the inverse code performs the + 1 operation, the sign bit also needs to participate in the operation

Next, let’s see if the following code is correct

#include
int main()
{
unsigned int a = 10;//right
unsigned int b = -10;//?

//Compile without error
return 0;
}

In this code, -10 is assigned to the variable b of type unsigned int. Since unsigned int is an unsigned integer, its value range is 0~4294967295, so b actually stores 4294967286, which is the representation of the complement of -10 in the unsigned integer range.