前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >简单的通过demo了解C++的基础语法笔记

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

作者头像
包子388321
发布2020-06-16 09:48:59
3430
发布2020-06-16 09:48:59
举报
文章被收录于专栏:包子的书架包子的书架

前言

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

开始扯犊子

涉及的知识点

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

基本数据类型

  1. C++ 常见的基本数据类型: int short long float double char bool
  2. sizeof 关键字不是函数,计算类型所占用的内存大小
代码语言:javascript
复制
    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

代码语言:javascript
复制
int n;
scanf("%d", &n);

数组的定义

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

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

错误的声明:

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

正确的声明:

代码语言:javascript
复制
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 分配内存函数)
代码语言:javascript
复制
//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一样,就不多讲了。

指针

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

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

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

代码语言:javascript
复制
int    *ip;    /* 一个整型的指针 */
double *dp;    /* 一个 double 型的指针 */
float  *fp;    /* 一个浮点型的指针 */
char   *ch;    /* 一个字符型的指针 */

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

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

运算符优先级.png

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

  • 撸代码:
代码语言:javascript
复制
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指向它的下一个地址

  • 撸代码:
代码语言:javascript
复制
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

数组与指针

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

代码语言:javascript
复制
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

指针和函数参数

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

代码语言:javascript
复制
//形参的修改不能带来这个实产的修改, 将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

  • 指针数组的例子
代码语言:javascript
复制
#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

二级指针

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

代码语言:javascript
复制
int main() {
    int a = 100;
    int *p;
    p = &a;

    int **p2;
    p2 = &p;
}

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

如下图:

image.png

函数指针

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

  • 函数指针的定义
代码语言:javascript
复制
int Func(int x);   /*声明一个函数*/
int (*p) (int x);  /*定义一个函数指针*/
p = Func;          /*将Func函数的首地址赋给指针变量p*/
  • 活生生的例子:
代码语言:javascript
复制
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的例子:
代码语言:javascript
复制
// 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的例子:
代码语言:javascript
复制
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 语言双引号的字符串,约定成熟的都是放在常量区

  • 例子:
代码语言:javascript
复制
#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;
//
//}

结语

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 开始扯犊子
    • 涉及的知识点
      • 基本数据类型
        • 输入函数
          • 数组的定义
            • 控制流程
              • 指针
                • 指针运算
                  • 数组与指针
                    • 指针和函数参数
                      • 指针数组和数组指针
                        • 二级指针
                          • 函数指针
                            • 内存分配
                              • 字符串
                              • 结语
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档