《算法笔记》笔记之笔记 - 2、C&C++快速入门(4)

16 篇文章 0 订阅
16 篇文章 0 订阅

2.6 函数

2.6.1 函数的定义

函数是一个实现一定功能的语句的集合。它可以使代码更加简洁和清晰,不必将一些语句多次重复写一遍。

函数的基本语法格式如下所示:

返回类型 函数名称(参数类型 参数){
    函数主体
}

若是小括号里没有添加参数,我们就称这个函数为无参函数,否则就为有参函数。C语言中还会使用return来返回函数需要传回的数据,且传回参数的数据类型必须跟函数的返回类型一致。这里有一个特殊的返回类型void,它表示空,使用它的时候我们就不需要返回参数了。

(1)全局函数

全局函数是指在定义之后的所有程序段内都有效的变量。

#include <stdio.h>
// 全局变量x
int x;
void change(){
    x = x + 1;
}
int main(){
    x = 10;
    change();
    printf("%d\n", x);
    return 0;
}

// 输出结果:
// 11

所有在定义x之后的程序段都共用这个x,所以使用change()函数可以直接改变x的值

(2)局部变量

局部变量定义在函数内部,且旨在函数内部生效。函数结束就会被销毁。

#include <stdio.h>
void change(int x){
    x = x + 1;
}
int main(){
    int x = 10;
    change(x);
    printf("%d\n", x);
    return 0;
}

// 输出结果:
// 10

由于局部变量只在函数内部生效,所以change()函数中的x被赋值之后直接就被销毁了,所以main()函数中输出的x仍然是在其中定义的x的值10。

注意,main()函数中的x和change()函数中的x不是同一个。大家可以理解为当我们在main()函数中通过change()函数传递x的值时,系统另外开辟了一个空间,也取名叫做x,我们把原先x中的值给了它,而这个x中的值进行的一系列操作,并不会影响原先的x。就像有两个人都叫小明一样。

这种传递参数的方式称为值传递,函数定义的小括号内的参数称为形式参数形参;而把实际调用时小括号内的参数称为实际参数实参

2.6.2 再谈main()函数

主函数对于程序来说只能有一个。无论它在程序的哪个位置,整个程序都是从它的第一个语句开始执行,然后在需要调用其他函数的时候才会去调用其他函数。

int main(){return 0;
}

现在我们再看main()函数可以发现,main就是函数名称,由于小括号里没有参数,所以它还是一个无参函数,返回类型是int类型,并且在函数主体的最后都返回了0。这个0的意义就在于告知计算机系统程序是正常终止的。

2.6.3 以数组作为函数参数

如果是数组作为函数参数,数组的第一维不需要填写长度,实际调用时也只需填写数组名。而且数组作为参数时,在函数中对数组元素的修改就相当于对原数组元素的修改(和局部变量不一样)。不过数组不允许作为返回类型出现,所以会有下面的方法,将数组作为参数传入。

#include <stdio.h>
void change(int a[], int b[][5]){
    a[0] = 1;
    a[1] = 3;
    a[2] = 5;
    b[0][0] = 1;
}
int main(){
    int a[3] = {0};
    int b[5][5] = {0};
    change(a, b);
    for(int i=0; i<3, i++){
        printf("%d\n", a[i]);
    }
    return 0;
}

// 输出结果
// 1
// 3
// 5

2.6.4 函数的嵌套调用

函数的嵌套调用就是指在函数中调用另一个函数,跟在main()函数中调用其他函数的方法一样。

2.6.5 函数的递归调用

函数的递归调用就是函数自己调用自己的过程。

2.7 指针

2.7.1 什么是指针

在计算机中,我们程序中的每个变量都会存放在内存中。内存中的空间就像是一个一个的小房间,变量就住在里面。我们可以把一个字节理解为一个房间,一个int型的变量就需要占用连续的4个房间。有了房间,自然要有房间号,要不然我们怎么知道到哪里去找它呢?房间号就类似于计算机中说的地址,通过地址我们就能找到变量。变量的地址一般指它占用的字节中第一个字节的地址。

在C语言中,用“指针”来表示内存地址,初学者可以简单地认为指针就是变量的地址(当然这么说不那么严谨)。怎样获得指针呢?可以用前面提到的取址符&,只要在变量的前面加上&,就代表这个变量的地址。指针是一个unsigned类型的整数

2.7.2 指针变量

指针变量就是用存放指针的变量,类似于int型变量就是存放int型常量。它的定义与普通变量的定义有些不同,格式如下:

int* p1;	// 等价于int *p1;
double* p2;	// 等价于double *p2;
char* p3;	// 等价于char *p3;

这个“*”号可以添加在数据类型之后,也可以添加到变量之前,看自己的风格。但大家要牢记int*等形式才是指针变量的类型。我们在存储地址的时候,赋值给p而不是*p。其次,这里的int、double和char又叫做变量的基类型,基类型必须和指针变量存储的地址类型相同。

另外,如果一次有好几个同种类型的指针变量要同时定义,星号只会结合第一个变量,如:

int* p1, p2;

上面语句中的p1是int*型的,p2则是int型的。如果要定义多个int*型的变量,我们必须写成如下形式才行:

int* p1, *p2, *p3;
// 或许这样更好看
int *p1, *p2, *p3;

若是要取地址然后赋值给同种类型的指针变量,还可以这样定义:

int a;
int* p = &a;

// 或者
int a;
int* p;
p = &a;

那么,如果我们要取出地址内的东西该如何操作呢?还是使用星号,我们可以将它看成是开门的钥匙,于是*p就代表地址内的值。注意,我们经常会改变地址内的值,只要地址不变,其他指向这个地址的数值会跟着改变。

指针变量同时支持加减法和自增自减操作。它的加减法的结果是整个地址的偏移。比如对于int*型的指针变量p来说,p+1指的是p所指的int型变量的下一个int型变量地址,这里的下一个跨越了一整个int型字节内存。如果p指向地址1,那么p+1就是跨越了2、3、4指向地址5。

2.7.3 指针与数组

数组是由地址上连续的若干个相同类型的数据组合而成。也是因为地址连续,只要知道数组首个数据的地址,我们就能知道数组其他数据的地址。在C语言中,数组名称也作为数组的首地址使用,对于数组a,a == &a[0]是成立的。

前面提到过指针变量支持加减法,很容易推出对于数组a,下面几个等式也都是成立的:

a+i == &a[i];
*(a+i) == a[i];

scanf("%d", a+i);	// 等价于scanf("%d", &a[i]);
printf("%d", *(a+i));	// 等价于printf("%d", a[i])

顺便提一下,两个int型的指针相减,等价于在求两个指针之间相差了几个int

2.7.4 使用指针变量作为函数参数

此时视为将变量的地址传入函数,如果在函数中改变这个地址中的元素,那么原先的数据也会跟着改变。这种传递方式叫做地址传递,与值传递的方式对应。总结一下就是,**只有在获取地址的情况下对元素进行操作,才能真正地修改变量。**数组也是一样。

这里总结一下两种初学者常犯的错误写法(这里以交换a和b的值为例):

// 第一种错误写法:
void swap(int* a, int* b){
    int* temp;
    *temp = *a;
    *a = *b;
    *b = *temp;
}

这里的代码看似正确,但对于指针来说是有问题的,问题在于temp。由于没有初始化,指针变量temp中存储的地址是随机的,可能会指向系统工作区间,这样就会出错!当然,如果我们只是定义一个变量就不会出现这种问题,所以我们可以很好解决这个问题。

// 第一种错误写法修改:
void swap(int* a, int* b){
    int x;
    int* temp = &x;
    *temp = *a;
    *a = *b;
    *b = *temp;
}

第二种错误写法则是想直接更换两个变量的地址:

// 第二种错误写法
void swap(int* a, int* b){
    int* temp = a;
    a = b;
    b = temp;
}

main()函数传给swap()函数的地址其实是一个“无符号整型”的数,其本身跟普通变量一样只是“值传递”。在swap()函数中对地址本身的修改,并不会更改main()函数中的地址。

2.7.5 引用

  1. 引用的含义

引用是C++中一个强有力的语法,当我们想不使用指针也达到修改传入参数的目的的时候,一个很方便的方法就是使用“引用”。引用不产生副本,而是给原变量起了一个别名,对引用变量的操作就是对原变量的操作

使用引用我们只需要在参数类型后面加个&就可以了,至于加在类型后还是变量名前都可以,一般我们加在变量名前面。由于这是C++中的语法,所以使用时我们要将文件保存为.cpp格式。

#include <stdio.h>
// 这里&x中的x不一定要和原来的相同,写成&a也完全可以!
// 函数的参数名和实际传入的参数名可以不同!!
void change(int &x){
    x = 1;
}
int main(){
    int x = 10;
    change(x);
    printf("%d\n", x);
    return 0;
}

// 输出结果:
// 1
  1. 指针的引用

在2.7.4节的错误写法二中,我们无法通过直接修改传入的地址来改变两个变量,这是因为对指针变量本身的修改无法作用到原指针变量上。但是在这里,我们可以使用引用来实现上面的效果:

#include <stdio.h>

void swap(int* &p1, int* &p2){
    int* temp = p1;
    p1 = p2;
    p2 = temp;
}

int main(){
    int a=1, b=2;
    // 使用指针变量存放地址,才能修改地址,否则传进去的是常量。
    int *p1=&a, *p2=&b;
    swap(p1, p2);
    printf("a = %d, b = %d", *p1, *p2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值