【C++_02】史上最全,一文讲透指针 (指针概念、const指针、指针和数组、指针和函数、数组指针、指针数组、字符指、函数指针)

本文深入介绍了C++中的指针,包括指针的概念、直接访问与间接访问、指针的类型定义与赋值、const指针、指针与一维数组的关系、堆内存分配、指针函数、二维数组的指针访问以及动态内存分配。通过实例展示了如何使用指针进行数组排序、转置和动态分配。此外,还探讨了字符指针、函数指针以及typedef的作用。

1. 指针概念

1、变量的地址

我们知道,在计算机的语言里,变量都存在地址,比如定义变量int num,那么使用取地址符 &num,我们就可以得到num这个变量在计算机中的地址。

同样的,对于数组,比如定义一个数组 int num[10],同样使用 &num[0]来得到数组num的首地址。这里需要注意的是,数组名 num 本身也表示这个数组的首地址。

2、直接访问和间接访问

举一个生活的例子:甲有要事找丙。

第一种情况:

如果甲知道丙的住址,那他直接去访问丙就可以了,这就是直接访问

第二种情况

那如果甲不知道丙的住址怎么办呢?假如这个时候刚好乙知道丙的住址,且甲有乙的地址(甲可直接访问乙),那么就可以产生以下操作:

  • 甲先访问乙,从乙处得到丙的住址
  • 甲按乙所提供的丙的地址去访问丙

这就是我们所说的间接访问,从而引出了指针的概念。

3、指针的概念

对应直接访问,变量的直接访问就是,int i ; i=3 来访问i(2000表示的变量i在计算机的地址)。
在这里插入图片描述
在计算机中,指针就相当于间接访问例子中的乙,我们从乙处得到丙的地址,再去访问丙。如下图所示:

int * i_pointer 表示的就是指针变量,指针里面存放的是其他变量的地址,而不是一个值(比如这里指针指向的是变量i,指针存放的不是i的值3,而是变量i在计算机里的地址2000)。因为存放的是地址,所以我们用一个*号来进行定义区分。

那么定义完了指针变量,这个指针存放的是哪个变量的地址呢?只需要将指针变量i_pointer指向变量i的地址即可。这里的指向我们使用的是=,而变量i的地址使用的是取地址符&i。

在这里插入图片描述
这样我们就通过两步完成了指针的定义:第一,定义指针变量;第二,将指针指向其他变量的地址进行赋值。也可以合并为一个语句,完成对变量i的间接访问。

int *i_pointer = &i

总结一下,

  • 指针:变量的地址称为该变量的指针。上述的 i_pointer就是指针,表示的是变量i的地址。
  • 指针变量:专门用来存放“另一个变量地址(即指针)”的变量。即上述的int *i_pointer就是指整形指针变量,存放的是变量i的地址。由于我们可以通过变量的i的地址来访问变量i的值,所以 *i_pointer表示的就是变量i的值。

*i_pointer是指针,是地址;i_pointer是指针变量,是值

4、指针的类型、定义与赋值

(1)指针的定义:*数据类型 指针变量名

  • 存放整型变量的地址–整型变量指针 int *ip
  • 存放整型常量的地址–整型常量指针 const int *icp
  • 存放字符变量的地址–字符变量指针 char *cp

(2)指针的赋值

我们知道变量必须先赋值,再使用。给指针变量赋初值,也叫做初始化指针变量,其实就是取地址。再次重复如下:

int *iPtr;      		//定义整型变量指针
int  iCount=18;		//定义整型变量
iPtr=&iCount;		//取iCount的地址存放到iPtr中

在VS中进行测试如下,所以只要理解*p是值,p是地址即可。

#include<iostream>
using namespace std;
int main()
{
    int count = 10;          //定义整型变量
    int *p;                  //定义整型变量指针,*p表示指针p指向的数据变量count
    p = &count;              //取count的地址存放到p中。取地址符,p表示的是地址

    cout << p <<" "          //0x7ff7befb15ac
         << &count << " "    //0x7ff7befb15ac
         << *p <<  " "       //10
         << count << endl;   //10

    double d = 2.333;        // 定义整型变量
    double *dp = &d;         // 也可以定义指针变量的同时赋值
    cout << dp << " "        //0x7ff7b99de5a8
         << &d << " "        //0x7ff7b99de5a8
         << *dp << " "       //2.333
         << d << endl;       //2.333
    
    return 0;

}

2. const指针

1、&—取地址运算

由于指针变量也是变量,所以由于指针指向的变量不同,指针变量也会改变。

#include<iostream>
using namespace std;
int main()
{
   int *p;
   int x=1,y=2;
   p = &x;    	            //p 指向 x 
   cout << *p <<endl;      // 输出1
   p = &y;    	            //p 指向 y, now 
   cout << *p <<endl;      //输出2
   
   return 0;

}

2、const指针

这就引出了const指针的知识点,根据const限定的是int变量还是指针p变量,可以分为如下三种情况

  • 指向常量的指针(常量指针):指针指向的对象i是常量,而指针本身p是变量。
    • const 数据类型 * 指针名 = &常量名
  • 指针常量:指针本身p是常量,而指针指向的对象i可以改变
    • 数据类型 * const 指针名 = &变量名
  • 指向常量的指针常量(常量指针常量):指针指向的对象和指针本身都是常量
    • const 数据类型 *const 指针名 = &常量名

我们接下来看几个例子

const int a =89,b=60; //a,b是常量,指针p是变量 
const int   *p=&a;    

p=&b;				//ok,p是变量,可以指向a,也可以指向b
*p=100;    			//error,*p表示的是a的值,这里a是常量,不能修改
int a =89,b=60;  	//  a,b是变量,p是常量,指向a的地址
int *const  p = &a;
p=&b;				//error,p是常量,已经指向a的地址了,不能再修改
*p=100;  			//ok,*p表示的是a的值,a是变量,可以修改
const int a =89,b;    
const int *const p = &a;
p=&b;	 	//error			
*p=100;		//error,同理

3. 指针和一维数组的关系

既然我们可以通过i=1来直接访问i变量,为什么还需要通过指针来访问变量呢?确实,用指针来访问单变量是没有意义的,那指针有什么用呢?

1、指针运算

我们先来进行一下测试,输出数组每个元素的地址和值

#include<iostream>
using namespace std;
int main()
{

    const int N =5;

    int num[5] = {1,2,3,4,5};
    int *p = &num[0];

    for(int i=0;i<N;i++)
    {
        cout << "address:" << &num[i] << " " << p+ i <<endl;
        cout << "value:" << num[i] << " " << *(p+i) <<endl;
    }
    
    return 0;

}

运行结果如下图所示,可以看到

数组元素地址:&num[i] = p + i
数组元素值:num[i] = *(p + i )

且可以看到,因为这里的数组是int类型,每个数组元素间隔4个字节,所以&num[i] = p + i = p + sizeof(int)
在这里插入图片描述
这就是指针运算。指针可以进行加减运算,表示在内存中移动指针。一般将数组起始地址赋值给指针,通过指针运算(移动指针)对数组元素进行操作。

  • 指针数组赋值
int a[10];
int *p=&a[0]; //指针指向数组起始地址int *p=a;    	//数组名本身是数组的起始地址 //也称数组指针
  • 指针移动:
数据类型   *p = 值;
p+n = p + sizeof(数据类型)*n       //指向数组才有意义
p-n = p – sizeof(数据类型)*n        //向前、向后跳过的元素数
  • 指针相减
  p – q      /*中间间隔元素数,必须指向同一数组*/
  • 指针比较
 p == q, p<q, etc.            /*必须指向同一数组,前后关系*/

2、指针与数组

指针和一维数组的关系只需要理解两个核心点即可:

  • 指针指向一维数组时,指针当数组名使用

  • 根据[ ]变址运算符,a[i] = *(a+i)

基于这两个点,我们对数组元素的地址和值都有多种表示方法。

int a[10];
int *p=a; // 或者int *p=&a[0] 表示指针指向数组起始地址
&a[i] = a + i = &p[i] = p + i  // 数组元素地址的等价表示
a[i] = * (a + i) = p[i] = *(p + i)  // 数组元素值的等价表示

理解了指针和一维数组的关系,我们对数组进行求和也有了多种方法。

#include<iostream>
using namespace std;
int main()
{
    const int N = 5;

    int num[5] = {1,2,3,4,5};
    int *p;
    int sum, i;

    for(sum = 0, i = 0;i<N;i++) //最简单的方式
        sum += num[i];
    cout << sum << endl;  //输出15

    for(sum = 0,p = num;p < num + N;p++) //使用num来控制
        sum += *p;
    cout << sum << endl;  //输出15

    for(sum=0,i=0,p=num;i<N;i++) //使用i来控制
        sum += *(p+i);
    cout << sum << endl;    //输出15

    return 0;

3、堆分配内存

  • 堆内存 :存放程序动态数据的内存空间,允许程序根据需要申请一定大小的空间,使用完毕可以释放该空间。
分配:数据类型 *指针名=new 数据类型[下标表达式];
释放:delete []a;

例:1) int *a = new int[10];
       delete []a;
    2) int *a = new int[10]{1,2,3,4,5};
            delete []a;

  • 空指针
int *p = NULL; 
int *p = nullptr//使用的多,C++11定义     

4、指针与函数

函数传值和函数传指针

我们先来看一个例子

#include<iostream>
using namespace std;

void swap1(int x, int y)
{  
    int temp;
    
    temp = x; 
    x = y; 
    y = temp;
}

void swap2(int *px, int *py)//这里使用指针
{  
    int temp;
    
    temp = *px; 
    *px = *py; 
    *py = temp;
}

int main()
{
    int a=3,b=8;
    swap1(a,b);
    cout<<a<<" "<<b<<endl;   //3 8
    swap2(&a,&b);  //因为传的是指针,所以这里的参数要取地址,表示指针指向的变量的地址
    cout<<a<<" "<<b<<endl;   // 8 3 

    return 0;

}

上述例子可以看到,函数swap1在函数里传的是值,在主函数中调用swap1时并没有改变参数的值。函数传值相当于是备份,同文件复制,复制文件修改,不影响原文件。

而swap2在函数里传的是指针,在主函数中调用sawp2的时候参数的值改变了。函数传指针时,实参、虚参共享空间,同创建文件链接,所以会同时改变。

所以记住:

  • 传值不改变参数的值。
  • 传指针改变参数的值。

函数传数组

那么如果函数要传数组呢?前面已经讲过,针指向一维数组时,指针当数组名使用,所以传数组首地址,即指针。因此传指针也是传数组。对其进行递增递减的操作可访问数组元素。

我们来进行一个练习: 写函数sort,对数组a进行升序排序。
void sort( int *a, int size ) ,根据指针和数组关系,int *a为传数组。

#include <iostream>
#include <iomanip>
using namespace std;

//void input(int n, int num[])  输入数组
void input(int n, int *num)
{
	for (int i = 0; i < n; i++)
		cin >> num[i];
}

//void output(int n, int num[]) 输出数组
void output(int n, int *num)
{
	for (int i = 0; i < n; i++)
		cout << num[i] << " ";
}

//传地址改变参数值,交换函数
void swap1(int *a, int *b)
{
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;
}

//void sort(int n, int num[]),排序函数
void sort(int n, int *num)
{
	int i, j;
	for (i = 1; i < n; i++)
		for (j = 0; j < n - i; j++)
			if (num[j] < num[j + 1])
				swap1(&num[j], num + j + 1);   //num[j+1] = *(num+j+1)

}

int main()
{
	int n,i;

	cin >> n;
	int* p = new int[n]; //动态内存分配

	if (p == nullptr)    //C++11 判断是否是空指针,这里仅做测试用
	{
		cout << " new error!" << endl;
		exit(0);
	}

	input(n, p);
	cout << "--------" << endl;
	sort(n, p);
	output(n, p);

	delete []p; //释放内存
}
	

指针函数

返回指针的函数称为指针函数。返回的可以是单变量地址或者数组首地址,但是要保证返回的指针(地址)不能是局部指针变量,即要保证出了函数之后函数空间仍然没有释放。

#include <iostream>
#include <iomanip>
using namespace std;


//返回数组首地址,要保证地址不是局部变量,出了函数之后地址还在
char* createstr(int n)      
{
	
	//char str[100];         //error,注意:返回的指针可以是堆地址、全局或静态变量的地址,但不能是局部指针变量。
	//static char str[100];  // ok,保证出了这个函数后地址还存在
	int i;
	char* str = new char[100];
	for (i = 0; i < n; i++)
	{
		str[i] = 'A' + i;
	}
	str[i] = 0;
	return str; //返回数组首地址
}

int main()
{
	cout << createstr(10) << endl;	 //ABCDEFGHIJ
}

5、指针和二维数组

二维数组,可以理解为两个一维数组,如下图所示:

int   a[N][M];   //假设N,M为已定义的常量。
int   (a[N])[M];    //将A数组理解为两个一维数组 

在这里插入图片描述
虽然说是二维数组,但其实在计算机中是以连续地址进行存放的,我们来看下面这个例子:分别输出二维地址每个元素的地址。

#include<iostream>
using namespace std;

int main()
{
    int num[3][4] = {1,2,3,4,5,6,7,8,9};
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<4;j++)
        {      
            cout << & num[i][j]<<" ";
        }
        cout<<endl;
    }
    
}

输出结果如下,可以看到二维数组其实每个元素的地址都是连接在一起的,就像排队一样,只需要知道首地址,就可以通过加减运算访问二维数组的所有元素。

0x7ff7bd550580 0x7ff7bd550584 0x7ff7bd550588 0x7ff7bd55058c 
0x7ff7bd550590 0x7ff7bd550594 0x7ff7bd550598 0x7ff7bd55059c 
0x7ff7bd5505a0 0x7ff7bd5505a4 0x7ff7bd5505a8 0x7ff7bd5505ac 

接下来我们来看二维数组有哪些访问方式?

方式一:不使用指针,使用数组直接访问

从下面测试可以看出,num和num+1之间间隔了16个字节,也就是跨越了一行(1行4个int元素,1个int类型4个字节);num[0]和num[0]+1跨越了4个字节,也就是相邻元素。

int main()
{
    int num[3][4] = {1,2,3,4,5,6,7,8,9};
    int *p; //p+1=p+sizeof(int)

    cout<<num<<endl;     //0x7ff7b1836540
    cout<<num+1<<endl;	//0x7ff7b1836550 
    cout<<num[0]<<endl;	//0x7ff7b1836540
    cout<<num[0]+1<<endl;	//0x7ff7b1836544
}

方式二:使用指针,指针指向二维数组的首地址,也就是int * p;p=num[0]或者p = &num[0][0]

看下面这个测试,使用指针输出二维数组每个元素的地址和值。

#include<iostream>
using namespace std;

int main()
{
    int num[3][4] = {1,2,3,4,5,6,7,8,9};
    int *p; //p+1=p+sizeof(int)

    //p=num;            //error,num是行指针,对应一行,num+1跨越一行 
    p = num[0];         //指向一维数组首地址,通过num[0]访问整个二维数组
    //p = &num[0][0]    //这种方式也可以,都是指向首地址
    
    for(int i=0;i<3;i++)
    {
        for(int j=0;j<4;j++)
        {    
            
            cout<<"address "<<"row "<<i<<" "<<"col "<<j<<" "<<": "<<&num[i][j]<<" "<< p+i*4+j<<" "<<&p[i*4+j]<<endl; 
            cout<<"value "<<"row "<<i<<" "<<"col "<<j<<" "<<": "<<num[i][j]<<" "<<*(p+i*4+j)<<" "<<p[i*4+j]<<endl;
        }
        cout<<endl;
    }
    
}

运行结果如下:

address row 0 col 0 : 0x7ff7b89f8580 0x7ff7b89f8580 0x7ff7b89f8580
value row 0 col 0 : 1 1 1
address row 0 col 1 : 0x7ff7b89f8584 0x7ff7b89f8584 0x7ff7b89f8584
value row 0 col 1 : 2 2 2
address row 0 col 2 : 0x7ff7b89f8588 0x7ff7b89f8588 0x7ff7b89f8588
value row 0 col 2 : 3 3 3
address row 0 col 3 : 0x7ff7b89f858c 0x7ff7b89f858c 0x7ff7b89f858c
value row 0 col 3 : 4 4 4

address row 1 col 0 : 0x7ff7b89f8590 0x7ff7b89f8590 0x7ff7b89f8590
value row 1 col 0 : 5 5 5
address row 1 col 1 : 0x7ff7b89f8594 0x7ff7b89f8594 0x7ff7b89f8594
value row 1 col 1 : 6 6 6
address row 1 col 2 : 0x7ff7b89f8598 0x7ff7b89f8598 0x7ff7b89f8598
value row 1 col 2 : 7 7 7
address row 1 col 3 : 0x7ff7b89f859c 0x7ff7b89f859c 0x7ff7b89f859c
value row 1 col 3 : 8 8 8

address row 2 col 0 : 0x7ff7b89f85a0 0x7ff7b89f85a0 0x7ff7b89f85a0
value row 2 col 0 : 9 9 9
address row 2 col 1 : 0x7ff7b89f85a4 0x7ff7b89f85a4 0x7ff7b89f85a4
value row 2 col 1 : 0 0 0
address row 2 col 2 : 0x7ff7b89f85a8 0x7ff7b89f85a8 0x7ff7b89f85a8
value row 2 col 2 : 0 0 0
address row 2 col 3 : 0x7ff7b89f85ac 0x7ff7b89f85ac 0x7ff7b89f85ac
value row 2 col 3 : 0 0 0

可以看到,对于二维数组,由于变址运算符的存在,下面的三种访问方式是等价的。

address:&num[i][j] = p+i*4+j = &p[i*4+j]
value: num[i][j] = *(p+i*4+j) = p[i*4+j]

需要注意的是,对于二维数组来说,当指针指向数组首地址时,指针是不能当作数组名来使用的

需要注意的是,我们这里指针指向的都是数组头,但是其实指针是可以指向任何一个数组元素的。

可以看到,这种访问方式是很麻烦的,还要去算一行有多少个元素,再去加减。所以访问二维数组最好的方式是使用行指针。

第三种访问方式,行指针。它是一个指针,指向一维数组(它不是指向一个数,指向的是一行),叫数组指针。

定义方式是,*int num[][]; int(p)[4] = num;

需要注意的是,指针符号*和变址运算符[]结合再一起时,[]的运算优先级更高。int (*p)[4] 就表示p是一个指针变量,这个指针指向的变量类型是 int [4],也就是含有4个元素的一维数组,所以我们说这是一个行指针。

从下面的例子可以看出,对于二维数组来说,当使用行指针的访问方式时,指针相当于数组名(num+i = p+i)。所以我们说使用行指针访问二维数组是最方便的。

#include<iostream>
using namespace std;

int main()
{
    int num[3][4] = {1,2,3,4,5,6,7,8,9};

    //行指针, 它是一个指针,指向一维数组,叫数组指针
	int(*p)[4] = num;
	for (int i = 0; i < 3; i++)
		cout << num + i << " " << p + i << endl;
    
    return 0;  
}

/*输出结果
0x7ff7bcc55580 0x7ff7bcc55580
0x7ff7bcc55590 0x7ff7bcc55590
0x7ff7bcc555a0 0x7ff7bcc555a0
*/

行指针访问二维数组总结

所以,对于二维数组的访问方式总结一下。关键在于理解变址运算符和指针的等价关系,即对[]不断地进行变址。
在这里插入图片描述
我们使用程序来验证一下:

#include<iostream>
using namespace std;

int main()
{
    int num[3][4] = {1,2,3,4,5,6,7,8,9};

    int(*p)[4] = num;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 4; j++)
		{
			//address, &num[i][j]
			cout << &num[i][j] << " " << num[i]+j << " " << *(num+i) + j << " "<< &(*(num+i))[j] << endl;
			cout << &p[i][j] << " " << p[i] + j << " " << *(p + i) + j << " " << &(*(p + i))[j] << endl;

			//val, num[i][j]
			cout << num[i][j] << " " << *(num[i] + j) << " " << *(* (num + i) + j) << " "  << (*(num + i))[j] << endl;
			cout << p[i][j] << " " << *(p[i] + j) << " " << *(*(p + i) + j) << " " << (*(p + i))[j] << endl;
		}
    
    return 0;  
}
*/

这里我们回顾一下之前的一维数组访问方式。

address:&a[i] = a + i = &p[i] = p + i 
value:a[i] = * (a + i) = p[i] = *(p + i) 

例题:用指针实现转置

#include<iostream>
using namespace std;

int main()
{
	int num[4][4] = { 1,2,3,4,5,6,7,8,9 };
    int i, j;
	
    // 访问方式一:指针指向首地址
    int* p = num[0];
	
    //4*4矩阵,交换
    for(i=0; i<4; i++)
		for (j = 0; j < i; j++)
		{
			//[i][j],[j][i]
			swap(p[i * 4 + j], p[j * 4 + i]);
	}
    
    //输出
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			cout << *(p + i * 4 + j) << " ";
		}
		cout << endl;
	}
    
    // ——————————————————————————————————————————
    // 访问方式二:行指针
	int(*q)[4] = num;
	
    //4*4矩阵,交换
    for (i = 0; i < 4; i++)
		for (j = 0; j < i; j++)
		{
			//[i][j],[j][i]
			swap(q[i][j], q[j][i]);
		}
    
    //输出
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			cout << q[i][j] << " ";
		}
		cout << endl;
	}

    return 0;  
}

二维数组的动态内存分配

分配

//定义:分配n行m列的二维数组空间
int    **numArray;    

//第一步,分配n个元素,每个元素是指针
numArray = new int*[n]; 

//第二步,对n行的每个元素分配一维数组,含m个元素
for(int i=0; i<n; i++)
	 numArray[i] = new int[m];

释放

//释放n行m列的二维数组空间

//第一步,释放n行空间,即n个一维数组
for(int i=0; i<n; i++)
	 delete []numberArray[i];
	 
//第二步,释放最初分配的一维数组
delete  []numberArray;

例子

#include<iostream>
using namespace std;

int main()
{
	int i, j;
	int** p;
	int n, m;

	cin >> n >> m;

    //分配n行m列
	p = new int*[n];
	for (i = 0; i < n; i++)
		p[i] = new int[m];

	//输入,分配完就可以当作数组名使用了
    for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
			cin >> p[i][j];
	}
    
    //输出
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < m; j++)
			cout << p[i][j] << " ";
		cout << endl;
	}

    //释放
	for (i = 0; i < n; i++)
		delete[]p[i];
	delete[]p;

    return 0;  
}

但需要注意的是,动态分配后的二维数组不一定是连续,所以memcpy、memset不可用

memset(num, 0, sizeof(int) * 4 * 4);  //ok  , 连续空间ok
memset(p, 0, sizeof(int) * 4 * 4);   //error,不连续空间

6、数组指针(行指针)和指针数组(数组)

#include<iostream>
using namespace std;

int main()
{
    
    /*行指针, 它是一个指针,指向一维数组,叫数组指针
	int(*p)[4] = num;
	for (int i = 0; i < 3; i++)
		cout << num + i << " " << p + i << endl;
    */

	//p是数组,每一个元素是指针,叫指针数组 
	int *p[20];   
	int a = 10, b = 20, c = 30;
	p[0] = &a;
	p[1] = &b;
	p[2] = &c;
	
    for (int i = 0; i < 3; i++)
	{
		cout << p[i] << " " << *p[i] << endl;
		cout << &a << " " << a << endl;
	}  
    return 0;  
}

6、字符指针

字符数组和字符串常量

C++中的字面字符串有两种:

char buffer[]=“hello”;	//用于字符数组初始化,不占空间	
cout<<“good”<<endl;	//字符串常量,占有存储空间

当编译器遇到一字符串,就把它放到全局数据区的const区中,以’\0’作为结束符,并记下它的起始地址;

在以后的代码中,使用到该字符串就使用该地址,所以字符串就“变成”了地址。所以我们可以指针变量指向字符串常量。

char *p = “hello world”

我们经常将指针数组和字符串结合起来。这个例子的 const char * city[20] 就是一个指针数组,包含20个元素,每个元素都是一个指针,每个指针都指向字符串的首地址,但因为“beijing”等元素不能改变,所以需要加上const

#include<iostream>
using namespace std;

int main()
{ 
 const char * city[20] = {"beijing","wuhan","guangzhou" };

 for(int i=0;i<3;i++)
    cout << city[i] << " " << *city[i] <<endl;
}
/* 输出1:运行结果 
beijing b
wuhan w
guangzhou g
*/

C++中可以用字符串初始化字符数组,但不能对字符数组赋予字符串。

	char buffer[10]=“hello”;	//初始化,ok

	char buffer[10];
	buffer=“hello”;			//数组名赋值,error

	char *pstr;
	pstr=“hello”;			//字符指针赋值,ok

可以将字符串复制到字符数组,但不可以复制到字符指针。因为指针指向字符串的方法没有空间。

	char buffer[10];
	strcpy(buffer,“hello”);	 // ok	

	char *pstr;
	strcpy(pstr,"hello");	 //error,没有空间	

typedef类型定义

输入n, 输入n个城市名,输出其字典升序排序

为了解决这个问题,我们需要定义一个数组指针,指向一维数组,含20个字符。

//char (*p)[20];        //数组指针
//p = new char[20][n];  //error,没有这种定义,所以需要使用typedef

所以需要使用到typedef,它有两个作用:

作用一:为现有类型创建别名,定义易于记忆的类型名

typedef  long long  ll; 
// ll是longlong类型的别名
// longlong num; 与 ll num;等价

作用二:掩饰复合类型,如指针和数组

typedef  char name[20];
//name是含20个字符的数组类型的别名
//char cityName[20] ; 与 name cityName;等价

typedef int * int_pointer;
//int_pointer是int *类型的别名
//int *pointer;与  int_pointer pointer; 等价

在这里我们使用typedef来解决

#include<iostream>
#include<cmath>
using namespace std;

int main()
{

    typedef char name[20];
	name* p;

	int n;
	cin >> n;
	p = new name[n];
	for (int i = 0; i < n; i++)
		cin >> p[i];
	for (int i = 0; i < n; i++)
		cout << p[i] << endl;

    return 0;
}

/* 程序测试
3
beijing shenzhen shanghai 
beijing
shenzhen
shanghai
*/

7、函数指针

函数指针,它是指针,指向函数(两种方式指向函数,第一种是等于函数名,一种是指向函数取地址)。函数指针的声明:
返回值类型 ( * 指针变量名) ([形参列表]);

  • 返回值类型—函数的返回类型,
  • 形参列表—指针变量指向的函数所带的参数列表。
#include<iostream>
#include<cmath>
using namespace std;

int main()
{

    //函数指针,它是指针,指向函数
	//以系统自带的sqrt ,fabs为例

	double  (*fun)(double); //声明一个函数指针

	fun = sqrt;  //第一种方式:指针变量名等于函数名
	cout << fun(10) << endl; //调用
	fun = &fabs; //第二种方式:函数名取地址
	cout << fun(-10) << endl;
	
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ferry_xie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值