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

前言

许久未碰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;
//
//}

结语

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

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