安卓逆向之路 C语言基础

入门

概念:
*源文件:源文件即源代码文件,C语言源文件后缀名是.c。
*头文件:头文件后缀名为.h ,C语言代码由源文件和头文件组成。
*关键字:关键字是C语言征用了的一些字,这些字在C语言中代表特殊含义,已经被C语言定义好了,轮不到我们用了。每个关键字都有自己特定的含义,我们必须搞懂每个关键字的所有含义和用法,否则就看不懂C语言程序。
*注释:C语言中注释以//开头,或者/* */,注释是给程序员看的,不是给机器看的。也
就是说,程序员写注释给其他人看,以让人明白自己为什么要这么写。但是编译器编译程序的时候是忽略注释内容的,所以机器根本看不见注释,也不理注释。
*符号:C语言程序中包含很多符号,如; : , + () {} *等等,各种符号都有自己的含义,必须
搞明白各种符号的含义,才能看懂C语言程序。
*变量:会变化的量。C语言程序中用变量来进行计算。
*函数:函数是C语言的一个基本组成单位,一个C语言程序其实就是由很多函数组成的,每个函数用来完成一定的功能,函数可以调用别的函数来完成功能。函数的标志是().C语言中有一个特殊的函数叫main,这个函数是整个程序的内定的入口,也就是说整个C语言程序是从main函数开始执行的,其他的函数都是直接或者间接被main调用。

C语言关键字:
include(包含) 头文件包含
int(integer,整数) 用来表示一个整数的类型叫整形。
float(浮点型) 用来表示小数的类型
char(character,字符) 字符型数据类型
return(返回) 函数返回

Java 字节数 C
int 4 int
short 2 short
byte 1 char
long 8 long long
double 8 double
float 4 float
char 2 short
boolean 1 0代表假 非0代表真

数据类型

整形
C语言中的整形对应数学中的整数,整形变量是用来描述一个整数值的,整形变量经过计算
后也只能是整数(整型),不可能出现小数(浮点型).
要求:要学会整形变量的定义,赋值,计算
要学会使用printf函数打印出一些整形变量的值,作为输出和调试。
C语言中整形有三种:
(1)int 整形
(2)short int,又叫做short,短整形(<=整形)
(3)long int,又叫做long,长整型(>=整形)

浮点型
C语言中浮点型对应数学中的小数。浮点型有float和double两种。使用方式相同,不同在于
表示范围和精度。float表示的范围小,精度低(小数点后6位);而double表示范围大,精度
高。(小数点后16位)
范围是说表示的数有多大,精度是指这个数的分辨率有多细
注意:printf中打印float或double类型,要是用%f,不能用%d。

 

字符型
字符型对应ASCII字符。ASCII字符是一种编码,就是用数字编码来表示一个符号的一种方法
本质上说,字符型其实也是整形,只是这些整形数被用来表示一些字符的ASCII编码值,所
以叫做字符型。字符型一般用8位二进制表示,无符号字符型范围是0~255.
字符型其实是一种比short还短的整形,所以它可以和int相运算。

 

有符号数和无符号数
数学中数是有符号的,有整数和负数之分。所以计算机中的数据类型也有符号,分为有符号
数和无符号数。

有符号数:
整形:signed int(简写为 int)
signed long,也写作signed long int,(简写为long)
signed short,也写作signed short int(简写为short)
signed(表示signed int)
浮点型:
signed float(简写为float)
signed double(简写为double)
字符型:
signed char(简写为char)

无符号数:
整形:整形有无符号数,用来表示一些编码编号之类的东西。譬如身份证号,房间号
unsigned int(没有简写)
unsigned long int(简写unsigned long)
unsigned short int(简写为unsigned short)

浮点数:没有无符号浮点数。也就是说,小数一般只用在数学概念中,都是有符号的。

字符型:字符型有无符号数
unsigned char(没有简写)

注意:对于整形和字符型来说,有符号数和无符号数表示的范围是不同的。
譬如字符型,有符号数范围是-128~127,无符号数的范围是0~255

void

内存与指针

内存

#include <stdio.h>
#include <stdlib.h>

main(){
    
    int a;         //申请一块内存 然后类型是int 
    a = 100;       //往这块内存地址 写入数据 100 
    
    printf("%#x\n", &a);
    
	system("pause");
}

指针

1、指针和指针变量
指针就是地址,地址就是指针
指针变量,其实质是C语言的一种变量。这种变量比较特殊,通常它的值会被
赋值为某个变量的地址值(p = &a),然后我们可以使用*p这样的方式去间接访问p所
指向的那个变量。

2、为什么需要指针?
指针存在的目的就是间接访问。有了指针之后,我们访问变量a不必只通过a这个变量名来
访问。而可以通过p = &a; *p = xxx;这样的方式来间接访问变量a。

3、两种重要运算符:&和*
&:取地址符,将它加在某个变量前面,则组合后的符号代表这个变量的地址值。
例如: int a = 100; int *p; p = &a; 则将变量a的地址值赋值给p。
就在上面的例子中,有以下一些符号:
a 代表变量a本身
p 代表指针变量p本身
&a 代表变量a的地址值
*p 代表指针变量p所指向的那个变量,也就是变量a
&p 代表指针变量p本身的地址值。符号合法,但无意义
*a 把a看作一个指针,*a表示这个指针所指向的变量。该符号不合法

*:指针符号。指针符号在指针定义和指针操作的时候,解析方法是不同的。
int p; // p是一个整形变量
int *p; // p是一个指针变量,该指针指向一个整形数
使用指针的时候,*p则代表指针变量p所指向的那个变量。

4、指针的定义和初始化
指针既然是一种变量,那么肯定也可以定义,也可以初始化
第一种:先定义再赋值
int *p; // 定义指针变量p
p = &a; // 给p赋值
第二种:定义的同时初始化
int *p = &a; // 效果等同于上面的两句

5、各种不同类型的指针
指针变量本质上是一个变量,指针变量的类型属于指针类型。int *p;定义了一个指针类型的
变量p,这个p所指向的那个变量是int型。
int *pInt; // pInt是指针变量,指向的变量是int类型
char *pChar; // pChar是指针类型,指向的变量是char类型
float *pFloat;
double *pDouble;
各种指针类型和它们所指向的变量类型必须匹配,否则结果不可预知。

6、指针定义的理解方法:
首先看到p,这个是变量名;其次,看到p前面的int *,把int *作为一个整体来理解,
int *是一种类型(复合类型),该类型表示一种指向int型数据的指针。

7、指针常见错误
指针变量未经赋值,不能使用 ---- 野指针
指针变量类型不匹配

#include <stdio.h>
#include <stdlib.h>

main(){
    int a = 100;
    int* p = &a;
    
    int** q = &p;
    
    int*** r = &q;
    
    printf("%d\n", ***r);
    
    system("pause");       
}
多级指针

数组

1、数组
到目前为止,我们已经学习了C语言的基本数据类型:整形、浮点型、字符型。再往后
就是复合数据类型。
所谓复合数据类型,是指由简单数据类型,经过一定的数据结构封装,组成而成的新的
数据类型。譬如数组、譬如结构体、譬如共用体、联合体

2、为什么需要数组?
数组就是数组成一个组,数就是一个特定数据类型的变量,组就是说好多数放在了一起。

3、怎么定义数组?
int a[4]; 数组中元素类型 数组名[数组元素个数];
数组中的所有元素必须是同一种数据类型,不可能在一个数组中存储两种数据类型的数。

4、怎么使用数组?
数组定义的时候作为整体定义。但是使用的时候不能作为整体使用,使用时必须拆开使用数组中的各个元素。
譬如数组int a[4],使用其中的四个元素,分别用a[0]``a[3],其中[]是数组的标志,[]中的
数字叫做数组下标(index,索引),下标是我们访问数组中各个元素的指引。下标是0代表数组中第一个元素,下标是1代表数组第二个元素。如果数组长度为n,下标中最后一个是n-1。访问数组时要特别注意下标,下标是从0开始的,如果下标超出了n-1,会产生越界访问,结果是不可预期的。

5、数组的初始化问题
初始化(initinalize,简写为init),是为了让对象有一个预定的初始状态。
譬如说:
(1)简单变量的初始化
当一个局部变量定义时没有初始化,它的值是随机的。这个如果没有注意,可能会导致程序出错。怎么办?解决方案有两个:
第一个,在定义过后明确给它赋值,使用=运算符。
第二个,定义该变量时,同时进行初始化。
总结:
1、一般来讲,只要你记得显示赋值,则两种方式并无优劣差异。但是人会犯错,会不小心,所以还是定义同时初始化好一点,因为这个定义的时候就有了固定值,即使之后忘记显示赋值也不会造成结果是随机的。
2、一般情况下,定义的同时都将变量初始化为0。局部变量定义同时初始化为0,这是一个写代码好习惯。

(2)数组的初始化
第一种:完全初始化。依次赋值
第二种:不完全初始化。初始化式中的值从a[0]开始,依次向后赋值,不足的默认用0填充赋值

#include <stdio.h>
#include <stdlib.h>

void printArr(int arr[], int len){
    int i = 0;
    for(i=0; i<len; i++){
        printf("arr[%d]=%d\n",i,arr[i]);     
    }
}

main(){
    short arr[] = {1,2,3,4,5,6,7,8,9,0};
    //char arr[] = {'h','e','l','l','o'};
    short* p = arr;
    //指针运算 
    printf("arr[0]=%d\n", *(p+0));  
    printf("arr[1]=%d\n", *(p+1)); 
    printf("arr[2]=%d\n", *(p+2)); 
    printf("arr[3]=%d\n", *(p+3)); 
    printf("arr[4]=%d\n", *(p+4)); 
    
    //printf("arr[1]=%#x\n", &arr[1]); 
    //printf("arr[2]=%#x\n", &arr[2]); 
    //printf("数组名=%#x\n", arr); 
    
    system("pause");       
}
案例

函数 

1、函数
截至目前为止,已经学习的数据类型,运算符,三种程序结构,已经可以完成一些C语言
程序了。但是不足之处在于写简单程序可以,写不了复杂程序。

当程序简单的时候,一个人可以用一个main函数搞定功能。当程序变成的时候,超出了人的大脑承受范围,这时候逻辑不清了。这时候就需要把一个大程序分成许多小的模块来组织,于是乎出现了概念叫做函数。
函数是C语言代码的基本组成部分,它是一个小的模块,整个程序由很多个功能独立的
模块(函数)组成。这就是程序设计的基本分化方法。

之前接触过的函数:
main:C语言中所谓的主函数,主函数就是一种特别的函数。特别之处在于,一个C语言程序只能有且必须有一个main函数。C语言规定,一个C语言程序从主函数开始执行,到主函数执行完结束。

printf:函数的作用是用来在标准输出中打印信息。这个函数不是程序员自己写的,是C语言标准库提供的一个库函数。在C语言中写代码时可以引用库函数,但是必须使用#include引用这个库函数所在的头文件。

2、使用函数来写程序时的关键部分:
函数定义:函数定义是关键,是这个函数的实现。函数定义中包含了函数体,函数体中的代码段 决定了这个函数的功能。

函数声明:函数声明实际上是叫函数原型声明。什么叫原型?函数的原型包含三部分:函数名,返回值类型,函数参数列表。通俗讲,函数原型就是这个函数叫什么,接收什么类型的几个参数,返回一个什么样的返回值。
函数声明的作用,在于告诉使用函数的人,这个函数使用时应该传递给他什么样的参数,它会返回什么样类型的返回值。这些东西都是写函数的人在函数定义中规定好的,如果使用函数的人不参照这个原型来使用,就会出错,结果就会和你想的不一样。

函数调用:函数调用就是使用函数名来调用函数完成功能。调用时必须参照原型给函数传参,然后从函数得到适当的返回值作为结果。

3、函数参数:
形参:形式参数的简称。在函数定义和函数声明中的参数列表中的参数,都是形参。
实参:实际参数的简称。函数调用中,实际传递的参数才是实参。

函数调用的过程,其实就是实参传递给形参的一个过程。这个传递实际是一次拷贝。实际参数的时候,实参(本质是一个变量)本身并没有进入到函数内,而是把自己的值复制了一份传给了函数中的形参,在函数中参与运算。这种传参方法,就叫做传值调用。

4、返回值:(关键字return)
当函数执行完之后,会给调用该函数的地方返回一个值。这个值的类型就是函数声明中返回值类型,这个值就是函数体中最后一句return xxx;返回的那个值。

5、函数名,变量名
第一点:起名字时候不能随意,要遵守规则。这个规则有两个层次:第一层就是合法,第二层是合理。合法就是符号C语言中变量名的命名规则。合理就是变量名起的好,人一看就知道什么意思,一看就知道这个函数是干嘛的,而且优美、好记。
第二点:C语言中,所有的符号都是区分大小写的。也就是说abc和Abc和aBc都是不同的符号。
第三点:C语言函数名变量名的命名习惯。没有固定的结论,有多种使用都很广泛的命名方式。介绍两种这里,
一种是linux的命名习惯 student_age str_to_int
另一种是骆驼命名法 studentAge StrToInt

字符串  

#include <stdio.h>
#include <stdlib.h>

main(){
    //定义一个字符串 
    char a1[6] = "hello\0";
    printf("1 %s\n", a1); 
    
    char a2[] = "hello";
    printf("2 %s\n", a2);
    
    char* a3 = "hello";
    printf("3 %s\n", a3); 
    
    printf("=====================================\n");
    //定义字符串数组 
    char* aa[5] = {
          "hello",
          "nihao",
          "good",
          "ok",
          "bye"
    };
    printf("4 %s\n", *(aa+0)); 
    printf("4 %s\n", *(aa+1)); 
    printf("4 %s\n", *(aa+2)); 
    printf("4 %s\n", *(aa+3));
    printf("4 %s\n", *(aa+4));
    
    system("pause");       
}  

结构体

#include <stdio.h>
#include <stdlib.h>

struct Student{
       char* name;
       int age;
       char sex;
       float score;
};//这个分号不能少 不然会报错 报的还是稀奇古怪的错 

main(){
    int a = 100;
    /*
    struct Student st;
    st.name = "xiaojianbang";
    st.age = 28;
    st.sex = 1;
    st.score = 99.5f;
    */
    struct Student st = {"xiaojianbang", 28, 1, 99.5f};
    struct Student* p = &st;
    //printf("name=%s\n", (*p).name);
    //printf("name=%f\n", (*p).score);
    
    printf("name=%s\n", p->name);
    printf("name=%f\n", p->score);
    
    system("pause");       
}

结构体指针

#include <stdio.h>
#include <stdlib.h>

struct Student{
       char* name;
       int age;
       char sex;
       float score;
};//这个分号不能少 不然会报错 报的还是稀奇古怪的错 

main(){
    int a = 100;
    /*
    struct Student st;
    st.name = "xiaojianbang";
    st.age = 28;
    st.sex = 1;
    st.score = 99.5f;
    */
    struct Student st = {"xiaojianbang", 28, 1, 99.5f};
    struct Student* p = &st;
    //printf("name=%s\n", (*p).name);
    //printf("name=%f\n", (*p).score);
    
    printf("name=%s\n", p->name);
    printf("name=%f\n", p->score);
    
    system("pause");       
}

函数指针  

#include <stdio.h>
#include <stdlib.h>

struct Student {
       char *name;
       int age;
       char sex;
       float score;
       void (*pf)(int a, int b);
};

void func(int a, int b){
     printf("和为%d\n", a + b);
}

main(){
    struct Student st;
    st.name = "xiaojianbang";
    st.score = 99.5;
    st.pf = func;//函数名其实就是函数的首地址 
    printf("%f\n", st.score);
    st.pf(5, 6);
    
    struct Student* pst = &st;
    (*pst).pf(6, 7);
    
    pst->pf(7, 8);
    
    func(8, 9);
    
    system("pause");       
}

  

  

 

posted @ 2020-08-28 16:05  沐风先生  阅读(446)  评论(0)    收藏  举报