C语言基础

数据类型

基本数据类型

  • java基本数据类型
  * byte 1个字节
  * short 2个字节
  * int 4个字节
  * long 8字节
  • 浮点型
  * float 4个字节
  * double 8字节
  • 字符型
  * char 2字节
  • boolean true/false

C的基本数据类型

  • 整型
  * short int
  * int
  * long int
  * long long int
  • 浮点型
  * float
  * double
  • 字符型
  * char

格式化输出

  • short类型:
short a = 123; 
printf("%hd\n", a);
  • int类型
int b = 123;
printf("b = %d\n", b);
  • long 类型
long int c = 345;
printf("c = %ld", c);
  • long long 类型
long long d = 123456;
printf("d=%lld\n", d);
  • float 类型
float e = 1.234;
printf("e=%f\n", e);//默认6位有效数字
printf("e = %.2f\n", e);//保留两位有效数字
  • double 类型
double f = 2.34567;
printf("f = %lf\n", f);//默认6位有效数字
printf("f = %.4lf\n", f);//保留四位有效数字
  • 字符类型
char g = 'a';
printf("g = %c\n", g);

各种数据类型在内存中的大小

  • 判断变量在内存中的大小
int a = 12;
int size = sizeof(a);
printf("sizeof(int) = %d\n", size);
  • 基本数据类型在内存中的大小
sizeof(int) = 4;
sizeof(short) = 2;
sizeof(long) = 4;
sizeof(long long) = 8;
sizeof(float) = 4;
sizeof(double) = 8;
sizeof(char) = 1;
  • 0为假, 非零为真。
if(0){//假
}else{
}

指针 直接操作内存区域

  • 指针赋值和修改值
int a = 123;
int *p;
p = &a;//将a的地址取出来, 然后赋值给p
printf("a=%d\n",*p);
*p = 234;//将234赋值给指针p
printf("a=%d\n", a);
  • 交换两个数的值
int a = 0;
int b = -1;
int *p1 = &a;
int *p2 = &b;
*p1 = *p1^*p2;
*p2 = *p1^*p2;
*p1 = *p1^*p2;
printf("a =%d, b = %d\n", a, b);
  • 多级指针
void test(){
    int a = 1;
    int *p1 = &a;//一级指针 
    int **p2 = &p1;//二级指针 
    int ***p3 = &p2;//三级指针 
    int ****p4 = &p3;//四级指针
    printf("*p1 = %d\n", *p1); 
    printf("**p2 = %d\n", **p2);
    printf("***p3 = %d\n", ***p3);
    printf("****p4 = %d\n", ****p4);
}
  • 函数指针
int add(int a, int b){
    return a+b;
} 
int minus(int a, int b){
        return a-b;
}
//指针当做函数参数
int compute(int a, int b, int(*pf)(int, int)){
    return pf(a, b);
}
void test2(){
    //定义函数指针变量p,p指向一个函数, 
    //函数的返回值必须是int,  函数的形参 
    int (*p) (int, int);
    p = &add;
    p = −
    int result = p(2, 3);
    //使用方式一
    int result = compute(2, 3, p);
    //使用方式二
    result = compute(2, 3, &add);
    printf("result = %d\n", result);
     
}
  • 野指针 没有任何指向的指针
int *p;
*p = 123;
printf("*p = %d\n", *p);

数组

  • 数组定义
void test3(){
    //数组定义 
    int arr1[10];
    int arr2[] = {1, 2, 3, 4, 5};
    int arr3[10] = {1, 2, 3};
    int len1 = sizeof(arr1)/sizeof(int);
    int len2 = sizeof(arr2)/sizeof(int);
    int len3 = sizeof(arr3)/sizeof(int); 
    printf("%d\n", len1); 
    printf("%d\n", len2); 
    printf("%d\n", len3); 
    
    for(int i=0; i<len2; i++){
        printf("arr2[i] = %d\n", arr2[i]);
    }
}
  • 数组的遍历
void print_arr(int arr[], int len){
    for(int i=0; i<len; i++){
        printf("arr[%d] = %d\n", i, arr[i]);
    }   
} 

void print_arr2(int *p, int len){
    for(int i=0; i<len; i++){
        //通过索引取值 
        //printf("arr[%d] = %d\n", i, p[i]);
        //通过地址值取值 
        printf("arr[%d] = %d\n", i, *(p+i));
    }
}

void test4(){
    int arr[] = {1, 6, 3, 4, 67};
    int len = sizeof(arr) / sizeof(int);
    print_arr(arr, len);
    print_arr2(arr, len);
}
  • 数组的排序
void test5(){
    int arr[] = {1, 6, 3, 4, 67};
    int len = sizeof(arr)/sizeof(int);
    for(int i=0; i<len-1; i++){
        for(int j=0; j<len-1-i; j++){
            if(arr[j] > arr[j+1]){
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    for(int k=0; k<len; k++){
        printf("arr[%d] = %d\n", k, arr[k]);
    }
} 
  • 内存模型 linux环境下
    1. Text段
    2. data段
      stack
      heap
      static data//已经初始化的常量或全局变量
    3. bss段 未初始化的常量和全局变量
  • 内存申请和释放 动态数组
#include <stdio.h>
#include <stdlib.h>
void test6(){
    int len = 3;
//  void* p = malloc(len*sizeof(int));
    //申请内存 
    int *p1 =(int*) malloc(len*sizeof(int));
    p1[0] = 0;
    p1[1] = 1;
    p1[2] = 2;
    int i = 0;
    for(; i<len; i++){
        printf("p1[%d] = %d\n", i, p1[i]);
    }
    //释放空间 
    free(p1);
}
int main(){
    test6();
}

结构体

  • 结构体定义方式
#include <stdio.h>
#include <stdlib.h>
//第一种定义方式 结构体 
struct Student{
    char *name;
    int age;
    double score;
}; 
//第二种定义方式
struct Person{
    char *name;
    int age;
} person; 

//第三种定义方式 匿名结构体 
struct {
    char *name;
    int age;
}; 

int main(){
    
    //结构体赋值
    struct Student stu = {"张三", 23, 99.5};
    printf("stu.name = %s, stu.age=%d, stu.double=%.2lf\n", stu.name, stu.age, stu.score); 
     
    person.name = "wwf";
    person.age = 12;
    
    printf("name = %s, age = %d\n", person.name, person.age); 
}
  • 结构体指针
struct Student{
    char *name;
    int age;
    double score;
}; 
int main(){
    //结构体赋值
    struct Student stu = {"张三", 23, 99.5};
    struct Student *p = &stu;
    (*p).name = "龙飞";
    (*p).age = 99; 
    printf("stu.name = %s, stu.age=%d, stu.score=%.2lf\n", stu.name, stu.age, stu.score); 
    //间接引用运算符
    printf("stu.name = %s, stu.age = %d, stu.score = %.2lf\n", p->name, p->age, p->score); 
    //二级指针 
    struct Student **p2;
    p2 = &p; 
    printf("stu.name = %s, stu.age = %d, stu.score = %.2lf\n", (**p2).name, (**p2).age, (**p2).score);
    printf("stu.name = %s, stu.age = %d, stu.score = %.2lf\n", (*p2)->name, (*p2)->age, (*p2)->score);
}
  • 结构体大小
内存开辟为2的整数倍, 各个数据类型的大小之和
struct Student{
    char *name;//4byte 
    int age;//4byte
    double score;//8byte
}; 
大小为16byte
  • 结构体中添加方法
int add(int a, int b){
    return a+b;
}
// 结构体 
struct Student{
    char *name;//4byte 
    int age;//4byte
    double score;//8byte
    int (*Add)(int, int);
}; 
int main(){
    
    //结构体赋值
    struct Student stu = {"张三", 23, 99.5};
    stu.Add = &add;
    int result = stu.Add(1, 2);
    printf("result = %d\n", result);
}

联合体

#include <stdio.h> 
#include <stdlib.h>
union Type{
    char *a;
    int b;
    double c;
};
int main(){
    union Type t;
    t.a = "abc";
    t.c = 124.1234;
    
    return 0;
}
  • 联合体和结构体区别
  结构体大小, 是所有数据大小之和, 
  联合体共享内存. 采用最大的那个数据类型大小, 联合体赋值一次, 赋值多次没有意 
  义, 因为后面的会覆盖前面的数据.

枚举


enum DAY{
    Mon, Tues, Web, Thur, Fri, Satu, Sun
};

int main(){
    enum DAY today = Sun;
    switch(today){
        case Mon:
            break;
        case Tues:
            break;
        case Web:
            break;
    }
    return 0;
}

typedef(给类型起别名)

typedef int size;
//给别名起别名 
typedef size length;
typedef length width;
typedef length height;

struct Student{
    char *name;
    int age;
}; 

typedef struct Student *Stu;

int main(){
    int a = 123;
    size b = 123;
    width c = 1;
    height d = 2;
    
    Stu s = {"玲玲", 21};
    return 0;
}

宏定义(#define)

#define PI 3.1415926;
//方法模板
define AREA(r) PI * r * r;
int main (){
  double result = AREA(2);
  printf("result = %lf\n", result);
}

c语言语法

一. 字符串格式化输出

  1. %d 整数 int类型
  2. %s 字符串类型
  3. %f float类型
  4. %.3lf double类型, 保留3位小数
  5. %p 指针类型
  6. %% 打印一个百分号
  7. %c 字符类型
  8. \0 不可见字符, 字符串结尾, 存储结束
  9. %hd short int 类型
  10. %u unsigned int
  11. %lu unsigned long

二. 随机数

  1. arc4random_uniform(n)产生一个0到n-1的随机数
  2. 10-20的随机数, 最大数-最小数+1 + 最小数 如下:
    arc4random_uniform(20-10+1) + 10

三. goto语句

  1. 使用方法: 标签名: 语句 在需要跳转的地方, goto 标签名, 容易造成死循环, 如下
  2. 可以向前跳, 也可以向后跳.
  3. 只能在当前函数中使用
  4. 标签名下面的第一行代码, 不能定义变量
int main(int argc, const char * argv[])
{
loop:
    // insert code here...
    printf("Hello, World!33333\n");
    float f = 1.234554654;
    printf("f = %.2lf\n", f);
    int r = random2(10);
    if(r<6)
    {
        goto loop;
    }
    printf("r = %d\n", r);
    
    return 0;
}

四. xcode调试

  1. 调试时, 高亮选中的代码是一个函数, 按下step over, 跳过函数, 执行下一步
  2. 如果, 想进入函数, 按下step in

五. 原, 反, 补码

  1. 正数, 原反补码, 一样
  2. 负数, 原反补码, 不一样
  3. short int num = 12; 在内存中两个字节
  4. int num = 1234; 四个字节
  5. long int num = 213435554; 8个字节 64位系统, 4个字节 32位系统

六. 垃圾值的由来, 我们声明一个局部变量, 在内存回收的时候, 只是标记不在使用,

而不会清理内存中的数据, 再次分配给其他变量时, 数据会分配给新的变量.

七. 数组初始化方式

  1. 先声明, 再赋值

    int arr[3];
    arr[0] = 1;
    arr[1] = 2;
    arr[2] = 3;
    
  2. 声明数组时, 初始化数组元素, 也可以省略数组长度

    int arr[3] = {1, 2, 3};
    int arr[] = {1,, 2, 3};
    
  3. 赋值时, 指定数组元素个数, 后面大括号中的元素可以不等于数组长度, 会自动截取, 或者补0

```
int arr[3] = {1, 2, 3, 4, 5};
int arr[3] = {1};
```
  1. 指定下标的初始化

    int arr[3] = {[1]=2, [2]=3, [3]=4}; 
    
  2. 二维数组, 行数可以省略, 列数不能省略.

    int arr[][4] = {1, 5, 3, 4, 6, 3, 8, 0, 8, 9, 12, 45, 23};
    //行数
    int rows = sizeof(arr) / sizeof(arr[0]);
    //列数
    int cols = sizeof(arr[0]) / sizeof(arr[0][0]);
    

八. 字符串

  1. 字符串长度的计算 从左向右遍历, 遇到\0结束.

  2. 常用函数

    // 1, puts()函数, 输出字符串, 自动换行, 只能输出字符串, 也不能使用占位符
    char name[] = "jack";
    puts(name);
    
    // 2, gets()函数 作用: 从控制台接收一个字符串数据, 与scanf的区别, 
    // 可以接收空格, 和scanf()函数一样, 不安全
    char name[10];
    printf("请输入你的姓名: ");
    gets(name);
    puts(name);
    
    // 3, #include <string.h>  
    // strlen(str) 字符串长度
    char name[20] = "237748895985";
    long len = strlen(name);
    printf("len = %lu\n", len);
    
    // 4, int strcmp(str1, str2) compare 比较字符串
    //   等于0 相等
    //   大于0 str1大
    //   小于0 str2大
    char name1[12] = "345454";
    char name2[12] = "2345465768";
    int num = strcmp(name1, name2);
    if(num==0){
        
    }
    
    // 5, strcpy(str1, str2)函数 copy, 将str1 拷贝到str2中\0也拷贝
    
    // 6, strcat(str1, str2)函数. concat: 链接
    将str2连接到str1上
    
    // 7, fputs(str, stdout); file 将字符数据输出到指定的流中
        标准输入流和标准输出流 stdin, stdout
        
    // 8, fopen(path, mode)函数, 可以创建一个指向文件的指针.
        FILE * pFile = fopen("/Users/wwf/a.txt", "a");
        char *name = "黑马程序员";
        fputs(name, pFile);
        fclose(pFile);
        
    // 9, fclose(file pointer)关流
    
    // 10, fgets()函数, 从控制台接收用户输入字符串
        scanf()缺点, 不安全, 输入的空格会被认为结束
        gets()缺点, 不安全
        fgets(<#char *restrict#>, <#int#>, <#FILE *#>)
        参数一: 将字符串存到那个数组中
        参数二: 接收字符串的长度
        参数三: 指定流
    // 11, 从文件流中读取内容
        FILE * pFile = fopen("/Users/wwf/a.txt", "r");
        char name[50];
        fgets(name, 50, pFile);
        fclose(pFile);
        printf("name = %s\n", name);
    

九. 指针: 变量的地址 指针在内存中占据8个字节

  1. 指针变量 存储指针的变量, 只能用来存储地址

    int *p1;
    int num = 10;
    p1 = &num;
    // p1的值
    printf("p1 = %p\n", p1);
    // p1的地址
    printf("p1 = %p\n", &p1);
    
  2. *p1 代表指针指向的变量

  3. 批量声明指针

    int *p1, *p2, *p3;
    // NULL 不指向任何地址, 完全等价于0
    int *p1 = NULL;
    
  4. 野指针 声明一个指针变量, 未初始化, 是一个垃圾值, 随机的.
    未初始化, 直接使用.

  5. 多级指针

    1. 二级指针: 指针变量存储的是, 一级指针的地址,
    2. 三级指针: 指针变量存储的是, 二级指针的地址.
    3. 一级指针, 只能存储普通变量的地址
    4. 二级指针, 只能存储一级指针变量的地址
    5. 三级指针, 只能存储二级指针变量的地址
    6. n级指针, 只能存储n-1级指针变量的地址
    int num = 12;
    int *p1 = &num;
    int **p2 = &p1;
    int ***p3 = &p2;
    
  6. 指针的运算

    1. 指针可以和整数进行加减运算
    2. 指针+1 并不是+1, 而是增加一个单位变量所占用的字节数
    3. 指针之间只能做减法运算, 其他运算没有意义
  7. 指针遍历数组的元素

    int arr[] = {3, 6, 3, 9, 23};
    int *p = arr;
    int len = sizeof(arr)/sizeof(arr[0]);
    for(int i = 0; i < len; i++)
    {
        printf("arr[%d] = %d\n", i, *(p++));
    }   
    
  8. 指针与指针之间的减法运算, 结果是一个long类型的数据

    结果的意义代表两个指针变量之间相差多少个单位变量

  9. 指针与指针之间,比较运算

    内存分配时, 先分配高字节, 再分配低字节.

十. 内存中的五大区域

  1. BSS段
    未初始化的全局变量和静态变量, 程序在刚运行的时候,未初始化.

  2. 数据段/常量区
    全局变量初始化为0后, 存到常量区.
    已经初始化的全局变量, 静态变量和常量等.

  3. 代码段
    用来存储程序的代码/指令

  4. 字符数组和字符指针的区别

    // 都是以字符串数组存储
    //可以修改值
    char arr[] = "jkklg"; 
    //不可以修改值
    char *arr1 = "dff";
    // 字符串恒定性, 以字符指针形式存储的字符串.
    
  5. 从文件流中读取字符串

    FILE * pFile = fopen("/Users/wwf/a.txt", "r");
    char name[50];
    fgets(name, 50, pFile);
    fclose(pFile);
    printf("name = %s\n", name);
    

十一. const关键字 常量

  1. const 修饰数组

    int arr[4] = {1, 4, 6, 8}; //数组元素的值不能修改
    
  2. const 修饰指针

    // 无法通过p指针去修改指针指向的变量的值. 直接操作变量可以
    int num = 14;
    const int *p = &num;
    // 可以把另外一个的变量的地址, 赋值给p
    int num2 = 15;
    p = &num2;
    // p的值不能改, 但是可以通过p修改指向的变量的值.
    int * const p = &num;
    *p = 16;
    // 都不能该
    const int * const p = &num;
    

十二. 如何申请堆空间内存

  1. 三个函数: malloc(size_t) unsigned long类型, 申请, calloc(size, sizeof(int))申请, realloc()释放; 申请多少个字节空间, 在stdlib.h头文件中

  2. 堆中的空间是, 从低字节开始分配的,不一定连续, 指的是多次molloc()分配内存, 每一次分配的字节数是连续的; 栈中的空间, 从高字节开始分配, 连续的.

  3. 判断是否申请成功,

    int *p = malloc(4);
    if(p)//申请成功 等价于p!=NULL
    {
    
    }
    
  4. calloc函数:

    • 作用: 向堆空间申请指定字节数的空间
    • 格式: 参数1: 多少个单位; 参数2: 每一个单位的字节数
    • calloc(3, sizeof(int))
    • 与malloc相比的优势, 申请内存后, 系统会将字节中的数据清零.
  5. realloc函数

    • 扩容

    • 注意: 指针 几乎可以操作内存上的每一个字节. 但是我们还是建议, 不要乱来,

    • 只操作我们申请的字节空间, 因为, 有可能出现一些问题.

    • 如果长度不够, 重新开辟一块新的内存将数据复制到其中, 将以前的内存释放.

    • 长度够, 还使用以前的内存

          int * arr = calloc(10, sizeof(int));
          for(int i = 0; i < 10; i++)
          {
              arr[i] = (i+1)*10;
          }
          realloc(arr, 15);
      
      
  6. 指针作为函数的返回值, 不能使用局部变量的地址.

  7. 指向函数的指针声明

    • 指针函数的指针, 并不是任意的函数都可以指向
    • 而是有限定的, 要求指向的函数的返回值类型和参数描述必须与指针的描述一样.
    • 声明语法:
      1. 返回值类型 (*指针名) ([]参数列表)
        void ( * pFun1) ();
      2. int (*pFun2) (int num1, int num2);
    void test()
    {
        
    }
    int test2(int a, int b)
    {
    }
    
    int main(int argc, const char * argv[])
    {
        void (*pFunc)() = test;
        pFunc();
        (*pFunc)(); 
        
        int (*pTest2)(int a, int b) = test2;
        pTest2();
    }
    

十三. 结构体

  1. 如何使用结构体来创建新的数据类型呢?

    语法格式:
    struct 新类型名称
    {
    // 在这里面写上, 你创建的类型是由哪些变量联合而成的.
    数据类型1 小变量名称1;
    数据类型2 小变量名称2;
    数据类型3 小变量名称3;
    }

  2. 别名:

    typedef char* String;
    typedef struct 
    {
        char *name;
        int age;
        char *sex;
    } Student;
    Student student;
    

十四. 宏定义

  1. 它是个预处理指令, 在编译之前执行, 在函数外, #define MAX = 10;

  2. 作用: 可以为一段c代码定义一个标识, 如果你要使用这段c代码, 那么你就使用这个标识.

  3. 语法: #define 宏名 宏值

  4. 原理: 在编译的时候, 就会执行源文件中的预处理指令.会将c代码中, 使用宏名的地方替换为宏值.

  5. 将c代码中的宏名替换为宏值的过程叫做 宏替换/宏代换

  6. 宏值可以是任意的东西, 定义时, 不会检查语法, 宏替换时, 检查语法

  7. 如果宏值是一个表达式, 那么宏值并不是表达式的结果, 而是它本身.

    #define N = 10+10+20;
    int main(int argc, const char *argv[])
    {
        int a = 2*N;
        printf("a = %d\n", a);//50
        return 0;
    }
    
  8. 宏值不可更改

  9. 宏值可以是表达式,

    #define C = a+a;
    int main(int argc, const char *argv[])
    {
        int a = 10;
        int c = C;
        printf("c = %d\n", c);
        return 0;
    }
    
  10. 宏可以定义在函数外或者函数内
    宏从定义的地方一直到文件结束都可以用, 取消宏undef 宏名,
    取消后, 后面就无法使用宏了

  11. 使用宏的注意点:

    #define MAX(a, b) a+b;
    // #define 空格 宏名 空格 宏值 
    
  12. static 和 extern

    • 修饰成员变量, 在当前模块中使用, 使用static, 跨模块使用extern修饰
    • 函数默认使用extern修饰,
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容