《算法笔记》2.6函数 2.7指针
一、学习概览
2.6函数
函数:实现一定功能的语句的集合。
2.6.1函数的定义
- void——“空”。表示自定义函数只是单纯实现一些语句,而不返回变量。
void printf1(){}
- return——C语言中使用return来返回函数需要传回的数据,且return后面的数据类型要和一开始给出的返回类型相同。
- 全局变量和局部变量:(注意:如果想让定义的变量对所有的函数都有用,最好还是使用全局变量的定义方式。)
1.全局变量:(推荐使用)
在定义之后的所有程序段内都有效的变量**(定义在其之后所有函数之前)**。
2.局部变量:
定义在函数内部,且只在函数内部生效,函数结束时局部变量销毁。
关于局部变量的一些专有名词:见局部变量
副本 值传递 形式参数(形参) 实际参数(实参)
- 传入多个参数时,用逗号“,”隔开。
2.6.2再谈main函数
- main函数返回0——告知系统 程序正常终止。
2.6.3以数组作为函数参数
- 数组作为参数时,第一维不需要填写长度,第二维需要。实际调用时也只需填写数组名。见数组作为函数参数
- 求解三个整数中的最大值时,可以先自定义一个比较两个数的函数,然后再调用两次(分别两次比较,每次比较两个变量)来实现。
- 每个字节(房间)都会有一个地址(房间号),计算机通过地址找到变量。
- 变量的地址:一般指它占用的字节中第一个字节的地址。
2.7指针
2.7.1什么是指针
- C语言中用指针来表示内存地址 (或者称指针指向了内存地址),如果这个内存地址恰好是某个变量的地址,则称“这个指针指向该变量”。可以认为,指针就是变量的地址。
2.7.2指针变量
- 只要在变量前面加上&,就表示变量的地址。&——取地址运算符。
- 非指针变量a的地址可以输出by &a:
printf("%d, %d\n",&a,a);
- 指针是一个unsigned类型的整数。
1.整型的每一种都分为:无符号(unsigned)和有符号(signed)两种类型(float和double总是带符号的),在除char以外的数据类型中,默认情况下声明的整型变量都是有符号的类型;char在默认情况下总是无符号的。在除char以外的数据类型中,如果需声明无符号类型的话就需要在类型前加上unsigned。
2. 无符号版本和有符号版本的区别就是无符号类型能保存2倍于有符号类型的正整数数据,比如16位系统中一个short能存储的数据的范围为-32768-32767,而unsigned能存储的数据范围则是0-65535。
3.由于在计算机中,整数是以补码形式存放的。根据最高位的不同,如果是1,有符号数的话就是负数;如果是无符号数,则都解释为正数。
4.另外,unsigned若省略后一个关键字,大多数编译器都会认为是unsigned int。
- 指针变量是用来存放指针(地址)的变量。定义时会在某种数据类型后加*来表示这是一个指针变量。
注意:*放在变量名之前也可以。但C++程序猿更倾向于前一种写法。
int* p;
double* p;
char* p;
int *p1,*p2,*p3; //如果一次有多个同种类型的指针变量要同时定义,
//*只会结合于第一个变量名。此时为了美观,会把*放在变量名之前。
- 给指针变量赋值的方式:把变量的地址取出来,然后赋给对应类型的指针变量。
int a;
int* p=&a; //p的基类型为int
int a,b;
int *p1=&a,*p2=&b;
//注意:地址&a是赋值给p而不是*p的(星号是类型的一部分)。
//int*是指针变量的类型,p才是用来存储地址的变量名。
- 如何得到地址所指的元素:把*视为一把开启房间的钥匙,加在p的前面。
#include <stdio.h>
int main() {
int a;
int* p=&a; //指针变量p得到整型变量a的地址
a=233;
printf("%d\n",*p); //用*打开p储存的房间号所对应的房间的门,得到里面变量的值
return 0;
}
//若直接对*p赋值,也可以改变保存的元素:
#include <stdio.h>
int main() {
int a;
int* p=&a;
*p=233;
printf("%d, %d\n",*p,a);
return 0;
}
//若输出p,则输出的是变量a的存储地址:
#include <stdio.h>
int main() {
int a;
int* p=&a; //指针变量p得到整型变量a的地址
a=233;
printf("%d\n",p); //用*打开p储存的房间号所对应的房间的门,得到里面变量的值
return 0;
}
若直接对*p赋值,也可以改变保存的元素:
若输出p,则输出的是变量a的存储地址:
- 指针变量的加减法(支持自增、自减)一般用于数组:减法——两个地址偏移的距离;p+1(p+i)——p所指的int型变量的下一(i)个int型变量地址(跨越4Byte或4iByte)。a+i等同于&a[i](a就是数组的首地址),*(a+i)等同于a[i]。
两个int型的指针相减,等价于在求两个指针之间相差了几个int(除以4——1个int占用4Byte)。(该解释对其他类型的指针同样适用。)
scanf("%d",a+i); //输入数组元素的新颖写法
printf("%d",*(a+i)); //输出
#include <stdio.h>
int main() {
int a;
scanf("%d",&a);
printf("%d %d\n",a,&a); //输出变量存储的值,变量的地址
return 0;
}
2. 基类型:指针变量存储的地址的类型。必须和指针变量存储的地址类型相同。
#include <stdio.h>
int main() {
char a;
char* p=&a;
*p='c';
printf("%c, %c\n",*p,a);
return 0;
}
//会报错的代码
#include <stdio.h>
int main() {
char a;
int* p=&a; //必须是char,和变量类型相同
*p='c';
printf("%c, %c\n",*p,a);
return 0;
}
2.7.3指针与数组
- 代码辨析
int* p=&a; //a为普通变量
int* p=a; //a为数组名,同时也是数组的首地址&a[0]。
//数组名称也作为数组的首地址使用
2.7.4使用指针变量作为函数参数
- 使用指针变量作为函数参数时,是把变量的地址传入函数。因此,只有在获知地址的情况下对元素进行操作,才能真正地修改变量。
2.7.5引用
-
引用:不使用指针,也能修改传入参数。
不产生副本,只是给原变量(常量不可以使用引用)起了个别名。对引用变量的操作就是对原变量的操作。 -
引用的使用方法:会在某种数据类型后加&来表示这是一个引用变量。
注意:考虑到引用是别名的意思,&一般放在变量名之前。 -
不管是否使用引用,函数的参数名与实际传入的参数名可以不同。
#include <stdio.h>
void change(int &x){
x=1;
}
int main(){
int a = 10;
change(a);
printf("%d\n",a);
return 0;
}
- 如果读者阅读代码时碰到了引用,却对其含义不甚了解时:先去掉引用,看其原意是什么,再加上引用——这样会容易理解得多。
二、代码分析
局部变量
#include <stdio.h>
void change(int x){
x=x+1;
}
int main(){
int x=10; //这里也要改为a
change(x); //x改为a也是同样结果,这句好像没啥用啊
printf("%d\n",x); //这里也改为a
return 0;
}
分析:
当在主函数中定义了x之后,将其作为 change()函数的参数传入,并令x加1,但是最后输出时x却仍然是10。
这是因为 change函数的参数x为局部变量,仅在函数内部生效,通过change(x)传进去的x其实只是传进去一个副本,也即 change函数的参数x和main函数里的x其实是作用于两个不同函数的不同变量(虽然名字相同),取成不同的名字当然是可以的。
这种传递参数的方式称为值传递,函数定义的小括号内的参数称为形式参数或形参,而把实际调用时小括号内的参数称为实际参数或实参。
单向一次性的值传递在传递参数时会产生一个副本,对副本的操作不会影响main函数中a、b的值。
参数传递的三种方式:传值(value),传址(address),和传引用(reference)
1.传值时,子函数(被调用者)复制父函数(调用者)传递的值,这样子函数无法改变父函数变量的值
2.传址时,父函数将变量的地址传递给子函数,这样子函数可以能过改写地址里的内容改变父函数中的变量
3.传引用则是一种看起来像传值调用,而实际上功能同传址一样的传递方式。子函数可以改写父函数的变量值。
main函数也是一个函数!!!
数组作为函数参数
#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;
}
分析:
注意,数组作为参数时,在函数中对数组元素的修改就等同于是对原数组元素的修改(这与普通的局部变量不同)。
虽然数组可以作为参数,但是却不允许作为返回类型出现。如果想要返回数组,则只能用上面的方法,将想要返回的数组作为参数传入。
使用指针作为参数,交换两个数
#include <stdio.h>
void swap(int* a, int* b){
int temp=*a;
*a=*b;
*b=temp;
}
int main(){
int a = 1, b =2;
int *p1=&a,*p2=&b; //取地址符
swap(p1,p2);
printf("a=%d,b=%d\n",*p1,*p2);
return 0;
}
错误写法一:temp没有被初始化
void swap(int* a, int* b){
int* temp;
*temp=*a;
*a=*b;
*b=*temp;
}
错误写法二:认为地址交换后元素就交换了
void swap(int* a, int* b){
int* temp=a;
a=b;
b=temp;
}
其实函数作用的还是地址的副本。
用引用来修正“错误写法二”
#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); //不可以写成swap(&a,&b),因为常量不可以使用引用
printf("a=%d,b=%d\n",*p1,*p2);
return 0;
}