1.數據類型介紹
內置類型
char //字符數據類型 1字節
short //短整型 2字節
int //整型 4字節
long //長整型 4/8字節
long long //更長的整型 8字節 (C99中引入的)
float //單精度浮點數 4字節
double //雙精度浮點數 8字節sizeof(long) >= sizeof(int)
4(32位平臺)/8(64位平臺) >= 4 字節
類型的意義
- 使用這個類型開辟內存空間的大小(大小決定使用范圍)
- 如何看待內存空間的視角
1.1類型的基本歸類
整型家族
char (字符的本質是ASCII碼值,屬于整型,所以劃分到整型家族) (char 類型 到底是signed char 還是unsigned char ,在C語言標準中是未定義的,取決于編譯器的實現) —— char 比較特殊unsigned charsigned charshortunsigned short [int]signed short [int]int (int a; ————> 這里的int類型默認指的是signed int) unsigned intsigned intlongunsigned long [int]signed long [int]long longunsigned long long [int] signed long long [int]
生活中有些數據是沒有負數的,比如身高,體重,長度等。 ——> unsigned
但有些數據是有負數的,比如溫度。——> signed
浮點型家族
float
double
只要是表示小數就可以使用浮點型。
float的精度低,存儲的數值范圍較小;double的精度高,存儲的數值范圍更大。
構造類型(自定義類型:我們可以自己創建出新的類型)
數組類型
結構體類型 struct
枚舉類型 enum
聯合類型 union
//數組類型
int arr1[5]; //數組類型:int [5]
int arr2[8]; //數組類型:int [8]
char arr3[5]; //數組類型:char [5]
指針類型
int* pi;
char* pc;
float* pf;
void* pv;
空類型
void 表示空類型(無類型)
通常應用于函數的返回類型、函數的參數、指針類型
void test(void) //第一個void 表示函數沒有返回值;第二個void 表示函數不需要傳任何參數
{}
2.整型在內存中的存儲
一個變量的創建是要在內存中開辟空間的。空間的大小是根據不同的類型而決定的。
//數值有不同的表示形式,2進制,8進制,10進制,16進制
//eg:21
//0b10101 —— 025 —— 21 —— 0x15
2.1原碼、反碼、補碼
整數的2進制表示也有3種表示形式:原碼、反碼、補碼。
1.正的整數,原碼、反碼、補碼相同。
2.負的整數,原碼、反碼、補碼是需要計算的。
原碼(直接通過正負的形式寫出的二進制序列)
反碼(原碼的符號位不變,其它位按位取反)
補碼(反碼+1)
int main()
{int a = 20;//00000000 00000000 00000000 00010100 - 原碼(二進制)//0x00 00 00 14 - 原碼、反碼、補碼(十六進制)//00000000 00000000 00000000 00010100 - 反碼(二進制)//00000000 00000000 00000000 00010100 - 補碼(二進制)int b = -10;//10000000 00000000 00000000 00001010 - 原碼(二進制)//0x80 00 00 0a - 原碼(十六進制)//11111111 11111111 11111111 11110101 - 反碼(二進制)//0xff ff ff f5 - 反碼(十六進制)//11111111 11111111 11111111 11110110 - 補碼(二進制) //0xff ff ff f6 - 補碼(十六進制)return 0;
}
整數在內存中存放的是補碼的二進制序列。
為什么呢?
在計算機系統中,數值一律用補碼來表示和存儲。原因在于,使用補碼,可以將符號位和數值域統一處理;同時,加法和減法也可以統一處理(CPU只有加法器)。此外,補碼與原碼相互轉換,其運算過程是相同的,不需要額外的硬件電路。
2.2大小端介紹
大端【字節序】存儲:把一個數據的高位字節序的內容放在低地址處,把低位字節序的內容放在高地址處。
小端【字節序】存儲:把一個數據的高位字節序的內容放在高地址處,把低位字節序的內容放在低地址處。
我們常用的X86結構是小端模式,而KEIL C51為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇大端模式還是小端模式。
//判斷大小端
#include <stdio.h>int main()
{int a = 1;if (*(char*)&a == 1){printf("小端\n");}else{printf("大端\n");}return 0;
}
//判斷大小端(封裝成函數)
#include <stdio.h>
int check_sys()
{int a = 1;if (*(char*)&a == 1){return 1;}else{return 0;}
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}
//判斷大小端(封裝成函數,簡潔版)
#include <stdio.h>
int check_sys()
{int a = 1;return *(char*)&a;
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}
3.浮點型在內存中的存儲
常見的浮點數:
3.14159
1E10(1.0*e的10次方,科學計數法)
浮點數家族:float、double、long double(C99中引入)類型。
浮點數表示的范圍:float.h中定義。
整數表示的范圍:limits.h中定義。
3.1浮點數存儲序列
根據國際標準IEEE(電氣和電子工程協會)754,任意一個二進制浮點數V可以表示成下面的形式:
(-1)^S * M * 2^E
- (-1)^S【-1的S次方】表示符號位。當S=0,V為正數;當S=1,V為負數。
- M表示有效數字。大于等于1,小于2.
- 2^E【2的E次方】表示指數位。
IEEE 754規定:
對于32位的浮點數,最高的1位是符號位S,接著的8位是指數E,剩下的32位是有效數字M。
對于64位的浮點數,最高的1位是符號位S,接著的11位是指數E,剩下的52位是有效數字M。
IEEE 754對有效數字M和指數E,還有一些特別的規定:
前面說過,1<=M<2,也就是說,M可以寫成1.xxxx的形式,其中xxxx表示小數部分。
IEEE 754規定,在計算機內部保存M時,默認這個數的第一位總是1,因此可以被舍去,只保存后面的xxxx部分。比如保存1.01時,只保存01,等到讀取的時候,再把第一位的1加上去。這樣做的目的:節省1位有效數字。
至于指數E,情況比較復雜。
首先,E為一個無符號整數(unsigned int),這意味著,如果E為8位,它的取值范圍是0-255;如果E為11位,它的取值范圍是0-2047。但是,我們知道,科學計數法中的E是可以出現負數的。所以IEEE 754規定,存入內存時E的真實值必須再加上一個中間數。對于8位的E,這個中間數是127;對于11位的E,這個中間數是1023。
int main()
{float f = 5.5f;//101.1 - 二進制//1.011*2^2 - 科學計數法//S=0,M=1.011,E=2(存儲時+中間值127=129)//0 10000001 011(后面不足23bit直接補0)00000000000000000000//01000000101100000000000000000000//0100 0000 1011 0000 0000 0000 0000 0000(寫出16進制)//0x40 b0 00 00return 0;
}
然后,指數E從內存這種取出還可以分成三種情況:
E不全為0或不全為1
這時,浮點數就采用下面的規則表示:即指數E的計算值減去127(或1023),得到真實值,再將有效數字M前加上第一位的1。
比如:
0 10000001 01100000000000000000000
(-1)^0 * 1.01100000000000000000000 * 2^2 【129-127=2】
E全為0
這時,浮點數的指數E等于1-127(或者1-1023),即位真實值。有效數字M也不再加上第一位的1,而是還原為0.xxxx的小數。這樣做是為了表示正負0,以及接近于0的很小的數字。
E全為1
這時,如果有效數字M全為0,表示正負無窮大(正負取決于符號位S)。
作業1:輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得數組中所有的奇數位于數組的前半部分,所有偶數位于數組的后半部分。
//輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得數組中所有的奇數位于數組的前半部分,所有偶數位于數組的后半部分。#include <stdio.h>void move_odd_even(int arr[], int sz)
{int left = 0;int right = sz - 1;while (left < right){//從左向右找一個偶數,遇到奇數停下來while ((left < right)&& (arr[left] % 2 == 1)) //可能存在越界,加上條件(left < right){left++;}//從右向左找一個奇數,遇到偶數停下來while ((left < right)&& (arr[right] % 2 == 0)){right--;}//交換奇數和偶數if (left < right){int tmp = arr[left];arr[left] = arr[right];arr[right] = tmp;left++;right--;}}
}
int main()
{int arr[10] = {0};//輸入int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){//scanf("%d", &arr[i]); //等價于下面代碼scanf("%d", arr+i);}//調整move_odd_even(arr, sz);//輸出for (i = 0; i < sz; i++){printf("%d ", arr[i]); }return 0;
}
作業2:有序序列合并
#include <stdio.h>
int main()
{int n = 0;int m = 0;scanf("%d %d", &n, &m);int arr1[n]; //C99支持的變長數組int arr2[m];//輸入n個整數int i = 0;for (i = 0; i < n; i++){scanf("%d", &arr1[i]);}//輸入m個整數for (i = 0; i < m; i++){scanf("%d", &arr2[i]);}//合并打印int j = 0; //n數組下標int k = 0; //m數組下標while (j<n && k<m){if (arr1[j] < arr2[k]){printf("%d ", arr1[j]);j++;}else{printf("%d ", arr2[k]);k++;}}if (j<n){for (; j < n; j++){printf("%d ", arr1[j]);}}else //k<m{for (; k < m; k++){printf("%d ", arr2[k]);}}return 0;
}
//如果要存儲在一個新數組里面
#include <stdio.h>
int main()
{int n = 0;int m = 0;scanf("%d %d", &n, &m);int arr1[n]; //C99支持的變長數組int arr2[m];int arr3[m+n];//輸入n個整數int i = 0;for (i = 0; i < n; i++){scanf("%d", &arr1[i]);}//輸入m個整數for (i = 0; i < m; i++){scanf("%d", &arr2[i]);}//合并打印int j = 0; //arr1數組下標int k = 0; //arr2數組下標int r = 0; //arr3數組下標while (j<n && k<m){if (arr1[j] < arr2[k]){arr3[r++] = arr1[j];//printf("%d ", arr1[j]);j++;}else{arr3[r++] = arr2[k];//printf("%d ", arr2[k]);k++;}}if (j<n){for (; j < n; j++){arr3[r++] = arr1[j];//printf("%d ", arr1[j]);}}else //k<m{for (; k < m; k++){arr3[r] = arr2[k];//printf("%d ", arr2[k]);}}//打印for (i = 0; i < m+n; i++){printf("%d ", arr3[i]);}return 0;
}
總結
今天就暫且更新至此吧,期待下周再會。如有錯誤還請不吝賜教。希望對您學習有所幫助,翻頁前留下你的支持,以防下次失蹤了嗷。
作者更新不易,免費關注別手軟。