现在发现其实很多语法脑子里面还是有点数的,只是一般没有实践过,所以心里很没底。需要给自己多一些机会,理解别人的代码,以及锻炼书上面的习题,等到书上的都练习完了,可以找一些项目实践一下,甚至去改进一些工作上的小工具。
写代码时容易犯错的点:
- main函数里面不能定义其他函数,只能调用其他的函数.
- c++的话,编译要用g++来编译
- 第一次代码示例,练习了指针/友元/类的定义/实例化一个和两个对象等操作...
#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操作,是按照指针指向的类型占几个Byte为单位来加减的。
- & 这个叫取地址符
- * 这个叫 "解引用操作符"
- & 这个叫引用
1. 初步了解——指针与取地址
先看程序:
#include<stdio.h>
int main(void)
{
int num = 7;
int *p = #//初始化指针,也可以写作int* p = #
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 = #
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 = #
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 ...)
答疑:使用引用有何优点?
在传参的时候,使用指针传参,编译器需要给指针另行分配存储单元,存储一个该指针的副本,在函数中对这个副本进行操作;而使用引用传参,编译器就不需要分配存储空间和保存副本了,函数将直接对实参进行操作。所以使用引用使得程序的运行速度更快,执行效率更高。
C++里面的多态,就是指的不同的派生类对基类的同一个成员函数可以有不同的实现。而操作符的重载更是提供了编译器级别的多态。
函数名相同,参数不同,叫做重载。
- 数组的最后一个元素是'\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,只不过由于以前很多代码都有这种习惯,为了兼容,就保留下来了。
- 关于用指针访问二维数组里面的元素,做了个例子,有一点疑问,看下为什么???
#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;
}
- 指針的一個理解:
注意下面這個例子,明显,
- 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