数组和指针的关系
- 数组名表示一种数据结构,这种数据结构就是数组,指针代表地址
- 数组名可以看作指针常量,不能作自增自减等操作,不能被修改
这就可以解释为什么我们如此定义时不能进行一般修改: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) {...}
之后在函数中就可以正常调用 了
#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类型的字符变量可以直接进行关系运算:
字符串初始化
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 " 终止