指针(进阶讲解之二)

数组和指针的关系

  • 数组名表示一种数据结构,这种数据结构就是数组,指针代表地址
  • 数组名可以看作指针常量,不能作自增自减等操作,不能被修改
    这就可以解释为什么我们如此定义时不能进行一般修改:char *s="abcd";
  • 指向数组的指针则是另外一种变量类型,仅仅代表着数组的存放地址
  • 数组名作为函数形参时,在函数体内,作为指针处理,因此ta失去了其常量特性,可以做自增自减等操作,可以被修改

传递指针的选择排序

void swap(int *const p1,int *const p2) {
	int t=*p1;
	*p1=*p2;
	*p2=t;
}

void selectionSort(int *const a,const int size) {
    int t=0;
	for (int i=0;i<size-1;i++) {
		t=i;
		for (int j=i+1;j<size;j++)
			if (a[j]<=a[t]) t=j;
		swap(&a[t],&a[i]);
	} 
}

打牌吗?* ★° * ☆( ̄▽ ̄)/ * °★ *

52张牌(抽出大小鬼)的洗牌和发牌

在这里插入图片描述

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<iomanip>

using namespace std;

const char *suit[4]={"Hearts","Diamonds","Clubs","Spades"};
const char *face[13]={"Ace","Deuce","Three","Four",
                      "Five","Six","Seven","Eight",
                      "Nine","Ten","Jack","Queen","King"};
int p[4][13]={0};

int main()
{
	srand(static_cast<unsigned int>(time(0)));
	enum poker{Ace=1,Deuce,Three,Four,
               Five,Six,Seven,Eight,
               Nine,Ten,Jack,Queen,King};
	for (int i=1;i<=52;i++) {
		int x=rand()%52;
		while (p[x/13][x%13]) x=rand()%52;
		p[x/13][x%13]=i;
	}
	for (int i=1;i<=52;i++) 
		for (int j=1;j<=52;j++)
			if (p[j/13][j%13]==i) {
				cout<<setw(2)<<j<<":"<<setw(8)<<suit[j/13]<<" "<<suit[j%13]<<endl;
				break;
			}
	system("pause");
	return 0;
}

在这里插入图片描述


函数指针

什么是函数指针?

函数存放在内存的代码区域内,同样有地址,我们如何能获得函数的地址呢?

函数代码是程序的算法指令部分,ta们和数组一样也占用存储空间,都有相应的地址。
指针变量可以指向数组的首地址,也可以使用指针变量指向函数代码的首地址。
指向函数代码首地址的指针变量成为函数指针。

函数指针的基本形式就是这样的啦:

函数类型 (*指针变量名) (形参列表)
  • ()
    " () " 的优先级高于 " * ",所以指针变量名外的括号必不可少
  • 形参列表
    指针变量指向的函数所带的参数列表

例如:

int (*f)(int x);
double (*ptr)(double x);

两个一致:

  • 函数指针和ta指向的函数的参数个数和类型都应该是一致的
  • 函数指针的类型的函数的返回值类型也必须是一致的
函数指针的赋值?
inr func(int x);     //声明一个函数
int (* f)(int x);    //声明一个函数指针
f=func;              //将func函数的首地址赋给指针f
如何用函数指针调用函数?
  • 首先,要说明函数指针变量:int (*f)(int x);
  • 其次,要对函数指针变量赋值:f=func;
  • 最后,要用(*指针变量)(形参列表)调用函数:(*f)(x)
#include<iostream>

using namespace std;

int test(int x);
int main()
{
    cout<<test<<endl;
    int (*f)(int x);
    f=test;
    cout<<f(5)<<endl;
    return 0;
}
int test(int x) {return x*x;}

/*
输出:
00D410FA
25
*/

又来选择排序

(说实话,sort不够香还是怎么着,老师为什么要一直讲选择排序/捂脸)
这次的选择排序有高级了一点,不光有传递指针的swap函数
还可以支持升序或降序两种排序方式
而比较器的传入使用的就是刚刚介绍过的函数指针

我们在外部声明比较器:

bool ascending(int,int);
bool descending(int,int);

在之后传入函数的过程中,定义一个函数指针,指向比较器的地址

void selectionSort(int a[],int n,bool (*cmp)(int,int) {...}

之后在函数中就可以正常调用 c m p ( x , y ) cmp(x,y)

#include<iostream>
#include<iomanip>

using namespace std;

void selectionSort(int [],const int,bool (*)(int,int));
void swap(int * const,int * const);
bool ascending(int,int);
bool descending(int,int);

int main() {
	const int arraySize=10;
	int order;
	int a[arraySize]={2,6,4,8,10,12,89,68,45,37};
	cout<<"Enter 1 to sort in ascending order\n"
		<<"Enter 2 to sort in descending order: ";
	cin>>order;
	cout<<"\nData items in original order\n";
	for (int i=0;i<arraySize;i++)
		cout<<setw(4)<<a[i];
	if (order==1) {
		selectionSort(a,arraySize,ascending);
		cout<<"\nData items in ascending order\n";
	}
	else {
		selectionSort(a,arraySize,descending);
		cout<<"\nData items in ascending order\n";
	}
	for (int i=0;i<arraySize;i++)
		cout<<setw(4)<<a[i];
	cout<<endl;
	system("pause");
	return 0;
}

void selectionSort(int a[],int n,bool (*cmp)(int,int))
{
	int opt;
	for (int i=0;i<n-1;i++) {
        opt=i;
		for (int j=i+1;j<n;j++) 
			if (!cmp(a[opt],a[j])) opt=j;
		swap(&a[i],&a[opt]);
	}
}

void swap(int *const a,int *const b) {
	int x=*a;
	*a=*b;
	*b=x;
}

bool ascending(int a,int b) {return a<b;}
bool descending(int a,int b) {return a>b;}

字符变量和字符常量

  • 字符常量:
    就是那0~255,256个字符,值为其ASCII码值
  • 字符变量
    • char类型的字符变量可以直接进行关系运算:
      char a = ‘a’, b; cin >> b; if(a>b) ...
    • string类型
      include letters(字母A~Z, a-z), digits(数字0~9)and various special characterssuch as +, -, *, / and $.
      “Welcome to C++!”, “Hello, World!”等 string literals(字符串文本), or string constants(字符串常量)
      ①值为首字符的地址
      ②静态存储类别。
      这大概就可以解释:
      char *q= "blue!"; *(q+2) = ‘A’; cout << q<< endl; // 将字符串常量的地址赋值给字符指针,内存访问错误
字符串初始化
char color[]="blue";
//char color[]={'b','l','u','e','\0'};
const char *colorPtr="blue";

第一种声明方式定义了一个具有5个元素的内置数组color,ta包含字符 ’ b ’ , ’ l ’ , ’ u ’ , ’ e ’ , ’ \0 ’
第二种声明方式创建了指针变量colorPtr,ta指向内存某处的字符串 " blue\0 ",字符串是static存储类别的,在程序执行时间内一直存在

字符数组的读入
  • 这种方法太难了,只有想要逗你玩的大佬才会这么写,初学者根本想不到这种方法:
char s[20]; 
for(int i=0; i<20; i++)  cin>>s[i];
  • 输入流读入:
    注意这读入的字符最多只能由19个,因为 " \0 " 需要在字符数组中占据一个位置
char s[20]; 
cin>>s;
  • 设置域宽的输入流读入:
    指定cin最多读入n-1个字符,并将第n个字符赋值为 " \0 "
char s[20]; 
cin>>setw(n)>>s;  //n不大于20
  • get&&getline
    (妈耶,这真的是最让我mengbier的两个操作,噗,但是我还是要努力讲解一下)
  • get和getline所属iostream类,作用是读取一整行,通过换行符确定读取结束,ta们都可以读取空格
  • get与getline区别
    getline会在读取结束后舍弃换行符,而get回将换行符保留到输入序列中

实际上,get函数有三种版本:

  • 无参数:cin.get();
    读入单个字符,包括空白字符
  • 一个字符型参数:cin.get(c);
    自动读取输入流中的下一个字符
  • 三个参数:cin.get(s,s_size,"\n"); 或者 cin.get(s,s_size);
    分别是接受字符的字符数组,接收字符的数量和终止字符(默认 " \n " )
    输入的字符数可以小于s_size
    为使字符数组(被程序用作缓冲区)中的输入字符串能够结束,空字符会被插入到字符数组中
    函数不把终止字符放到字符数组中,但是终止字符仍然会保留在输入流中

成员函数 getline与带三个参数的get函数类似
cin.gatline(s,s_size);
ta读取一行信息到字符数组中然后插入一个空字符
不同的是,getline要去除输入流中的终止字符,不把ta存放在字符数组中

为了避免出现一些不必要的麻烦,强烈建议大家把s_size设置的足够大

简单来说
输入:aaa\n
get:只读取aaa不读取回车,会导致下一个读取输入时读取 " \n "
getline:getline读取aaa\n,并将 " \n " 转换为 " \0 " 读取,所以最终读取的是 " aaa\0 "

下面就用一些实例说明一下:

char c;
c=cin.get();
cout<<c<<endl;

/*
输入:
a
输出:
a
*/
char c;
c=cin.get();
cout<<c<<endl

/*
输入:
a
输出:
a
*/
char s[10];
cin.get(s,10);
cout<<s<<endl;

/*
输入:
Hello world!
输出:
Hello wor
//还有一个空字符占据了位置,所以实际读入的只有9个有效字符
*/
char s[10];
cin.get(s,10);
cout<<s<<endl;
cin.getline(s,10);
cout<<s<<endl;

/*
输入:
Hello world!
输出:
Hello wor
ld!
*/

总结一下:

读入一行字符,选择cin.getlien(s,s_size)最稳,其中s_size要足够大,别抠抠索索的

然而此时我赫然发现我喜欢用的getline(cin,s);是属于C语言的,以后不用了/黑脸


C++char类型自带函数

这里我再补充一个不怎么常有但是看起来很拉嘣的函数
但是我私以为for循环+特判ta不香嘛/斜眼笑

strtok

函数原型:

char *strtok(char *s,const char *s0);

分解字符串为一组字符串
s为要分解的字符,s0为分隔字符(如果传入字符串,则传入的字符串中每个字符均为分割符)
首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL

不太明白?
没关系,看看代码就好了
没有什么问题不是看代码不能解决的,如果有,那就多看几遍

#include<iostream>

using namespace std;

int main()
{
	char sentence[]="This is a sentence with 7 tokens";
    char *p;
	cout<<"The string to be tokenized is :\n"<<sentence
		<<"\n\nThe tokens are:\n\n";
    p=strtok(sentence," ");
	while (p!=NULL) {
		cout<<p<<endl;
		p=strtok(NULL," ");
	}
	cout<<"\nAfter strtok,sentence ="<<sentence<<endl;
	system("pause");
	return 0;
}

在这里插入图片描述
最后解释一下为什么最后的输出是:This

每次strtok之后,分隔符的位置就被设置成了 " \0 "
在用cout输出流输出的时候,默认遇到 " \0 " 终止
在这里插入图片描述

发布了941 篇原创文章 · 获赞 192 · 访问量 32万+

猜你喜欢

转载自blog.csdn.net/wu_tongtong/article/details/103782701