文章目录
指针
1、概念
把地址作为数据处理
指针变量:存储地址的变量
eg:a是变量b的指针,意思是a存了b的地址,也可以说a指向b.
2、指针变量的定义
类型标识符 *指针变量;
int *intp; //用*特别强调!!指针存的是起始地址!
double *doublep;//这里加类型,是说明指针指向的单元的数据类型,输出的时候就只取那么多
int *p, x, *q;//定义了俩指针和一个int变量,*必须每次都跟着
3、指针变量的赋值
- 地址运算符 “&”表示取地址
p = &x;
注意:& 运算符后面不能跟常量或表达式,如 &2 和 &(m * n + p )是没有意义的. - 改变指针的指向:通过对intp重新赋值
intp=&y
. - 同类的指针变量之间可相互赋值,表示两个指针指向同一内存空间。
- 空指针:指针没有指向任何空间,用常量NULL表示,NULL的值一般赋为0
注意:不能引用空指针指向的值(一般作为循环判断条件)
4、用指针变量对原单元的值进行操作
引用运算符 * 表示的是指针指向的这个单元的内容:*intp = 5
是把 x (intp指向的单元内容)改成 5.
注意:在对 intp 使用引用运算之前,必须先对 intp 赋值
5、注意事项
指针在使用前必须初始化。
空指针NULL是一个特殊指针值,它的值为0。它可被用来初始化一个指针,表示不指向任何地址。//空指针为假,其他为真
6、指针运算与数组
数组元素是一个独立的变量,因此可以有指针指向它:p = &a[1], p = &a[i]
数组名可以看成是常量指针,意思是不可以对数组名的变量进行操作.
如执行了p=array,则p与array是等价的,对该指针可以进行任何有关数组下标的操作
若定义 int a[10], *p
,并且执行了 p = a
,那么可用下列语句访问数组a的元素
for ( i=0; i<10; ++i )
cout << p[i]; //好怪救命
虽然通过指针可以访问数组,但两者本质是不同的:在定义数组时为数组的各个元素分配了全部的存储区,而在定义指针时仅仅分配四个字节的存储区存放指针地址。只有把一个数组名赋给了对应的指针后,指针才能当作数组使用.
7、指针运算
指针+1表示数组中指针指向元素的下一元素地址;
指针-1表示数组中指针指向元素的上一元素地址;
合法的指针操作:p + k
, p - k
,p1 - p2//p1和p2之间有几个元素
对数组:第i个元素的地址可表示为 intp + i
,第i个元素的值可表示为 *(intp + i)
等价于p[i]
等价于a[i]
。
8、指针作为函数参数
用指针作为参数可以在函数中修改主调程序的变量值,即实现变量传递。必须小心使用!!!
void swap(int *a, int *b){//*定义指针
int c;
c=*a; *a= *b; *b=c;//*对地址对应的单元操作
}
//用函数的时候:
swap(&x, &y)//&取地址
swap用引用更好
数组传递的本质是地址传递,因此形参和实参可以使用数组名,也可以使用指针。
数组传递时函数的声明可写为:
type fun(type a[], int size);
也可写为:
type fun(type *p, int size);
但在函数内部,a和p都能当作数组使用;调用时,对这两种形式都可用数组名或指针作为实参。调用举例:
fun(a,10);
建议:如果传递的是数组,用第一种形式;如果传递的是普通的指针,用第二种形式.
实例:分治法代码没写过,复制的老师的
void minmax ( int a[] , int n , int *min_ptr , int *max_ptr){
int min1 , max1 , min2 , max2;
switch(n){
case 1: *min_ptr = *max_ptr = a[0]; return;
case 2: if (a[0] < a[1] ) { *min_ptr = a[0]; *max_ptr= a[1]; }
else { *min_ptr = a[1]; *max_ptr= a[0];}
return;//???????
default: minmax( a, n/2, &min1, &max1 );
minmax( a + n/2, n - n / 2, &min2, &max2 );
if (min1 < min2)
*min_ptr = min1;
else *min_ptr = min2;
if (max1 < max2)
*max_ptr = max2;
else *max_ptr = max1;
return;
}
}
9、返回指针的函数
类型 *函数名(形式参数表);
- 当函数的返回值是指针时,返回地址对应的变量不能是被调函数的局部变量。
理解:type a; type *b=&a;//指向type类型的指针
10、引用传递
减少星号的地址传递方式!
引用的定义:给一个变量取一个别名,但事实上指向同一个内存单元。
例子:
int i;
int &j=i; //&不要和取地址混淆了……救命
-
引用实际上是一种隐式指针。每次使用引用变量时,可以不用书写间接引用运算符“*”,因而引用简化了程序。
-
C++引入引用的主要目的是将引用作为函数的参数。
-
注意:
定义引用时必须立即对它初始化,不能定义完成后再赋值;
为引用提供的初始值可以是一个变量或另一个引用;
引用不可重新赋值,不可使其作为另一变量的别名。//不能再改变了 -
sizeof(引用)得到的是该变量的大小;sizeof(指针)得到的是指针本身的大小(在32位系统中为4字节)。
-
指针和引用的自增运算意义不一样。//指针+1是加上数据类型大小,引用是本身加1
指针与引用的不同写法
理解:调用swap(x,y)时,相当于发生了变量定义int &m = x; int &n = y
,即形式参数m和实际参数x共享一块空间,形式参数n和实际参数y共享一块空间。在swap函数中交换了m和n的值,就相当于交换了x和y的值。意思就是,如果你想要在函数里面改变这个变量,引用传递更好使。 -
如果在函数内不许改变参数的值,则参数用const限定。
-
对非const的参数,实际参数不能是常量或临时量。
-
Const参数可以是常量
-
关于函数模板的使用:什么时候复习函数模板?嗯?
template <int n> //n是一个非类型参数
void g(int (&arr)[n]){
cout <<typeid(arr).name()<<" "<< sizeof(arr) << endl;
}
int main(){
int a[10]={1,2,3,4,5,6,7,8,9,0};
cout <<typeid(a).name()<<" "<< sizeof(a) << endl;
g(a);
return 0;
}
//输出:
//A10_i 40
//A10_i 40
勿混淆“”的两种不同用法:ab乘法,int a指针,后面再出现a表示引用,取值
例题
14291. 【原4291】凑数题
听说同学们学了指针,憨憨助教想借此机会考考大家,然而CPU大作业要写不完了,于是就有了这样一道凑数题…
写出以下代码中的两个函数funA和funB的声明与定义,使得funA(p)=a能实现通过指针p访问用户输入的字符串a,funB能将该字符串中的小写字母全部变成大写,并返回这个大写的字符串。
要求如下:
除了实现funA和funB外,不能修改包括main函数在内的其他代码,不能在全局定义新的变量、指针、数组和函数等,不能调用其他的库。
funA和funB内不能调用任何输入输出函数(包含但不限于cin,cout,scanf,prinf,getchar,putchar等等)。
不允许存在内存泄漏,由于OJ不能检测内存泄漏(同学们可以自行了解内存泄漏检测工具valgrind),助教将进行手动评测(以最后一次提交为准)。
用户输入的单个字符串长度不会超过98,而且只含有小写和大写字母。
#include <iostream>
#include <cstring>
using namespace std;
// 写出两个函数的声明
int main() {
char a[100], b[100];
for(int i = 0; i < 3; ++i) {
char **p;
cin >> a;
funA(p) = a;
cout << funB(b, p);
}
return 0;
}
// 写出两个函数的定义
Ans:
#include <iostream>
#include <cstring>
using namespace std;
char *&funA(char **&p);//char *表类型,&取地址,funA(char **表类型,&表引用)
char *funB(char targ[],char **p);
int main() {
char a[100], b[100];
for(int i = 0; i < 3; ++i) {
char **p;
cin >> a;
funA(p) = a;
cout << funB(b, p);
}
return 0;
}
char * &funA(char ** &p){
p = new char *;//p指向存了char a位置的地址
return *p;//这个p现在和a一样了
}
char *funB(char *targ,char **p){
int len = strlen(*p);
for(int i=1;i<=len;i++){
if((*p)[i-1]>='a'&& (*p)[i-1]<='z') targ[i-1] = (*p)[i-1]+'A'-'a'; //注意(*p)是整体表示p指向的单元,是一个地址,然后再方括号[i-1]是取该地址的值
else targ[i-1] = (*p)[i-1];
}
targ[len] = '\n';//记得换行
targ[len+1] = '\0';
return targ;
}
暂时还没有学习valgrind,学了来补充内容