逆向基础之C语言 第一篇
逆向之C語言
- 00_C語言概述
- 知識點1 【寫代碼的過程】
- Visual C++ 6.0
- 安裝VC6.0
- 配置兼容性
- 創建c工程
- 添加源文件
- 知識點2 【完整的C代碼分析】
- 1、案例1:hello iot
- 2、案例2:求圓的面積
- 3、案例3:用分函數的方式求兩個數的和
- 01_C語言的基本語法
- 知識點1【數據類型】
- 案例:驗證數據類型的長度
- 知識點2【unsigned和signed】(重要)
- 1、無符號數unsigned
- 2、有符號數signed 默認一般省略
- 知識點3 【結構體struct 和 共用體union】
- 1、struct 結構體中的 成員擁有 獨立的空間
- 2、union 共用體中的成員 共享同一份空間
- 知識點4【enum枚舉 和 void無類型】
- 知識點5【其他關鍵字】(了解)
- 1、register 寄存器變量
- 2、typedef 為已有的類型取一個別名
- 3、volatile 防止編譯器優化 強制訪問內存操作
- 知識點6 【常量與變量】(重要)
- 1、常量 值不能被修改 (千萬不能反過來說:值不能被修改的就是常量X)
- 2、變量 系統根據變量的類型 開辟對應的空間 其值可以被修改
- 知識點7【八進制 十進制 十六進制】
- 知識點9 【整型變量的操作 - 讀(取值)、寫(賦值)】
- 1、取值與賦值——讀寫操作
- 2、整型的輸出形式
- 知識點10 【實型 常量】
- 知識點11 【字符常量和變量】(重要)
- 1、字符常量char
- 案例: 鍵盤輸入“abc”,只取‘a’和‘c’
- 知識點12 【轉義字符】
- 知識點13 【字符串】“”雙引號作用
- 知識點14 【輸出的格式回顧】(了解)
- 知識點15 【typedef】
- 02_C語言的基本語句
- 知識點1【數據類型轉換】
- 1、自動類型轉換(保證精度不丟失)
- **轉化的方向**
- 案例:有符號和無符號的轉換
- 案例:int和double的轉換
- 案例:char和short的類型轉換
- 2、強制類型轉換
- 知識點2 【運算符】
- 1、算術運算符(+、-、*、/、%)
- 案例:鍵盤輸入一個數 ,是否能被3整除
- 2、邏輯運算符(!、&&、||)
- ① 邏輯非 ——!0 == 真 、 !真 == 假
- ② 邏輯與 &&(重要)
- ③邏輯或 ||
- 綜合案例:
- 知識點3 【位運算符】二進制位操作(<< 、>> 、& 、| 、~ 、^)
- &:按位與
- |:按位或
- 案例:將1010 1010的第2、3位變成1,其他不變
- ~:按位取反
- ^:按位異或 (重要)
- 案例:將1010 1010的第0位發生翻轉(要將哪位翻轉,就將它與1異或)
- <<:左移運算符(左邊丟棄,右邊補0)
- >>:右移運算符
- 知識點4【三目運算符】?:
- 案例
- 知識點5【逗號運算符】
- 案例:逗號運算符
- 知識點6【符合運算符】
- 案例
- 知識點7【自增自減 運算符】
- 知識點7【優先級】分析已有的代碼
- 知識點8【if 語句】
- 1、如果只在乎項目的一個結果,選擇if
- 2、如果項目只有**兩種結果**,且**不會同時出現**。請選擇if....else....
- 3、如果一個項目 有多個結果 且不同時出現。選擇if....else if...else if...else
- 4、一個項目有多個結果,不確定是否同時出現
- 知識點9【switch 選擇語句】
- 案例:有時候break可以省略
- 03_循環語句及數組
- 知識點1 【for循環】
- 案例: 求1~100的和
- 案例:
- 案例:break只能跳出離它最近的一層循環
- continue
- 案例:求出0~100的所有偶數的和
- break和continue的區別
- 循環嵌套循環
- 知識點2【while 循環 和 do...while 循環】
- 案例:
- 案例
- 知識點3【goto 跳轉】(慎用!)
- 知識點4【數組的概述】
- 知識點5【一維數值數組】
- 1、數組的定義
- 案例1:遍歷數組
- 案例2:數組的初始化
- 全部初始化:
- 部分初始化:
- 拓展:特殊初始化
- 2、數組的空間大小(重要)
- 遍歷數組
- 案例:
- 3、數組元素的操作
- 案例:定義一個數組 5個元素,每個元素int類型,獲取鍵盤輸入
- 作業:定義一個數組 5個元素,每個元素int類型,獲取鍵盤輸入,請求出數組的最大值,最小值,平均值。
- 知識點6【二維數組】
- 1、二維數組的定義
- 二維數組的遍歷:
- 案例:
- 2、二維數組的初始化
- 分段初始化:用大括號里面的大括號,明確的表示一行
- 連續初始化:放滿一行才能放下一行
- 案例:
- 04_數組與函數
- 知識點1 【作業講解】
- **作業:定義int arr[3][4]的二維數組,獲取鍵盤輸入,并求出每一行的平均值。**
- 案例:求每一列的平均值
- 知識點2 【字符數組】(重要)
- 字符數組的初始化
- 字符數組的遍歷
- 逐個字符初始化和字符串初始化的區別
- 字符數組獲取鍵盤的字符串
- scanf和%s使用的時候有個缺點,遇到**空格**會結束輸入
- **gets()缺點:獲取鍵盤輸入的時候,不會管buf的大小,容易造成內存污染**
- fgets():既可以獲取帶空格的字符串,也可以保證buf的不越界
- 案例:
- 提高案例:字符串的大小寫的轉換
- 知識點3【二維字符數組】(了解)
- 二維字符數組獲取鍵盤輸入的字符串:
- 知識點4 【函數概述】
- 1、函數的定義
- 2、函數的聲明
- 3、函數的調用
- 知識點5 【函數的參數】
- 1、如果函數的形參什么都不寫 在調用的時候可以傳實參,只是實參得不到使用
- 2、如果函數沒有參數,請將形參寫成void
- 3、函數參數的傳遞
00_C語言概述
知識點1 【寫代碼的過程】
編輯器:程序員寫代碼的過程(記事本、VC6.0、vim…)(讓程序員看懂)
編譯器:查看代碼的語法錯誤,生成匯編語言。
匯編器:將生成好的匯編語言生成二進制語言(也叫做目標文件)
連接器:將生成好的二進制語言+用到的庫+啟動代碼=====>可執行文件
- Windows開發特點:
- 在電腦上裝一個VC或者其他集成開發環境
- 編輯程序——》編譯程序——》看現象——》有問題——》修改程序——》調試程序 ——》看現象
- Linux開發特點:
- Linux下程序開發大多數通過在本地安裝虛擬機、物理機或者網絡連接到服務器完成
- 出于效率、遠程開發、嵌入式開發的考慮:開發方式太多 是在命令行下完成,沒有很好的集成開發環境供我們使用
Visual C++ 6.0
安裝VC6.0
Visual C++6.0下載地址:https://pan.baidu.com/s/1UoyJD6FUARS2EvDdC9l7qA 提取碼: 1314.
解壓單擊安裝即可,默認下一步
配置兼容性
- 鼠標右鍵點擊桌面生成的快捷方式,選擇屬性
- 點擊兼容性,找到兼容模式的位置,在“以兼容模式運行這個程序”前打勾,在下方下拉菜單中選擇Windows XP(Service Pack3),點擊確定即可
-
創建c工程
打開VC6.0,關閉每日提示
文件——》新建——》工程——》win32 console Application控制臺程序——》確定
從0開始學,選擇一個空工程——》完成
單擊FileView就可以看到工程的目錄結構
添加源文件
文件——》新建——》文件——》C++ Source File——》寫文件名(后綴名為.C,若不寫則默認為.cpp為C++源文件)——》確定
知識點2 【完整的C代碼分析】
1、案例1:hello iot
# include<stdio.h> //std是標準的意思 i表示輸入 o表示輸出(標準的輸入輸出頭文件) // 雙斜杠//是行注釋 /**/是塊注釋,注意塊注釋不能嵌套//()里面的是函數的形參(函數外部將數據傳遞到函數內部的橋梁) int main(int argc, char *argv[]) //main主函數 是程序的入口,有且僅有一個主函數 //main左邊的int代表的是函數的返回類型 {// 使用任何東西 必先存在printf("hello iot\n");// printf來自系統庫文件,printf:將雙引號的字符串輸出(o)到終端上return 0;//0是一個int類型 } //{}函數的功能都在大括號里面實現,每敲一行代碼記得用tab縮進,不要用空格 //分號是C語言的結束標記 //如果你的代碼一閃而過 可以用帶阻塞的代碼 getchar(); // int char return都是關鍵字總結:
1、main有且只有一個
2、printf的頭文件是stdio.h
3、注釋 行注釋和塊注釋(塊注釋不能嵌套)
調整VC6.0字體大小:工具——》選項——》三角符號往后點有個格式——》選擇原窗口——》選擇合適大小
2、案例2:求圓的面積
知道的條件:半徑r
算法:面積=πrr
解析步驟:
①r通過鍵盤輸入或者用特定的值
②定義一個面積變量 area=πrr
③將圓的面積輸出到終端上
運行結果:
3、案例3:用分函數的方式求兩個數的和
步驟分析:
1、定義兩個變量data1、data2 獲取鍵盤輸入
2、定義一個函數 去計算上面兩個變量data1、data2的和
在函數內部計算計算(怎么將數據傳遞到函數內部?)
需要用形參 將data1 data2傳遞到函數內部
3、得到函數的計算結果(怎么得到呢?)
通過函數的返回值 得到函數的計算結果
運行結果:
01_C語言的基本語法
知識點1【數據類型】
數據分為不同類型 是為了 更加合理的利用內存空間
計算機存儲的是二進制,一位二進制只能存放0或者1(低電平或高電平)
1byte=8bit (0000 0000~ 1111 1111)
1kb=1024byte 10月24日是程序員節
1MB=1024kb
1GB=1024MB
1TB=1024GB
1PB=1024TB
1EB=1024PB
-----------------------------------在32位平臺-----------------------------------------
char 字符類型 占1個字節 (8個二進制位)
short 短整型 占2個字節 (16個二進制位)
int 整型 占4個字節(32個二進制位)
long 長整型 占4字節空間(32個二進制位)
float 單精度浮點型 占4字節空間(32個二進制位)
double 雙精度浮點型 占8字節空間(64個二進制位)
-----------------------------------在64位平臺-----------------------------------------
char 字符類型 占1個字節 (8個二進制位)
short 短整型 占2個字節 (16個二進制位)
int 整型 占4個字節(32個二進制位)
long 長整型 占8字節空間(64個二進制位)
float 單精度浮點型 占4字節空間(32個二進制位)
double 雙精度浮點型 占8字節空間(64個二進制位)
案例:驗證數據類型的長度
sizeof是測量類型的長度
#include<stdio.h> int main(int argc,char *argv[]) {printf("sizeof(char)=%d\n",sizeof(char));printf("sizeof(short)=%d\n",sizeof(short));printf("sizeof(int)=%d\n",sizeof(int));printf("sizeof(long)=%d\n",sizeof(long));printf("sizeof(float)=%d\n",sizeof(float));printf("sizeof(double)=%d\n",sizeof(double));return 0; }運行結果:
知識點2【unsigned和signed】(重要)
1、無符號數unsigned
數據沒有符號位,自身所有二進制位都是數據位
比如:unsigned char ——》 0000 0000~ 1111 1111
2、有符號數signed 默認一般省略
二進制的最高位為符號位,其他為是數據位
signed char ——》 xxxx xxxx(x為0或1)
最高位為1表示負數,最高位為0表示正數
比如:一個字節: -10==1000 1010
案例:
#include<stdio.h> int main(int argc,char *argv) {//定義一個有符號intsigned int num1=10; //系統給num1開辟4字節的空間(這個4字節的最高位是符號位)// signed 默認是省略的(推薦)int num2=10; //num2也是有符號數//unsigned表示無符號數 不能省略unsigned int num3=10;return 0; }知識點3 【結構體struct 和 共用體union】
1、struct 結構體中的 成員擁有 獨立的空間
struct data1{char a;short b;int c; }; a b c就是結構體data1中的成員2、union 共用體中的成員 共享同一份空間
union data2{ char a; short b; int c; };
知識點4【enum枚舉 和 void無類型】
1、enum枚舉 將變量要賦值的值一一列舉出來
enum BOOL(false,true);
enum BOOL bool=false;
2、void 無類型 (重要)
不能用void定義變量
int num; // √
void num; //X 系統不能確定給num開辟多大的空間
知識點5【其他關鍵字】(了解)
auto 自動類型 ,register 寄存器變量,static 靜態變量,const 只讀變量
sizeof 測類型的大小
typedef 為已有的類型 重新取一個別名
volatile 防止編譯器優化
1、register 寄存器變量
總結:
1、如果沒有顯示表明register,就類似int num,如果num被高頻繁使用系統也會放入寄存器中。
2、register int num; //顯示的將num放入寄存器中
3、寄存器的變量不能取地址 &num(X)
2、typedef 為已有的類型取一個別名
#include<stdio.h> void test01() { register int num=10;// %p輸出地址//printf("%p\n",&num); //**錯誤**,不能對寄存器變量 取地址 }typedef int INT32; //給已有的int類型 取了一個別名叫INT32 void test02() {int num1=10;INT32 num=20;printf("num1=%d\n",num1);printf("num2=%d\n",num2); }int main(int argc,char *argv[]){test02();//想運行那個就寫哪個return 0; }3、volatile 防止編譯器優化 強制訪問內存操作
知識點6 【常量與變量】(重要)
1、常量 值不能被修改 (千萬不能反過來說:值不能被修改的就是常量X)
10 20 4.14 ‘a’ “abv”
2、變量 系統根據變量的類型 開辟對應的空間 其值可以被修改
變量名num代表的是空間的內容
操作變量名就是對空間 內容 的操作
變量名的命名規則:由數字字母下劃線組成,但是不能以數字開頭
建議:小寫英文字母 + "_"線 構成(C語言命名風格)
C++一般用小駝峰法命名:numName
知識點7【八進制 十進制 十六進制】
十進制: 0~9
八進制:0~7
十六進制:0 ~ 9 ,a ~ f
八進制 十進制 十六進制都是整型的輸出形式
十進制輸出 %d %u %ld %lu
八進制輸出 %o
十六進制輸出 %x
八進制是以0開頭
printf("八進制:num=%#o\n",num); //0144十六進制是以0x開頭
printf("十六進制:num=%#x\n",num); //0x64不同的進制僅僅是數據的表現形式, 并不能修改數據本身
知識點9 【整型變量的操作 - 讀(取值)、寫(賦值)】
1、取值與賦值——讀寫操作
void test01() {//在大括號內部定義的變量叫局部變量(局部變量不初始化,內容隨機)int data=0;int num=0;printf("num=%d\n",num);// 讀操作 取值//寫操作 賦值 num=100;printf("num=%d\n",num);data=num; //將右邊的值賦值給左邊 ,對于num來說是讀取它的值100(讀操作),賦值給data(寫操作)printf("data=%d\n",data);//獲取鍵盤輸入printf("請輸入一個int數據:");scanf("%d",&data); //&data 代表的是data空間的起始地址printf("data=%d\n",data); }運行結果:
2、整型的輸出形式
void test02() {int num1=0;//%d 有符號int數據輸出printf("num1=%d\n",num1);unsigned int num2=0;//%u 無符號int數據輸出 (u是unsigned的簡寫)printf("num2=%u\n",num2)long num3=0;//%ld 有符號long型數據輸出printf("num3=%ld\n",num3);unsigned long num4=0;//%lu 無符號long型數據輸出printf("num3=%lu\n",num4);long long num5=0;//%ld 有符號long long型數據輸出printf("num3=%lld\n",num3);unsigned long long num6=0;//%lu 無符號long long型數據輸出printf("num3=%llu\n",num4);//注意:VC6.0不支持long long但在其他集成環境支持short num7=0; //short本來就是整型,所以一般不寫int// %hd是有符號short數據輸出printf("num7=%hd\n",num7);short num8=0; //short本來就是整型,所以一般不寫int// %hu是無符號short數據輸出printf("num8=%hu\n",num8); }unsigned short num6=0;
scanf(“num6=%hu\n”,num6); //輸入
x
知識點10 【實型 常量】
注意:賦值語句 等號 兩邊類型盡量保證一致
%lf double型浮點型
加f的是float類型,不加f的是double類型
void test03() {//不以f結尾的實型常量 為double類型printf("sizeof(3.14)=%d\n",sizeof(3.14));//8byte//sizeof針對于變量可以不加小括號,針對類型要加小括號//為了通用 最好都加小括號//以f結尾的實型常量 為float類型printf("sizeof(3.14)=%d\n",sizeof(3.14f));//4bytefloat f=3.14; //有沒有問題? 賦值語句要保證兩邊類型相同// f是float類型4字節 3.14是double類型 8字節//把8字節賦值給4字節(這樣賦值是有問題的) float f=3.14f;(√)//%f輸出 float數據printf("f=%f\n",f);//%lf 輸出 double數據double d=3.14;printf("%d=%lf\n",d)scanf("%f",&f);scanf("%lf",&d);}float類型占四個字節
double類型占八個字節
知識點11 【字符常量和變量】(重要)
直接常量:用單引號括起來,如:‘a’ 、 ‘b’ 、‘12’等
轉義字符:以反斜杠\開頭,后面跟一個或者幾個字符,如’\n’ '\t’等,分別代表換行、橫向跳格
1、字符常量char
void test01() {// %c輸出的是字符 printf("%c\n",'a');//字符常量//字符變量//ch存儲的是'a'的ASCII值char ch='a';// %c 輸出的是符號本身printf("ch=%c\n",ch);// ch=a// %d 輸出的是字符的ASCII值printf("ch=%d\n",ch);// ch=97// 'a' 單引號表示取字符的ASCII// 'a'==97是完全等價ch=97;printf("ch=%c\n",ch); //ch=aprintf("ch=%d\n",ch); //ch=97ch=ch+1;printf("ch=%c\n",ch); //ch=bprintf("ch=%d\n",ch); //ch=98}ASCII碼表:
總結:
1、‘a’ 單引號表示取a的ASCII值
2、字符在計算機中存儲的是ASCII
3、‘a’==97
思考:
char ch='a'; printf("%d\n",sizeof('a'));//4byte sizeof測量的是小括號里面類型的長度 printf("%d\n",sizeof(char));//1byte 為什么?‘a’,單引號是取字符的ASCII值——》97,本質是sizeof(97); 97是int類型,所以是4byte。
字符類型如何去獲取鍵盤輸入呢?
void test03() {char ch;printf("請輸入一個字符");//字符變量獲取鍵盤輸入scanf("%c",&ch);//scanf中%c只能**提取一個字符**printf("ch=%c\n",ch);//輸出字符用%cprintf("ch=%d\n",ch);//輸出ASCII用%d//ch=getchar();//這也是獲取一個字符,把值賦給變量ch//getchar是一個函數調用,其實scanf也是一個函數調用 }運行結果:
案例: 鍵盤輸入“abc”,只取‘a’和‘c’
char ch1,ch2;printf("請輸入abc:");//提取ach1=getchar();getchar();//丟棄一個字符ch2=getchar();//提取cprintf("ch1=%c\n",ch1);printf("ch2=%c\n",ch2);運行結果:
知識點12 【轉義字符】
// '\n'換行 '\t' tab//printf("##%c##\n",'\\');//兩個反斜杠輸出一個反斜杠//printf("#%%90#\n");//兩個百分號輸出一個百分號//ABCD中哪兩個相等?BC//判斷哪兩個相等,其實就是查看這兩個在計算機中存放的ASCII值是不是一樣printf("A:%d\n",'0');//字符0的ASCII是48 字符本質存放的就是ASCIIprintf("B:%d\n",'\0');//’\0‘的ASCII值是0printf("C:%d\n",0); //0printf("D:%d\n","0");//字符串0 取的是字符0的地址
知識點13 【字符串】“”雙引號作用
%s的本質作用就是:從字符串的首元素地址開始,逐個字符輸出,遇到’\0’自動結束輸出(重要)
請描述‘a’和“a”的區別:
1、單引號a只占一個字節,雙引號a占2個字節(一個字節存放字符a,一個字節存放\0)
2、'a’取的是字符a的ASCII值,雙引號a取的是字符串a的首元素的地址
"ab"作為類型代表的的字符串占空間的大小
“ab”作為地址 代表的是字符串 首元素 的地址
知識點14 【輸出的格式回顧】(了解)
格式化輸出字符:
%d 十進制有符號整數
%x 以十六進制表示的整數
%f float型浮點數
%e 指數形式的浮點數
%c 單個字符
%u 十進制無符號整數
%o 一八進制表示的整數
%lf double型浮點數
%s 字符串
%p 指針的值
特殊應用:
%3d , %03d , %-3d , %5.2f
運行結果:
知識點15 【typedef】
為已有的類型重新取個別名
★步驟:
1、用已有的類型定義一個變量
2、用別名替換變量名
3、在整個表達式的前方加上typedef
02_C語言的基本語句
知識點1【數據類型轉換】
1、自動類型轉換(保證精度不丟失)
轉化的方向
案例:有符號和無符號的轉換
int data1=-20; unsigned int data2=10;//有符號數data1和無符號數data2進行轉換的時候//會先將data1轉換為無符號(-20的補碼——》很大的正數)//很大的數 + 10 必然 >0if(data1+data2>0){printf(">0\n");// √}else if(data1+data2<0){printf("<0\n");}運行結果是:大于0
案例:int和double的轉換
int data1=10;//int和double轉換時,會將int轉換為doubleprintf("%d\n",sizeof(data1+3.14));// double是8字節案例:char和short的類型轉換
char ch='a';short data=20;//由于char,short自身字節數過小,很容易溢出//所以,只要char,short參與運算,都會將自身轉換成intprintf("%d\n",sizeof(ch+ch));//4printf("%d\n",sizeof(data+ch));//4printf("%d\n",sizeof(data+data));//42、強制類型轉換
(類型說明符)(表達式)
例如:
(float)a;
(int)(x+y);
注意:無論是強制轉換還是自動轉換,都只是為了本次運算的需要,而對變量的數據長度進行臨時性轉換,而不改變數據定義的類型。
知識點2 【運算符】
運算對象:常量、變量、函數(返回值)等
1、算術運算符(+、-、*、/、%)
a/b (a,b都為整數) 取整
%取余
案例:鍵盤輸入一個數 ,是否能被3整除
int num=0;//鍵盤輸入一個整數printf("請輸入一個整型數據:");scanf("%d\n",&num);//判斷能否被3整除if(num%3==0){printf("ok\n");}else{printf("no\n");} printf("%d\n",5/2);//如果反斜杠兩邊都是整數,就是單純的取整 2printf("%f\n",5/2.0f);//如果有浮點型,就是真正的除法 2.5運行結果:
2、邏輯運算符(!、&&、||)
① 邏輯非 ——!0 == 真 、 !真 == 假
printf("%d\n",!1);//0printf("%d\n",!0);//1//C中0為假,其他都為真 -1也是真printf("%d\n",!-1);//0② 邏輯與 &&(重要)
A&&B —— A、 B同時為真 則 整個表達式,AB只要有一個為假,結果就為假。
if((3>2) && (5>4)){printf("ok\n");//ok}else{printf("no\n");}邏輯與有短路特性
A&&B ——》如果發現A為假,系統不會判斷執行B,這就是短路特性
③邏輯或 ||
A||B 只要AB任意一個為真,整個表達式都為真,AB同時為假的時候,結果才為假。
邏輯非也有短路特性:只要A為真,編譯器不會判斷B的真假
int num=10;printf("比較之前num=%d\n",num);//10(3>2) || (num=100);printf("比較之后num=%d\n",num);//10 num=100得不到執行綜合案例:
知識點3 【位運算符】二進制位操作(<< 、>> 、& 、| 、~ 、^)
&:按位與
語法:全1為1,其他為0
1010 1010 & 1111 0000 -------------------1010 0000特點:和1相與 保持不變,和0相與 清零
應用場景:將固定位清零
|:按位或
語法:有1就為1,全0才為0
1010 1010 | 1111 0000 -------------------1111 1010特點:和0相或 保持不變,和1相或置1
應用場景:將固定位置1
案例:將1010 1010的第2、3位變成1,其他不變
1010 1010 | 0000 1100 -------------------1111 1010~:按位取反
語法:0變1,1變0
~ 1010 1010=0101 0101應用場景:配合&|操作
^:按位異或 (重要)
語法:相同為0,不同為1
1010 1010 ^ 0000 1111 -------------------1010 0101特點:和0異或保持不變,和1異或 取反
應用場景:將固定的位 發生高低電頻翻轉
案例:將1010 1010的第0位發生翻轉(要將哪位翻轉,就將它與1異或)
1010 1010 ^ 0000 0001 -------------------1010 1011<<:左移運算符(左邊丟棄,右邊補0)
(將二進制位向左移動)
注意:移動的位數不要超過自身長度
1010 1100 << 2(左移2位)
>>:右移運算符
右移分為:邏輯右移和算術右移
邏輯右移:右邊丟棄,右邊補0
算術右移:
①無符號數:右邊丟棄,左邊補0
②有符號數:
<1>正數:右邊丟棄,左邊補0
<2>負數:右邊丟棄,左邊補1
右移基本上是右邊丟棄,左邊補0
只有負數且算術右移,才會左邊補1
邏輯右移和算術右移是編譯器決定,但是我們可以檢測。
作業:自己寫代碼,判斷你的編譯器是邏輯右移還是算術右移
char ch=0x85; // 1000 0101>>4//邏輯右移:1000 0101>>4 == 0000 1000 ==0x08//算術右移:1000 0101>>4 == 1111 1000 ==0xf8ch=ch>>4;printf("ch=%#x\n",ch);//0xf8是算術右移//邏輯右移、算術右移是編譯器決定的,我們只能測試,不能改變綜合案例:將data第1,5位清零(按位與),3、4位置1,其他位保持不變★
unsigned char data=0xaa; //1010 1010
//將data第1,5位清零
data = data & 1101 1101;// 與 完之后要賦值為data,才能更新它的值
1101 1101= ~(0010 0010)取反= ~(0010 0000 | 0000 0010)
0010 0000= 0000 0001 <<5
0000 0010= 0000 0001 <<1
所以:1101 1101= ~(0x01 <<5 | 0x01<<1)
data=data & ~(0x01<<5 | 0x01 <<1);//很清楚的能看出是第一位和第五位清零
第3、4位 置1
data=data | 0001 1000;
0001 1000 = 0001 0000 | 0000 1000=0x01<<4 | 0x01<<3
data = data | (0x01<<4 | 0x01<<3);
規律:只有按位與有取反,清零就 與、取反,后面第幾位就寫左移第幾位;要置1,只寫或,不寫取反,里面第幾位,就寫左移第幾位,每一位用或或上
知識點4【三目運算符】?:
表達式1 ? 值1:值2
語法:如果表達式1為真,那么整個表達式的值為“值1”,反之為“值2”
案例
int ret=0;//最好先初始化再去賦值ret=3>2 ? 5:6;printf("ret=%d\n",ret);//ret=5知識點5【逗號運算符】
案例:逗號運算符
int data1=0;int data2=0;//賦值運算符和逗號運算符,逗號運算符優先級其實是最低的data1=3,4,5,6;//先執行data1=3data2=(3,4,5,6);//小括號優先級最高,逗號運算符結合性是從左往右,所以data2=6printf("data1=%d\n",data1);//3printf("data2=%d\n",data2);//6
知識點6【符合運算符】
+= 、 -= 、 = 、 /= 、%= 等等
a+=b ===》a=a+b
a * =b ===》a= a * b
注意:等號的右邊必須看成一個整體
案例
int data = 3;//將等號右邊看成一個整體data *= 3+5;//data=data * (3+5);printf("data=%d\n",data);//24知識點7【自增自減 運算符】
++i 或者 --i :先加、減 ,后使用
i++或者i–:先使用,后加、減
注意:i++或者++i作為單獨的指令,是沒有區別的
知識點7【優先級】分析已有的代碼
優先級高的先執行,同級別的優先級要看結合性
C語言運算符的優先級
自己寫代碼,盡量加小括號
知識點8【if 語句】
1、如果只在乎項目的一個結果,選擇if
if(表達式)
{
????語句1;
}
表達式為真,才會執行語句1;
2、如果項目只有兩種結果,且不會同時出現。請選擇if…else…
if(表達式)
{
????語句1;
}
else
{
????語句2;
}
表達式為真執行語句1,否則執行語句2
3、如果一個項目 有多個結果 且不同時出現。選擇if…else if…else if…else
if(表達式1)
{
????語句1;
}
else if(表達式2) //if后面要加表達式
{
????語句2;
}
else //可以省略
{
????語句n;
}
只有表達式1位真,執行語句1,;只有表達式2位真,執行語句2;所有表達式都為假,才執行語句n。
注意:只有前面的條件不滿足,才會判斷后面的條件,如果前面的條件滿足,后面的條件不管真假都不會執行。
4、一個項目有多個結果,不確定是否同時出現
if(表達式1)
{
????語句1;
}
if(表達式2)
{
????語句2;
}
if(表達式3)
{
????語句3;
}
每個if語句都是獨立的。
作業:鍵盤輸入一個數,能否被3和7同時整除
知識點9【switch 選擇語句】
switch(表達式)
{
case 值1: // 不能是實型、字符串,只能是整型和字符(本質是ASCII)
????????語句1;
????????break;
case 值2:
????????語句2;
????????break;
case 值3:
????????語句3;
????????break;
default: //可以省略
????????語句n;
????????break;
}
switch就是判斷表達式的值是否和值1,值2,值3相等,來找到接下來程序的進入點。
int data=0;printf("請輸入一個int數據:");scanf("%d",&data);//判斷data對3的余數(有0,1,2三種情況)switch(data%3){case 0:printf("余數為0\n");break;case 1:printf("余數為1\n");break;case 2:printf("余數為2\n");break;}案例:有時候break可以省略
void test23() {char ch=0;printf("請輸入你的方向:wasd\n");ch=getchar();switch(ch){case 'w':case 'W':printf("向上移動\n");break;case 'a':case 'A':printf("向左移動\n");break;case 's':case 'S':printf("向下移動\n");break;case 'd':case 'D':printf("向右移動\n");break;}}03_循環語句及數組
知識點1 【for循環】
for(初始語句; 循環條件 ; 步進條件) {//循環語句 } //初始語句:只在循環開始時執行一次 //循環條件:每次循環都要執行,如果循環條件為真,進入循環體 如果為假,退出循環體 //步進條件:每次循環結束的時候要執行的語句案例: 求1~100的和
//1~100的和 1+2+3+4+...+100int i =0;int sum=0;for(i=1;i<=100;i++)// i++ ==> i=i+1{sum=sum+1}printf("sum=%d\n",sum);案例:
//1~100的和 1+2+3+4+...+100int i =1;int sum=0;//如果變量提前初始化了,for循環的初始化語句可以省略for(;;)//建議不要這么做{//如果for省略循環條件,必須在循環語句中實現退出循環的條件if(i>100)break;//跳出循環sum=sum+i;i++;//如果for省略步進條件,必須在循環語句中實現步進動作}printf("sum=%d\n",sum);案例:break只能跳出離它最近的一層循環
void test26(){for(;;){for(;;){break;//只能跳出離他最近的一層循環}}}continue
continue:結束本次循環,立即從當前位置直接進入下一次循環
作業:
1、求出1~100中能被7整除的數。
案例:求出0~100的所有偶數的和
//求0 2 4 6...100的和int i=0;int sum=0;for(;i<=100;i=i+2)//步進條件不是單純的i++{if(i==50)continue;//結束本次循環,立即從當前位置直接進入下一次循環sum+=i;}printf("sum=%d\n",sum);//2550break和continue的區別
break語句用于跳出本層循環
continue用于結束本次循環
循環嵌套循環
//外層循環的次數 * 內層循環的次數 == 總循環次數 void test28(){int i=0;int j=0;for(i=0;i<10;i++)// 0~9=10{for(j=0;j<10;j++)// 0~9=10{printf("i=%d,j=%d\n",i,j);//輸出幾次?100次}} }知識點2【while 循環 和 do…while 循環】
while(循環條件) {//循環語句 }//如果循環條件為真就進入循環體執行循環語句注意:
1、while沒有初始化語句 ,用戶要提前初始化
2、while沒有步進語句, 用戶必須在循環語句中寫好步進語句。
案例:
int i=1;int sum=0;//注意初始化while(i<=100)//1~100和{sum+=i;i++;}printf("sum=%d\n",sum);//5050
注意:局部變量如果不初始化,內容不確定
do {//循環語句 }while(循環條件);
先執行循環語句,在判斷循環條件是否滿足, 如果為真,進行下一次循環,如果為假,直接退出循環
案例
int num=0;do{printf("ok\n");}while(num>0);//先進do再去判斷條件,所以會將ok打印出來知識點3【goto 跳轉】(慎用!)
void test31(){printf("---------001---------\n");printf("---------002---------\n");goto here;//注意這里是分號printf("---------003---------\n");printf("---------004---------\n");printf("---------005---------\n"); here://注意這里是冒號printf("---------006---------\n");printf("---------007---------\n"); }int main(int argc,char *argv[]){test31(); return 0; }
作業:
重復輸入1~7的數字判斷星期幾,注意輸入0,就結束程序,如果是0或1 ~7以外的數,請提示“請重新輸入有效數值“
總結:
for 和 while 我們如何選擇呢?
for和while低位一樣
建議:
如果循環次數是確定的,建議選擇for。
如果循環次數不確定,只知道退出的條件,建議選擇while
知識點4【數組的概述】
知識點5【一維數值數組】
1、數組的定義
需求:請定義一個數組,該數組有十個元素,每個元素為int類型
在定義的時候:(按照步驟來,因為以后的數組很復雜)
a、arr[ ] arr和中括號結合時就是數組
b、將確定的元素的個數放入中括號中
c、用元素的類型定義一個普通變量
d、從上往下整體替換(變量名)
arr[10]
int num;
用arr[10]整體替換num——》int arr[10];
需求:請定義一個數組,該數組有十個元素,每個元素為地址,每個地址為int變量的地址。
arr[10]
*p //每個元素為地址,地址就是指針
int num;
//從上往下依次替換
arr[10]替換p——》 * arr[10]
星花 * arr [10]替換num——》int * arr [10]
注意:
1、數組名arr不能和其他變量名同名
2、數組的元素下標是從0開始的:0~9
3、數組的元素分別是:arr[0]、arr[1]…arr[9],如果訪問arr[10]數組越界
4、數組的元素等價于普通變量
5、在定義數組的時候,中括號里面的值必須為常量,不能是變量。
案例1:遍歷數組
//局部數組(變量)如果不初始化,內容也是不確定的int arr[10];//遍歷數組——》for循環int i=0;for(i=0;i<10;i++){printf("%d ",arr[i]);//輸出的都是不確定的值}printf("\n");案例2:數組的初始化
初始化:定義的時候給變量或者數組元素賦值的動作,叫做初始化。
全部初始化:
int arr[5]={10,20,30,40,50}; //如果是全部初始化,數組元素的個數可以省略,實際元素個數由初始化個數決定 int arr[]={10,20,30,40,50};//少用//錯誤演示: int arr[];//沒有初始化,不知道開辟多大空間部分初始化:
int arr[5]={10,20,30};//部分初始化,未被初始化的部分補0(重要)//初始化數字常見操作(將數組所有元素清零) int arr[5]={0};//初始化arr[0]=0,未被初始化部分自動補0 int arr[5]={2};//2 0 0 0 0拓展:特殊初始化
//了解 int arr[5]={[2]=3,[4]=7}; // 0 0 3 0 7 //[2]=3 將數組的第二個元素 初始化為 3 //[4]=7 將數組的第四個元素 初始化為 72、數組的空間大小(重要)
//arr數組名 作為數組類型 代表的是數組空間的總大小int arr[5]={10,20,30,40,50};int n=0;printf("數組的總大小:%d\n",sizeof(arr));//20個字節//數組的總大小=元素的個數*每個元素的大小// 20 = 5 * 4//數組元素的大小 arr[0]是數組第0個元素,數組的每個元素都是相同類型printf("數組元素的大小=%d\n",sizeof(arr[0]));//4//數組元素的個數=數組的總大小 / 每個元素的大小n= sizeof(arr) / sizeof(arr[0]);printf("數組元素的個數為:%d\n",n)//5;遍歷數組
//arr數組名 作為數組類型 代表的是數組空間的總大小int arr[5]={10,20,30,40,50};int n= sizeof(arr) / sizeof(arr[0]);//是求數組的元素個數int i=0;//遍歷數組for(i=0;i<n;i++){printf("%d ",arr[i]);}printf("\n");案例:
int arr1[5]={1,2,3};int arr2[]={1,2,3};char arr3[5]={1,2,3};int arr4[]={[5]=7};//VC6.0不支持printf("%d\n",sizeof(arr1));//20printf("%d\n",sizeof(arr2));//12printf("%d\n",sizeof(arr3));//5printf("%d\n",sizeof(arr4));//6*4=243、數組元素的操作
數組元素等價于普通變量
int arr[5]={1,2,3,4,5};int n=sizeof(arr)/sizeof(arr[0]);int i=0;//給數組元素賦值arr[0]=100;//num++arr[1]++; //arr[1]=arr[1]+1;//scanf("%d",&num)scanf("%d",&arr[2]);for(i=0;i<n;i++){printf("%d ",arr[i]);}printf("\n");案例:定義一個數組 5個元素,每個元素int類型,獲取鍵盤輸入
//數值數組只能逐個元素操作 不能整體操作int arr[5]={0};int n=sizeof(arr)/sizeof(arr[0]);int i=0;printf("請輸入%d個int型數據\n",n);for(i=0;i<=n;i++){scanf("%d",&arr[i]);//中括號[]的優先級高于&//&arr[i] 表示取第i個元素的地址}for(i=0;i<n;i++){printf("%d ",arr[i]);//arr[i]訪問數組的第i個元素 }作業:定義一個數組 5個元素,每個元素int類型,獲取鍵盤輸入,請求出數組的最大值,最小值,平均值。
知識點6【二維數組】
1、二維數組的定義
二維數組也叫數組的數組。
二維數組的遍歷:
//先寫內層循環,再把它粘貼到外層循環中去 int i=0; for(i=0;i<3;i++) {//遍歷第i行int j=0;for(j=0;j<4;j++){printf("%d ",arr[i][j])} }案例:
#include<stdio.h> void test01() {int arr[3][4];//局部數組不初始化,內容不確定int i=0,j=0;for(i=0;i<3;i++)//行的變化{//先寫內層循環,再寫外層循環,將內層循環復制到外層循環for(j=0;j<4;j++)//列的變化{printf("%d ",arr[i][j]);}printf("\n");} } int main(int argc,char *argv[]) {test01();return 0; }2、二維數組的初始化
分段初始化:用大括號里面的大括號,明確的表示一行
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};連續初始化:放滿一行才能放下一行
int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};案例:
int arr1[3][4]={{1,2},{3},{4,5}};int arr2[3][4]={1,2,3,4,5};04_數組與函數
知識點1 【作業講解】
作業:定義int arr[3][4]的二維數組,獲取鍵盤輸入,并求出每一行的平均值。
//作業:定義int arr[3][4]的二維數組,獲取鍵盤輸入,并求出每一行的平均值。 void test03() {int arr[3][4]={0};//將整個數組初始化為0//獲取鍵盤輸入int i=0,j=0;for(i=0;i<3;i++){for(j=0;j<4;j++){scanf("%d",&arr[i][j]);}}for(i=0;i<3;i++){//求每一行的平均值float sum=0; for(j=0;j<4;j++){sum+= arr[i][j];}printf("第%d行的平均值為%.2f\n",i,sum/4.0);}運行結果:
案例:求每一列的平均值
void test03() {int arr[3][4]={0};//將整個數組初始化為0//獲取鍵盤輸入int i=0,j=0;for(i=0;i<3;i++){for(j=0;j<4;j++){scanf("%d",&arr[i][j]);}}for(i=0;i<3;i++){//求每一行的平均值float sum=0; for(j=0;j<4;j++){sum+= arr[i][j];}printf("第%d行的平均值為%.2f\n",i,sum/4.0);}for(j=0;j<4;j++){int sum=0;for(i=0;i<3;i++){sum+=arr[i][j];}printf("第%d列的平均值為%d\n",j,sum/3);}// 計算第0列}int main(int argc,char *argv[]) {test03();return 0; }
知識點2 【字符數組】(重要)
字符數組:本質是數組,只是數組的美國元素是字符。
//定義一個數組有五個元素,每個元素為char
arr[5]
char ch;
從上往下替換======>
char arr[5];
字符數組的初始化
//初始化:逐個字符初始化(不推薦) char str[6]={'h','e','l','l','o'}; //str[0]== 'h'的ASCII碼值//初始化:以字符串的形式初始化(推薦) char str[6]="hello";//初始化更加簡潔字符數組的遍歷
char str[6]="hello";//逐個字符遍歷(不推薦)int i=0;for(i=0;i<6;i++){printf("%c",str[i]);}printf("\n---------\n");//字符數組可以整體遍歷(推薦)printf("%s\n",str);//%s: 從給定字符串的首元素地址開始逐個字符遍歷,直到遇到\0結束//%s加數組名str運行結果:
%s:只要告訴字符串第0個元素的地址,那么就可以逐個元素打印,直到遇到\0結束
非常重要:一維數組名代表的是 數組第0個元素的地址。(必須記住)
char str[6]="hello";//%p是十六進制輸出地址printf("第0個元素的地址:%p\n",&str[0]);printf("數組名:%p\n",str);逐個字符初始化和字符串初始化的區別
char str1[]={'h','e','h','e'};//逐個元素初始化,系統不會給它添加‘\0’char str2[]="hehe";//以字符串初始化,系統會給字符串添加'\0'//以上情況的前提條件是中括號里面沒給數組長度//開辟的空間大小printf("sizeof(str1)=%d\n",sizeof(str1));//4printf("sizeof(str2)=%d\n",sizeof(str2));//5
數組中括號里面沒有給定元素的個數那么元素的實際個數由初始化情況決定
%s輸出的內容
%s的作用是:從數組的第0個元素開始,逐個元素輸出,直到遇到\0結束
★★★運行結果:
字符數組獲取鍵盤的字符串
定義一個字符數組,有足夠大的空間
char buf[128]="";printf("請輸入一個字符串\n");scanf("%s",buf);//不能獲取帶空格的字符printf("buf=%s\n",buf);運行結果
scanf和%s使用的時候有個缺點,遇到空格會結束輸入
解決方法:用gets()——》把鍵盤輸入的大小全搬給你
案例:
gets()缺點:獲取鍵盤輸入的時候,不會管buf的大小,容易造成內存污染
fgets():既可以獲取帶空格的字符串,也可以保證buf的不越界
#include<stdio.h> char *fgets(char *s, int size, FILE *stream) //s表示存放字符串的空間地址 //size表示能提取字符串的最大長度 size-1(留一個給'\0') //stream stdin表示標準輸入設備 返回值:就是獲取到的字符串的首元素地址案例:
void test07() {char buf[10]="";printf("請輸入一個字符串\n");fgets(buf,sizeof(buf),stdin);printf("buf=%s\n",buf); }int main(int argc,char *argv[]) {test07();return 0; }
提高案例:字符串的大小寫的轉換
//'a' 97 'b' 98 'c'99 ~ 'z'122//'A' 65 'B' 66 'C'67 ~ 'Z'90//規律:小寫字母的ASCII值比大寫字母的ASCII值相差32//printf("%d %d\n",'a','z');//printf("%d %d\n",'A','Z');char ch='a';//小寫變大寫 -32ch=ch-32; //其實也可以寫:ch=ch-('a'-'A');printf("ch=%c\n",ch);//大寫變小寫 +32ch=ch+32;//其實也可以寫:ch=ch+('a'-'A');printf("ch=%c\n",ch);作業:鍵盤獲取一個字符串,大小自定義,將字符串的大寫字母變小寫,小寫字母變大寫,其他符號保持不變
思路如下:
1、獲取一個字符串
2、用for循環將字符串的逐個字符進行大小寫轉換
知識點3【二維字符數組】(了解)
一維字符數組是存放多個字符的
二維字符數組是存放多個字符串的 ,每個字符串占一行
//不管是數值還是字符的二維數組,在初始化的時候是庫省略行標的,行數又具體初始化決定 char str[3][16]={"hehe","haha","heihei"};//要保證每個字符串小于16個字節 str[0]="hehe"; str[1]="haha"; str[2]="heihei"; char str[3][16]={"hehe","haha","heihei"};//對整體字符串輸出,只需要使用行標printf("%s\n",str[0]);//heheprintf("%s\n",str[1]);//hahaprintf("%s\n",str[2]);//heihei//輸出的是 字符串中某個字符 必須用行標和列表printf("%c\n",str[1][3]);//a二維字符數組獲取鍵盤輸入的字符串:
應用場景:當鍵盤需要輸入多個獨立的字符串,用戶必須單獨存儲好,請選擇二維字符數組
輸入的字符串個數,決定了二維數組的行數 , 輸入字符串中最大長度,決定了二維字符數組的列數。
//hehe haha xixi heihei char buf[4][16]={""};//二維字符數組初始化為0int i=0;//獲取鍵盤的字符串for(i=0;i<4;i++){scanf("%s",buf[i]);//buf[i]已經代表是第i行的首元素地址,所以這時候不要取地址}//輸出鍵盤的字符串for(i=0;i<4;i++){printf("buf[%d]=%s\n",i,buf[i]);}
作業:
1、鍵盤輸入十個int數據,對其從小到大排序
2、鍵盤輸入一個字符串“abcdef”進行前后的顛倒(逆置)——》“fedcba”
知識點4 【函數概述】
1、函數的定義:實現函數的功能,確定函數體、返回值、形參類型。讓函數存在
2、函數的聲明:不是實現函數功能,僅僅是說明函數該函數有返回值類型,形參類型,函數名。
3、函數的調用:函數的執行
1、函數的定義
// 返回值類型:函數將來返回值的類型 //函數名:函數的入口地址 //形參類型:函數外部數據 傳遞到函數內部的橋梁 //函數體:具體的函數功能代碼 返回值類型 函數名(形參類型 形參) {函數體; }2、函數的聲明
返回值類型 函數名(形參類型 形參);3、函數的調用
//函數外部的實際數據 函數名(實參);//沒有函數返回值類型案例:
#include<stdio.h> //函數聲明(必須在函數調用的前方聲明才有作用)//編譯器是從上往下執行的 void my_fun();//函數聲明:告訴編譯器,該函數存在,請通過編譯。int main(int argc,char *argv[]) {//函數的調用:函數名+()my_fun();return 0; }//函數的定義 void my_fun() {printf("my fun\n");return;//void沒有返回值,return后面什么都不寫//return功能:1、返回函數結果 2、結束函數 }可以省略函數聲明:函數的調用在函數定義的下方,就可以省略函數聲明
#include<stdio.h> //函數聲明(必須在函數調用的前方聲明才有作用)//編譯器是從上往下執行的 //void my_fun();//函數聲明:告訴編譯器,該函數存在,請通過編譯。//函數的定義 void my_fun() {printf("my fun\n");return;//void沒有返回值,return后面什么都不寫 }int main(int argc,char *argv[]) {//函數的調用:函數名+()my_fun();return 0; }建議:
往往建議將主函數寫在上方,將函數定義寫在下方。(通過主函數,一眼就能看出大致框架)
知識點5 【函數的參數】
1、如果函數的形參什么都不寫 在調用的時候可以傳實參,只是實參得不到使用
#include<stdio.h>void my_fun(10,20);int main(int argc,char *argv[]) {my_fun();return 0; }//如果函數的形參什么都不寫 在調用的時候可以傳實參,只是實參得不到使用 void my_fun() {printf("my fun\n");return; }2、如果函數沒有參數,請將形參寫成void
#include<stdio.h>void my_fun(void);int main(int argc,char *argv[]) {//my_fun(10,20);//errormy_fun();return 0; }//如果函數的參數為void,在調用的時候就不要給函數傳遞實參 void my_fun(void) {printf("my fun\n");return; }3、函數參數的傳遞
總結
以上是生活随笔為你收集整理的逆向基础之C语言 第一篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: echarts 大屏可视化_看似复杂炫酷
- 下一篇: BUUCTF-Reverse:Simpl