2022-04-04

现在发现其实很多语法脑子里面还是有点数的,只是一般没有实践过,所以心里很没底。需要给自己多一些机会,理解别人的代码,以及锻炼书上面的习题,等到书上的都练习完了,可以找一些项目实践一下,甚至去改进一些工作上的小工具。

写代码时容易犯错的点:

  1. main函数里面不能定义其他函数,只能调用其他的函数.
  2. c++的话,编译要用g++来编译
  3. 第一次代码示例,练习了指针/友元/类的定义/实例化一个和两个对象等操作...
  #include <iostream>
  #include <string>

using namespace std;

class Person {
    string _name;
    int   _age;

    public:
    void SetName (string name) {
        _name = name;
    } 
    string GetName () {
        return _name;
    }

    friend int SetAge(Person &a, int b);
    friend int GetAge(Person &a);
    };

int SetAge(Person &a, int b) {
    a._age = b;
    return 0;
}

int GetAge(Person &a) {
    return a._age;
}

int main(){
    int a;
    int b;
    cout<<&a<<"\n"<<&b<<endl;
    
    int* p;
    p=&a;
    *p=20;
    cout<<p<<endl;
    p++;
    cout<<p<<endl;
    
    void* pp;
    pp=&p;
    cout<<pp<<endl;
    
    
    //class Person;
    
    
    Person xixi;
    xixi.SetName("xixi");
    cout<<xixi.GetName()<<endl;
    
    Person* yiyi = new Person();
    yiyi->SetName("yiyi");
    (*yiyi).SetName("yiyi1");
    cout<<(*yiyi).GetName()<<endl;
    
    
    cout<<&xixi<<endl;
    cout<<&yiyi<<endl;
    
    SetAge(xixi,4);
    cout<<GetAge(xixi)<<endl;
    
    
    cout<<"hello world!"<<endl;
    return 1;
}
  1. 注意指针的话,如果做加1或者减1操作,是按照指针指向的类型占几个Byte为单位来加减的。
  • & 这个叫取地址符
  • * 这个叫 "解引用操作符"
  • & 这个叫引用
1. 初步了解——指针与取地址

先看程序:

#include<stdio.h>
 
int main(void)
{
    int num = 7;
    int *p = &num;//初始化指针,也可以写作int* p = &num;
    printf("%d 的地址是 %p\n", num, p);
    return 0;
}
上面int *p定义了一个指向int类型指针p(我们使用*符号把p声明为指针),并初始化p使其指向int类型的变量num,这里&num中的&是取地址操作符,当&作用于一个对象上时,它返回了该对象的地址。
所以这里指针p指向了num所对应的地址。(我测试时输出了0028FF1C)


2. 如何使用指针?——解引用与指针赋值

让我们继续:

#include<stdio.h>
 
int main(void)
{
    int num = 7;
    int *p = &num;
    printf("数值%d所在的地址是 %p\n", num, p);
    printf("指针p所指向的地址为 %p , 该地址上所保存的值为%d\n", p, *p);
    *p = 100;
    printf("指针p所指向的地址为 %p , 该地址上所保存的值为%d\n", p, num);
    return 0;
}
注意这里*操作符为解引用操作符,它返回指针p所指的对象(左值)。

我们可以对*p赋值(对左值赋值),从而改变p所指的地址上所保存的值,从而改变此地址所存储的变量num的值。(上面num的值变为100)


当然,我们也可以给指针p赋值,使其指向另外一个地址(这样就改变了在解引用时获取的左值):

#include<stdio.h>
 
int main(void)
{
    int num = 7, another = -5;
    int *p = &num;
    p = &another;
    printf("%d\n", *p);//此时p指向了another,所以输出了another的值,即-5
    return 0;
}

3. 引用(引用概念在C++中才有,C中并没有)

从某种意义上来说,引用完全有别于上面说介绍的内容:

#include<cstdio>
 
int main()
{
    int val = 7, val2 = 999;
    int &refval = val, &refval2 = val2; ///引用必须要初始化,使其绑定到一个变量上
    ///修改引用的值将改变其所绑定的变量的值
    refval = -12;
    printf("%d %d\n", val, refval);//-12,refval的值和val一样
    
    ///将引用b赋值给引用a将改变引用a所绑定的变量的值,
    ///引用一但初始化(绑定),将始终绑定到同一个特定对象上,无法绑定到另一个对象上
    refval = refval2;
    printf("%d %d\n", val, refval);//999
    return 0;
}
关键:作用在引用上的所有操作事实上都是作用在该引用所绑定的对象上。

可以用int *&refp = p;的方式将一个指针引用和一个指针绑定起来。

PS:如果还是不理解,可以把引用认为是一个对象的别名(外号、简称等等)。(类比#define ...,typedef ...)


答疑:使用引用有何优点?

在传参的时候,使用指针传参,编译器需要给指针另行分配存储单元,存储一个该指针的副本,在函数中对这个副本进行操作;而使用引用传参,编译器就不需要分配存储空间和保存副本了,函数将直接对实参进行操作。所以使用引用使得程序的运行速度更快,执行效率更高。
  1. C++里面的多态,就是指的不同的派生类对基类的同一个成员函数可以有不同的实现。而操作符的重载更是提供了编译器级别的多态。

  2. 函数名相同,参数不同,叫做重载。

  1. 数组的最后一个元素是'\0' 而不是"\0",可以用这个来判断数组的长度。比如我写得这个程序
    -- yaojy纠正: 注意,字符数组的最后一个才是'\0',其他类型的数组的最后一个元素,不一定是'\0'
    -- 那么,怎么判断一个比如int类型的数组,它的长度是多少呢??? 可以用sizeof(数组名)/sizeof(数组元素类型)
#include <iostream>

using namespace std;

int main() {
    int array[11]={1,2,3,4,5,6,7,8,9,10,11};
    int* p;
    int* q;
    int i=0;
    
    //to change all element in array changed.
    int array_length = sizeof(array)/sizeof(int);
    while (i< array_length) 
    {
        cout<<"array["<<i<<"]:"<<array[i]<<endl;
        i++;
    }
    
    p = array;
    q = array + array_length - 1;
    cout<<p<<endl;
    cout<<q<<endl;
    
    cout<<*p<<endl;
    cout<<*q<<endl;
    
    int temp;
    while (p<q) 
    {
        temp = *p;
        *p = *q;
        *q = temp;
        
        p++;//yaojyNote: do not forget!!!
        q--;
    }
    
    i = 0;
    while (i< array_length) 
    {
        cout<<"array["<<i<<"]:"<<array[i]<<endl;
        i++;
    }
    
}
    char* name = {"yaojy"};
    cout<<sizeof(name)<<endl;
    
----这段代码会报下面的错,这是为什么?????

example7_12.cpp: In function ‘int main()’:
example7_12.cpp:46:19: warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]
   46 |     char* name = {"yaojy"};
      |                   ^~~~~~~

----如果写成下面这样的,又是允许的..
    char name1[]="yaojy1";
    cout<<sizeof(name1)<<endl;

原因见下面的解释:
在C++11中有明确规定

char* p = "abc"; // valid in C, invalid in C++
1
如果你进行了这样的赋值,那么编译器就会跳出诸如标题的警告。但是如果你改成下面这样就会通过warning

char* p = (char*)"abc"; //OK
1
或者这样:

char const *p="abc";//OK
1
 这到底是怎么一回事呢?事实上,我们在学习c或者c++的时候都知道,如果在赋值操作的时候,等号两边的变量类型不一样,那么编译器会进行一种叫做 implicit conversion 的操作来使得变量可以被赋值。
 
 在我们上面的表达式中就存在这样的一个问题,等号右边的"abc"是一个不变常量,在c++中叫做string literal,type是const char *,而p则是一个char指针。如果强行赋值会发生什么呢?没错,就是将右边的常量强制类型转换成一个指针,结果就是我们在修改一个const常量。编译运行的结果会因编译器和操作系统共同决定,有的编译器会通过,有的会抛异常,就算过了也可能因为操作系统的敏感性而被杀掉。
 
 像这种直接将string literal 赋值给指针的操作被开发者们认为是deprecated,只不过由于以前很多代码都有这种习惯,为了兼容,就保留下来了。
  1. 关于用指针访问二维数组里面的元素,做了个例子,有一点疑问,看下为什么???
#include <iostream>

using namespace std;

int main() {
    int array[2][3]={1,2,3,4,5,6};
    int* p;
    
    
    cout<<sizeof(array)<<endl;
    cout<<sizeof(array[0])<<endl;
    cout<<sizeof(array[0][0])<<endl;
    
    //print all array in the array[2][3];
    
    int xsize = sizeof(array)/sizeof(array[0]);
    int ysize = sizeof(array[0])/sizeof(array[0][0]);
    cout<<xsize<<" "<<ysize<<endl;
    //method1
    for (int i=0;i<xsize;i++) 
    {
        for (int j=0;j<ysize;j++)
        {
            cout<<array[i][j]<<endl;
        }
    }
    
    //method2
    for (int i=0;i<xsize;i++) 
    {
        for (int j=0;j<ysize;j++)
        {
            cout<<*(array[i]+j)<<endl;
        }
    }
    
    //method3
    for (int i=0;i<xsize;i++) 
    {
        for (int j=0;j<ysize;j++)
        {
            cout<<(*(array+i))[j]<<endl;
        }
    }
    
    //method4
    for (int i=0;i<xsize;i++) 
    {
        for (int j=0;j<ysize;j++)
        {
            cout<<*((*(array+i))+j)<<endl;
        }
    }
    
    //这个方式为什么不行呢????
    //yaojyNote: 这种方法不行,这是因为array在自增1的时候,实际上指向的已经是array[1]的首地址了,相当于一下就跳过了12。
    //method5 <----yaojyNote: not avaliable...why???????
    for (int i=0;i<xsize;i++) 
    {
        for (int j=0;j<ysize;j++)
        {
            //cout<<"address:"<<(array + i*sizeof(array[0])/sizeof(array[0][0]) + j*sizeof(array[0][0])/sizeof(int))<<endl;
            //cout<<*(array + i*sizeof(array[0])/sizeof(array[0][0]) + j*sizeof(array[0][0])/sizeof(int))<<endl;
            //cout<<(array+i*3+j)<<endl;
            //cout<<**(array+i*3+j)<<endl;
            cout<<(array)<<endl;//假设这个地址是0x0000;
            cout<<(array+1)<<endl;//就会发现这个打印出来是0x000c;<----直接跳过了array[0]的三个元素,直接跳到了array[1]的元素...因此这种方式是不能work的。
            cout<<(array+2)<<endl;
            cout<<(array+3)<<endl;
        }
    }
    
    return 1;
    
}
  1. 指針的一個理解:
    注意下面這個例子,明显,
  1. C++里面不让字符串直接赋值给char *,但是可以强制类型转换一下,C语言里面是可以的。
#include <iostream>

using namespace std;

void test(char* str) 
{
    //cout<<str<<endl;
    printf("string is %s\n",str);
    printf("string is %p\n",str);
    str=(char *)"hello world!!!";
    //cout<<str<<endl;
    printf("string is %s\n",str);
    printf("string is %p\n",str);
};


int main() {
    char* string;
    string = NULL;
    //cout<<string<<endl;
    printf("string is %s\n",string);
    test(string);
    //cout<<string<<endl;
    printf("string is %s\n",string);
    
    return 0;
}

Refrences

《轻松学C++》
《C++不如coding》from bilibili
《C++》清华大学郑莉, from bilibili
《指针》印度小哥 from bilibili

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

推荐阅读更多精彩内容