美文网首页
简单的通过demo了解C++的基础语法笔记

简单的通过demo了解C++的基础语法笔记

作者: JasonChen8888 | 来源:发表于2020-05-16 15:30 被阅读0次

    前言

    许久未碰C++的相关内容,已经有些被大脑的程序执行Lru算法了,导致近期在做NDK开发的时候,各种操作卡顿,决心还是回忆整理一下相关的基础知识。

    开始扯犊子

    涉及的知识点

    • 基本数据类型
    • 输入函数
    • 数组的定义
    • 控制流程
    • 指针
    • 指针运算
    • 数组与指针
    • 指针变量名
    • 指针和函数参数
    • 指针数组和数组指针
    • 二级指针
    • 函数指针
    • 内存分配
    • 字符串

    基本数据类型

    1. C++ 常见的基本数据类型:
      int short long float double char bool
    2. sizeof 关键字不是函数,计算类型所占用的内存大小
        int i = 1;
        printf("%d \n", i);
        printf("i 的地址 %#x \n", &i);
        printf("i 的类型,所占用的内存空间大小 %d \n", sizeof(i));
    
        long l = 200000;
        printf("%ld, sizeof: %d \n", l, sizeof(l));
    
        char c = 'b';
        printf("%c, sizeof: %d \n", c, sizeof(c));
    
        float f1 = 1.2;
        printf("%f, sizeof: %d \ n", f1, sizeof(f1));
    
        double f2 = 23.99f;
        printf("f2 的内存:%d \n", sizeof(f2));
    

    输入函数

    scanf函数,在vs使用的时候会报错,解决方法:https://www.cnblogs.com/dmego/p/6065144.html

    int n;
    scanf("%d", &n);
    

    数组的定义

    C /C++ 语言的数组,在申明的时候就必须确定大小和基本类型。

    • 静态声明
      在定义的时候,需要给定明确的大小;
      静态申明,分配的控件是在程序的内存的stack (栈)中
    int a_array[10];
    

    错误的声明:

    //错误调用, 申明的时候就必须知道大小
    int n = 9;   //n 是运行时才知道是9
    scanf("%d", &n);  //这边是运行时才确定大小
    int a_array[n];  //申请数组必须在编译时确定大小,需要用来分配内存空间
    

    正确的声明:

    cont int n = 10;   //const 表示常量的关键字,会在内存静态区域分配空间给n存储,让它任何时候调用n,默认是10, 所以可以确定数值
    int a_array[n];
    

    定义数组,必须给数组分配空间的大小,且连续
    看下内存的分配
    int占四个字节,地址连续网上加4
    0x00EFFA90 +0 ....
    0x00EFFA94 +1 ....
    0x00EFFA98 +2 ....
    0x00EFFA9C +3 ....
    0x00EFFAA0 +4 ....
    0x00EFFAA4 +5 ....
    0x00EFFAA8 +6 ....
    0x00EFFAAC +7 ....
    0x00EFFAB0 +8 ....
    0x00EFFAB4 +9 ....

    • 动态声明(malloc 分配内存函数)
    //40M 4byte *1024 *1024 *10 = 40M,  int是4byte
    int * a_array;
    a_array = (int *)malloc(sizeof(int) * 1024 * 1024 * 10);
    int i;
    printf("地址: %#x ", &a_array);
    for (i = 0; i < 10; i++) {
        a_array[i] = i;
    }
    
    • 动态声明和静态声明的区别
      静态数组是存储在stack 栈 ,stack的大小是有限制的,?10M,2M,平台相关, stack会自动释放
      动态申请数组堆里面, 系统不会自动释放

    控制流程

    for
    if
    switch
    while
    break
    contiue
    同和java一样,就不多讲了。

    指针

    每一个变量都有一个内存位置,每一个内存位置都定义了可使用连字号(&)运算符访问的地址,它表示了在内存中的一个地址。而指针就是我们用来访问地址,已经地址所在存储的值的工具。

    • 什么是指针?
      指针: 指针变量存储的是 变量类型对应的变量的地址(内存位置的直接地址)。
      声明:
    type *var-name;
    

    在这里,type 是指针的基类型,它必须是一个有效的 C++ 数据类型,var-name 是指针变量的名称。用来声明指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:

    int    *ip;    /* 一个整型的指针 */
    double *dp;    /* 一个 double 型的指针 */
    float  *fp;    /* 一个浮点型的指针 */
    char   *ch;    /* 一个字符型的指针 */
    

    所有指针的值的实际数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,都是一样的,都是一个代表内存地址的长的十六进制数。不同数据类型的指针之间唯一的不同是,指针所指向的变量或常量的数据类型不同。

    int * ip  ip 存储的是 int型 的变量的地址
    
    • 相关单目运算符:
      & 取地址操作符。运用在内存中的对象上面,即变量与数组元素
      *表示间接寻址或者引用运算符。
      有关对运算符的优先级,可以查看下图:


      运算符优先级.png

    指针定义声明的时候,容易误解的地方:
    int a = 10;
    //int *p = &a; //这边定义和下面两行的定义同等意思
    int *p;
    p = &a;
    即:
    (int *p = &a)等同于 int *p; p = &a;

    • 撸代码:
    int i = 10;
    int *p;
    p = &i;
    printf("i value: %d\n", i);
    printf("p 的地址:%#x \n", &p);  //%#x : 表示16进制输出
    printf("i 的地址:%#x \n", &i);
    printf("修改i ");
    
    printf("i value: %d\n", i);
    printf("sizeof(*p) %d\n", sizeof(*p));
    
    double f = 23.99f;
    double * fp;
    fp = &f;
    printf("f 的内存:%d \n", sizeof(f));
    // fp 是一个变量,指向double 类型的数据的变量
    // fp 是存储的 地址 是int的,地址是占用4个字节的。32 位 4 * 8
        
    printf("sizeof(fp) %d\n", sizeof(fp));
    printf("f 的地址:%#x \n", &f);
    printf("fpi 的地址:%#x \n", &fp);
    

    指针运算

    *p 代表是它所指的地址上面的值的运算
    p++ 是p 指针变量++,结果就是:p指向它的下一个地址

    • 撸代码:
    int main() {
        int a = 10;
        //int *p = &a;
        int *p;
        p = &a;
        *p = *p + 10;
        printf("p: %d, a: %d\n", *p, a);
        int y = 1 + *p;
        printf("y: %d\n", y);
        *p += 1;
        printf("p: %d, a: %d\n", *p, a);
    
        (*p)++;  //值增加
        *p++;    //指向的地址位移4个字节,一个地址4个字节
        printf("p: %d, a: %d, p address: %#x\n", *p, a, p);
        //*(p++); == p++; *p;
        //1+2 * 3
        printf("p: %d, a: %d, p address: %#x\n", *p, a, p);
        system("pause");
        return 0;
    }
    

    输出结果:


    结果.png

    数组与指针

    通过数组下标所能完成的任何操作都可以通过指针来实现。
    而用指针编写的程序比用数组下标编写的程序执行速度快,但是,指针写的程序会比较难理解一点。
    直接撸代码:

    int main() {
        //数组名,就是数组的首地址
        int a[5] = {1,2,3,4,5};
        int *p;
        p = a;
    
        int i;
        printf("%#x\n", a);
        // a+5 ,表示a 移动到数组的第5个位置
        for (i = 0; p < a + 5; p++)
        {
            *p = i;
            i++;
        }
        p = p + 1;
        p++;
    
        
        printf("p: %d\n", *p);
    
        /*a[i]  == *(p+i)
        &a[i] ==  a+i*/
        //p + i == &a[i] == a+i  ,这三个是等价的
    
        system("pause");
        return 0;
    }
    

    解释两个点
    第一点:a+5, a[5],数组名,就是数组的首地址,a+5就是从a首地址开始,相当于移到第六项, 如下图:


    image.png

    第二个点:p + i == &a[i] == a+i, 因为p指针存放的是a的地址,a本身是数组的首地址,a[i]表示的是对应的第i项的地址,所以 p + i == &a[i] == a+i
    运行输出:


    结果.png

    指针和函数参数

    指针在函数中的使用,举例子如下:

    //形参的修改不能带来这个实产的修改, 将a b 的值copy 给了 形参d, e 该函数最终调用是没有变化的
    void swap(int d, int e) {
        printf("d address: %#x, e address: %#x \n", &d, &e);
        int temp;
        temp = d;
        d = e;
        e = temp;
    }
    // 形参的修改不能带来这个实产的修改
    // 将a b 的地址copy 给了 形参,然后,将这个份copy 的地址进行了切换,而地址所指的值是没有变的
    void swap2(int *a, int *b) {
        printf("swap2 a address: %#x, b address: %#x \n", a, b);
        int *temp;
        temp = a; // a address 给了temp
        a = b;    // a 的地址变成了b的地址
        b = temp;  //b 的地址变成了a 的地址
        printf("swap2 a address: %#x, b address: %#x \n", a, b);
    }
    // 该函数最后是在指针所指引的地址上面的值进行操作,并且地址上的值发生了改变
    void swap3(int *a, int *b) {
        int temp;
        temp = *a;
        *a = *b;  // *a  代表是a所指的地址上面的值
        *b = temp; // 将 b 所在地址的值变成了temp
    }
    
    int main() {
        int a, b;
        a = 10;
        b = 5;
        printf("a address: %#x, b address: %#x \n", &a, &b);
        swap(a, b);//e = a; d = b;   复制了一份值
        printf("a = %d, b = %d\n", a, b);   
        swap2(&a, &b);//e = a; d = b;   复制了一份地址
        printf("a = %d, b = %d\n", a, b);
    
        swap3(&a, &b);//e = a; d = b;
        printf("a = %d, b = %d\n", a, b);
    
        system("pause");
        return 0;
    }
    

    运行输出:


    结果.png

    指针数组和数组指针

    首先,要区分指针数组和数组指针
    指针数组是指:是一个数组,数组里存储的是指针类型的变量
    如: int * p1[4];
    数组指针: 是一个指针,这个指针指向的是一个数组的首地址
    如:int (*p2)[4]

    指针数组和数组指针的关系如下图:


    指针数组和数组指针.jpg
    • 指针数组的例子
    #include "stdafx.h"
    #include <string.h>
    #include <stdlib.h>
    
    void sort(char *name[], int n);
    int main() {
    
        char *name[] = { "hello", "dongNao", "Alvin", "world" };
        int i, n = 4;
        sort(name, n);
        for (i = 0; i < n; i++)
        {
            printf("%s\n", name[i]);
        }
    
        // 数组指针
        // p2 指向的是?指向的是一个数组,一个有5个元素的数组
        char(*p2)[5];
        //p2 是一个指针变量名
        system("pause");
        return 0;
    }
    
    
    // 参数,是char 类型的指针数组
    //name[i]是一个指针
    void sort(char *name[], int n) {
        char *temp;
        int i, j;
        for (i = 0; i < n; i++) {
            for (j = 0; j < n - 1 - i; j++) {
                if (strcmp(name[j], name[j + 1]) > 0)
                {
                    temp = name[j];
                    name[j] = name[j + 1];
                    name[j + 1] = temp;
                }
            }
        }
    }
    

    输出结果:


    结果.png

    二级指针

    二级指针,存的内容是一个一级指针的地址
    用个例子来解释:

    int main() {
        int a = 100;
        int *p;
        p = &a;
    
        int **p2;
        p2 = &p;
    }
    

    p 的值就是 a 这个变量的地址
    *p 运算,得到的就是a 的值
    *p2 运算,得到的就是p的值
    **p2 运算,得到的就是 p的值的 *运算得到的值 a;

    如下图: image.png

    函数指针

    在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。

    • 函数指针的定义
    int Func(int x);   /*声明一个函数*/
    int (*p) (int x);  /*定义一个函数指针*/
    p = Func;          /*将Func函数的首地址赋给指针变量p*/
    
    • 活生生的例子:
    int plus(int a, int b) {
        return a + b;
    }
    
    int minus(int a, int b) {
        return a - b;
    }
    
    int plus2(int *a, int *b) {
        return *a + *b;
    }
    
    int minus2(char * a, char *b) {
        return 0;
    }
    
    int main()
    {
        int result;
        int c = 3;
        int d = 5;
        int(*calc)(int a, int b);
        int(*calc2)(void *a, void *);
    
        int * p(int a, int b);  //声明一个返回值是int指针的函数
        //calc = plus;
        calc = minus;
        //result = calc(3, 5);
        //calc2 = (int(*)(void *, void *))plus2;
        calc2 = (int(*)(void *, void *))minus2;
        result = calc2(&c, &d);
        printf("minus: %#x\n", minus);
        printf("result: %d\n", result);
        system("pause");
            return 0;
    }
    

    输出结果:


    结果.png

    解释一下上面的程序

    1. void 类型的指针:类似:java object
    2. 指针变量都是4个字节。都是用十六进制表示。
      void* ->> int * / char * /float*
    3. 上面两个表达式完全不同
      int(*calc2)(void *a, void ); 函数指针
      int * p(int a, int b); 一个返回值是(int * )指针的函数 等同 (int
      ) p(int a, int b)

    内存分配

    内存:
    3区:程序区、静态存储区、动态存储区

    程序区
    程序的二进制文件(代码)

    静态存储区
    全局变量和静态变量 (在程序编译的时候内存已经分配好了, 比如数组地址是固定,const修饰)

    动态存储区
    堆区:用于程序动态分配 (malloc)
    栈区:编译器自动分配,编译器自动申请和释放 2M, 比如:静态定义数组

    void* malloc(size_t size)
    分配内存的单元是 字节, 大小 size,连续的内存
    如果申请失败,返回值是NULL

    void* calloc(size_t _Count,size_t _Size)
    申请 _Count 个 大小为_Size 的连续空间,这个连续空间的大小是_Size,如果申请失败,返回值是NULL
    而不是 _Count * size, 同时,它会初始化为0

    void *realloc(void *ptr, size_t size)
    尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
    ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
    size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。

    注意: 动态申请的内存一定要收到释放
    free(*p)
    原则:不能重复释放
    必须赋值NULL
    申请和释放一一对应

    • 看malloc的例子:
    // 40.3M
    //40M 手动分配,0.3M 哪来的? 程序的二进制文件
    int const num = 100;
     
    void mem() {
        int *a = (int *)malloc(sizeof(int) * 1024 * 1024);  申请4M内存
    }
    
    int main()
    {
        // 4M 的数组
        //栈溢出, 正常栈的大小是2M
            int aa[1024 * 1024 * 10];
    
        /*while (1) {
            mem();
            Sleep(2000);
        }*/
    
        int *a = (int *)malloc(sizeof(int) * 1024 * 1024);
        if (a == NULL)
        {
            printf("内存不够,请温柔\n");
        }
        a[0] = 5;
        printf("%d\n", sizeof(a));
        printf("%#x", a);
    
            a = (int *)malloc(sizeof(int) * 1024 * 1024* 2);
        free(a);
        a = NULL;
        printf("a address: %#x,", a);
        // 如果不进行a = NULL;那么,a 就是一个野指针
        if (a != NULL) {
            ///
        }
        
        system("pause");
        return 0;
    }
    

    输出结果:


    结果.png
    • 看calloc的例子:
    int main() {
        int * b = (int *)calloc(4, 100 * sizeof(int));
        if (b == NULL)
        {
            printf("内存不够,请温柔\n");
        }
        printf("%d\n", sizeof(b));
        printf("%#x", b);
        system("pause");
        return 0;
    }
    

    输出结果:


    结果.png

    字符串

    C 语言没有String
    用数组和指针表示

    数组表示string char ch1[10] = {'c','h','i','n','a','\0'}; \0: 表示空格符 打印出来的是china,可以看出字符串上是使用 null 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
    char ch1[10] = {'c','h','i','n','a','\0','a'}; 打印出来也是china, 因为编译器取的是'\0'之前的字符
    Char * str = “china”;
    等同于
    char * c1 = (char )malloc(sizeof(char) * 20); c1 = "china"
    说明:是否可以修改str[2] = b;
    不能修改str[2] = ‘b’
    把“abc”赋值给一个字符指针变量时,如: char
    ptr = “abc”;因为定义的是一个普通字符指针,
    并没有定义空间来存放“abc”,所以编译器得帮我们先找个地方存放“abc”,显然,把这里的“abc”
    当成常量并把它放到程序的常量区是编译器最合适的选择。所以的那个你去修改 char* ptr = “abc” 中的值,
    如:ptr[0] = “g”的时候,会报错,因为这个地址里面存的是常量,常量是不能修改的。
    Char *str = “Hello world”;
    因为 “hello are you” 出现在一个表达式中时,“hello are you”使用的值就是这些字符所存储的地址(在常量区)。所以这个地址可以赋值给一个 char 类型的指针变量注意:“hello are you”作为字符数组初始化的时候就不是一个常量,其他情况下是一个常量。

    char * str = "string";

    分配一个常量区A:"string"
    char * str 没有初始化
    赋值:将常量区A的首地址赋值给str

    char[] 和 char * 的区别
    数组不能直接赋值, 需要借助其他的方法,如: strcpy
    如果name是指针,可以直接赋值

    c 语言双引号的字符串,约定成熟的都是放在常量区

    • 例子:
    #include "stdafx.h"
    #include <string.h> 
    #include <stdio.h> 
    #include <stdlib.h>
    
    int main() {
        //char ch1[10] = { 'c', 'h','i','n','a','\0','a'};
        //char ch1[] = { 'c', 'h','i','n','a','\0' };
        /*char ch1[20] = "china";
        char a;
        ch1[0] = 's';*/
        /*char * ch = "china";*/
        char * ch = (char *)malloc(100 * sizeof(char));
        /*free(ch);
        ch = NULL;*/
        ch = "china";
        strcpy(ch, "china");
        ch[2] = 'b';
    
        char * str = "string";
        printf("%#x\n", str);
        printf("%s\n", str);
        system("pause");
        return 0;
    }
    
    //字符串赋值和拼接
    //int main()
    //{
    //  char destination[25];
    //  char *blank = " ", *c = "C++", *Borland = "Borland";
    //  strcpy(destination, Borland);
    //  strcat(destination, blank);  //strcat函数字符串拼接
    //  strcat(destination, c);
    //  printf("%s\n", destination);
    //  system("pause");
    //  return 0;
    //
    //}
    

    结语

    以上就是当前已记录的相关语法笔记,欢迎阅读和指正

    相关文章

      网友评论

          本文标题:简单的通过demo了解C++的基础语法笔记

          本文链接:https://www.haomeiwen.com/subject/ljslohtx.html