0.问题引入
int a = 5;
a = 1024; //把1024存放到变量a的地址中去
b = a; // 取变量a的值,赋值给b
===>在c语言中,任何一个变量,都有两层含义
(1)代表变量的存储单元的地址:变量的地址 ===> lvalue(也称变量的左值)
(2)代表该变量的值: ===> rvalue(也称变量的右值)
对于变量的访问,只有两种情况
read: 从变量的地址中取值(读)
write : 把一个数值,写到变量的地址中去(写)
问题:
如果我们知道了一个变量的地址,是不是我们就可以通过该变量的地址去访问这个变量
可以的
如何通过一个变量的地址去访问这个变量呢?
====> 指针
1.对象的访问方式
(1)直接访问:通过对象名去访问
直接访问有一个缺陷?
直接访问受对象的作用域的影响(限制)
// 局部变量不是永久的,函数返回的时候,局部变量就会被删除
(2)间接访问:通过对象的地址去访问,指针访问
只要你知道了对象的地址,就可以去任何地方访问它,不受作用域的影响
2.什么是指针?指针的概念是什么?
存储单元的地址:
分配给每一个变量的内存单元都有一个编号,这个编号就是我们说的存储单元的地址,
并且是按字节来编址。
上图是用指针指向一维数组首地址
在c语言中,指针的概念与地址的概念差不多,你可以认为指针就是一个地址,一个变量的地址我们也称为变量的指针。
& 取地址符:
单目运算符, ”取xxx对象的地址“
通过一个对象的指针去访问它,首先要解决 对象的指针(地址)的存储问题
这个时候需要定义一个变量来保存它的地址,这个变量我们称为”指针变量“
3.指针变量
什么是指针变量?
指针变量也是一个变量,也是用来保存数据的,只不过指针变量是用来保存其他对象的地址的。
如何定义一个指针变量?
首先我们来看一下普通变量的定义方式:
变量的类型 变量名;
在定义指针变量的时候,为了区分在指针变量的前面加一个*,来表示他是一个指针变量
====》
指针变量的定义:
指向对象的类型 * 对象名
”指向对象的类型“:
指针变量指向的是对象的类型,不是指针的类型。
”指向“:
假如一个变量p保存了变量a的地址,
那么,我们就说p指向a
例子:
int a = 1024;
p = &a ; // 把a的地址赋值给p
// p保存了对象a的地址
// p 指向a
// p ---> 指针变量
这个p该如何定义?
指向对象的类型 * 指针变量名
typeof(a) * p
===> int * p;
练习:
char c[10];
char * p;
p = &c[9];
分析过程
p保存了c[9]的地址;
====> p指向c[9]
p是一个指针变量
4.与指针相关的运算符
& :取地址符
* :指向运算符
二者都属于 单目运算符,优先级最高
用法:
* 地址 <=> 地址对应的那个变量(对象)
int a;
* a // ERROR
例子:
int a = 1024;
*(&a) ===> *对象a的地址
===> 地址对应的那个对象
int b = *(&a); <==> b = a
so
*(&a) <==> *&a <==> a
* & ===>可以直接约掉
NOTE:
& *a <==> a ???? 不可以
& * 不能约
char ch ;
* (&ch) = 'A'; // *(&ch) ==>ch ,在此处代表的是变量ch的左值,”变量的地址“
char c = *&ch; // *(&ch) 在此处代表的是变量ch的右值,”变量的值“
代码分析:
int a = 5;
假如我们要定义一个变量p,来保存a的地址,
int *p;
p = &a;
// 把变量a 的地址赋值给p
p是一个指针变量
那么,它也有左值,右值
p的右值:p的存储单元中的内容 ===> &a
p的左值:p本身存储单元的地址 ===> &p
* p :在此处p代表 p的右值,p的值
* p <==>*(&a) <==> a;
a = 1024; <==> *(&a) ==>1024 ==> *p =1024
请大家写一个程序,来证明p的右值就是&a?
#include <stdio.h>
void main()
{
int a=1024;
int *p=&a;
printf("%d\n",*p);//a的值
printf("%p\n",&a);//a的地址
printf("%p\n",p);//a的地址
printf("%p\n",&p);//p的地址
}//指针与所指变量的关系
5.指针变量作为函数的参数
练习: 如下函数该这么设计?该怎么调用
void func(int *x,int *y)
{
int temp;
temp = *x; // ===> temp = *(&a)
*x = *y; // *(&a) = *(&b)
*y = temp; // *(&b) = temp
}
void func1(int *x,int *y) /*不可以的,因为此时该函数的功能仅仅是改变指针的指向,并没有改变实际的值,因为 因为指针的交换并不影响实际变量的值。*/
{
int *temp;
temp = x;
x= y;
y = temp;
}
int main()
{
int a = 5;
int b = 6;
int *p = &a;
int *q = &b;
func(p,q); //调用这个函数的目的是:为了交换变量a和b的值
printf("a==%d\n",a);//6
printf("b==%d\n",b);//5
} void func(int *x,int *y)
{
int temp;
temp = *x; // ===> temp = *(&a)
*x = *y; // *(&a) = *(&b)
*y = temp; // *(&b) = temp
}
void func1(int *x,int *y) /*不可以的,因为此时该函数的功能仅仅是改变指针的指向,并没有改变实际的值,因为 因为指针的交换并不影响实际变量的值。*/
{
int *temp;
temp = x;
x= y;
y = temp;
}
int main()
{
int a = 5;
int b = 6;
int *p = &a;
int *q = &b;
func(p,q); //调用这个函数的目的是:为了交换变量a和b的值
printf("a==%d\n",a);//6
printf("b==%d\n",b);//5
}
传的还是”值“,传的还是”实参的值“,
”实参的值可能是某个对象的地址“
例子:
void func(int *a,int *b)
{
int *t; /*定义了一个指针t,但是t没有赋值,不知道这个t保存了
哪个对象的地址;不代表它里面没有值
如果是一个指针变量的话,不知道它指向的对象是谁
*t ==>t指向的那一个对象
如果我们去操作*t ,分为两种情况
read :
int m = *t; //有可能t指向的那一个对象不可读。
// 可能导致内存的非法访问 =>段错误
write:
*t = 1024;//有可能t指向的那一个对象不可写
// 可能导致内存的非法访问 =>段错误*/
*t = *a; //可能导致”段错误“
*a =*b;
*b = *t; //可能导致”段错误“
}
像这个例子中的t这样的指针,我们称之为”野指针“。
6. 野指针:
指向一个未知单元(未知对象)的指针,称之为”野指针“
野指针的成因:
(1)指针未初始化
int main()
{
int *p;//未初始化指针
*p=10;
return 0;
}
(2)指针的越界访问
int main()
{
int arr[10]={0};
int *p=arr;
for(int i=0;i<=10;i++)
{
*(p++);/*i从0-10一共有十一次循环,而数组arr只有十个元素,在进行第十一次循环时就会出现数组越界问题*/
/*在进行前十次循环时,p还不是野指针,因为此时并未出现数组越界,一旦第十一次循环开始,就会出现数组越界问题,p就会变成野指针,在Linux下运行的表现未"段错误"*/
}
}
使用野指针,可能造成内存的非法访问 ===> 段错误
int *p;
// *p = 1024; 属于操作野指针
// int b = *p ; 属于操作野指针
int a;
p = &a; // 不属于操作野指针
7.空指针
空指针:
NULL
在计算机中,地址为0的存储空间是不存在的
如果一个指针的值为0(NULL),表示这个指针指向了
空(NULL),像这中指向0(NULL)的指针,我们称之为
空指针
int *p = NULL;
p它不是野指针
段错误原因:内存的非法访问
(1)数组越界,可能导致段错误
int a[10];
a[100] = 1024;
(2)非法使用指针(指针乱指)(野指针)
6.数组与指针
数组元素与普通变量是一样的,也有自己的地址。
数组元素也有左值和右值,并且数组元素的地址相邻的。
===>数组名可以代表首元素的地址(首地址)。
例子:
int a[5];
a是一个数组名, 数组名a当作指针来看: a <===> &a[0]
//一维数组名就是这个数组的首地址
我要定义一个指针变量p,来保存a[0]的地址
该如何定义p?
int *p = &a[0] //OK
如果把数组名a当作指针来看, a ===> &a[0];
<===> p = a; // OK
a[0] = 1024;
<===> *p = 1024;
<===> *a = 1024;
p是指向a[0],那么能不能通过p去访问a[1]?
可以的
*p ===>*&a[0] ==>a[0]
a[0]和a[1]的地址是相邻的
p + 4 ==&a[1] ????不对的
/*因为p+1的意思是指针p向前移动一个它所指向单元的长度,而p指向int型数组,一个单位长度是4个字节*/
p + 1 ===> &a[1]
通过指针p来访问a[1];
指针做加减的问题: int a[5]
p + i (p是一个指针,i是一个整数)
不是简单的加减值,而是加减i个指向单元的长度
p+1 ===> 往后面挪了一个指向单元的长度
p = > &a[0]
p+1 => 往后面挪了一个int单元的长度
(p+1) = >&a[1]
例子:
有p = &a[0],把数值100赋值给a[0],有多少种表示方法?
int a[10];
int *p = &a[0];
a[0] = 100;
*p = 100;
*&a[0] = 100;
*a = 100;
**&p = 100;
**&a = 100;
p[0] = 100;
*(p+0) = 100;
*(a+0)= 100;
*(&a[1]-1) = 100;
.....
练习:
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &a[2];
#include <stdio.h>
void main()
{
int a[10]={1,2,3,4,5,6,7,8,9,10};
int *p=&a[2];
int i;
for(i=-2;i<8;i++)
{
printf("a==%d\n",*(p+i));
}
}
通过指针p去访问每一个元素的值,将其依次输出。
7.多维数组与指针
(1)在C语言中所有数组都是一维数组
(2)数组名可以当作指向第0个元素类型的指针常量,
并且数值上为第0个元素的首地址
假如有:
int a[3][4]
//int [4] a[3]
//二维数组a[3][4]可以看成三个拥有四个元素的一维数组
a[0] _ _ _ _
a[1] _ _ _ _
a[2] _ _ _ _
表达式 表达式的含义
a (1)当作指针
&a[0]
(2)代表整个数组
a[0] a[0]又是一个一维数组名
(1)代表为a[0]的整个数组
(2)当作指针
&a[0][0]
&a[0][0] 元素a[0][0]的地址
-------------------------------------------------
a+1 数组名a当作指针
==> &a[0] + 1
==> &a[1]
取整个一维数组a[1]的地址
&a[1] 取整个一维数组a[1]的地址
&a 数组名a只能代表整个数组
&a:取整个二维数组的地址
&a+1 数组名a代表整个数组
&a+1 : 往后面挪了整个二维数组a的长度(12个int)
------------------------------------------------
a[1]+2 a[1]是一个一维数组名,只能当作指针
===> &a[1][0] + 2
===> &a[1][2] :元素a[1][2]的地址
*(a+1)+2 *(&a[0]+1)+2
==> *(&a[1])+2
==> a[1] + 2
==> &a[1][0] + 2
==> &a[1][2] :元素a[1][2]的地址
------------------------------------------------
*(a[1]+2) a[1]是一个一维数组名,只能当作指针
==> *(&a[1][0] + 2)
==> *(&a[1][2])
==>a[1][2]:元素a[1][2]
*(*(a+1)+2) ==> *(*(&a[1])+2)
==>*(a[1]+2)
==>*(&a[1][0]+2)
==>*(&a[1][2])
==>a[1][2]:元素a[1][2]
指针常量:
指针本身不能够改变,但是指向的空间里面的内容是可以改变的
如: 数组名作为指针
int m,n;
int *const a = &m; // a就是一个指针常量
*a = 1024;// ok
a = &n ; // ERROR
常量指针:
是一个指向常量的指针,指向的对象是常量,那个对象是不能改变的
但是指针是可以改变的(也就是说可以保存其他的地址)
如: 字符串指针
int m = 1024,n;
const int *b = &m; ===> int const *a;
*a = 250;//ERROR
a = &n ; // ok
8.指针数组与数组指针
(1) 指针数组
指针数组是一个数组,只不过它里面的每一个元素都是相同类型的指针罢了!!!
定义数组:
数组元素的类型 数组名[元素个数]
例子:
int * p[4]; //指针数组
//定义了一个数组,数组名为p,里面含有4个元素
每一个元素类型都是 int *.
(2) 数组指针
数组指针是一个指针,只不过这个指针是用来指向一个数组的
(这个指针保存的地址是一个数组的地址罢辽!!!)
例子:
int a[4];
您能不能定义一个指针p来保存数组a的地址呢?
指针定义:
指向对象的类型 * p;
typeof(a) *p;
int [4] *p;
=>int (*p) [4];// p是一个指针,它指向的对象是一个int[4]类型的数组
4. 有int b[3][4]; 假如要定义一个指针变量p,
来保存b[0][0]的地址,该如何定义?
//int[4] b[3]
//b[0] _ _ _ _
//b[1] _ _ _ _
//b[2] _ _ _ _
typeof(b[0][0]) *p;
====> int *p;
p = &b[0][0];
//typeof(p) ==> int *
p = b[0]; // ok
9.字符串指针
字符串是什么?
字符串就是一串字符,在c语言中是没有字符串类型的。
C语言的字符串是通过 char *(字符型指针)来实现的。
C语言的字符串,是用”“(双引号)引起来的一串字符来表示。
并且字符串后面默认会加一个’\0',表示字符串结束的标志。
如;
”abcde“ ==>5
"1" ==> 1
'123' 不是字符串,也不是字符啊
'\012' 不是字符串 ,是字符
"\012" ==>是字符串,有一个字符
"" ==>是字符串,空串
只需要保存字符串的首地址(首字符的地址)就可以了
从首地址开始找到第一个’\0',前面的这些字符就是字符串里面的字符
C语言中的字符串(如:”sssssss“)保存在一个叫做.rodata(只读数据)的内存区域中。
如;
"12345"
在程序运行时,系统会为这个字符串在.rodata中分配6个字节大小的空间给它
”12345“的值,就是首字符的地址。
typeof(”12345“)
====》typeof(&’1‘)
====》typeof('1') *
====> const char *
例子:
char *p = "12345";
p保存的是字符串首字符的地址,&’1‘
那么咱们是不是可以通过指针p来访问’1‘呢
可以
因为: p = &’1‘;
char m = *p ; <===> m = '1'
*p = 'A';//ERROR 因为p指向的对象是一个常量,不可以改变的
字符串就相当于一个常量指针
问题:
字符’1‘和’2‘的地址是不是连续的呢?
是连续的
既然是连续的,那么咱们也可以通过指针p来访问’2‘
p = &’1‘;
p+1 ===>往后面挪了一个char类型的单元长度 《===》 &’2‘
*(p+1) ==> *(&'2') ===> '2'
printf("%c\n",*p); // 1
printf("%c\n",*(p+1));// 2
p+= 1; // OBJK
==>
p = p+1;
p = &'2';
*(p+1) = 'B';//ERROR
字符数组:
char s[5] ={'a','b','c','d','e'};
sizeof(s) == 5
char ch = *(s+1); <==> ch = s[1]
*(s+1) = 'B' ; // <===> s[1] = 'B'
s+= 1; //ERROR ==》 s = s+1 因为s是一个指针常量(数组名作为指针)为指针常量
char s[] = {'a','b','c','d','e'};
sizeof(s) == 5;
可以省略元素个数
char s[] = {"abcde"};
<===> char s[] = {'a','b','c','d','e','\0'};
sizeof(s) = 6;
*(s+1) = ’B‘ ; // 可以,因为s是一个字符数组,数组区域是可读可写的
printf(”%s\n“,(s+1)); //Bcde
%s -> char *
把后面的哪个地址(指针)当作是一个字符串的首地址,一个一个字符的输出,
直到遇到'\0'结束,’\0' 不打印
1.分析如下程序的输出结果
p = "abcde";
printf("%s\n",p);//abcde
char s[] = "12345";
s[1] = ’B‘;
printf("%s\n",s);//1B345
2.写一个函数,用来求一个字符串的长度(包含了多少个有效字符)
(1)确定函数名
My_Strlen : 用来求一个字符串的长度
(2)确定参数列表(形参列表)
const char *s
(3)确定返回值的类型
返回值:
有效字符的个数(int)
(4)代码的实现
int My_Strlen(const char *s)
{
int count = 0;
while(*s) // *s !='\0'
{
count ++;
s++;
}
return count;
}
int main()
{
char q="123\0a0345";
scanf("%s",q);
// int a ;
// a = My_Strlen();
// printf(a)
printf("%d\n",My_Strlen((const char *)q));
}
10.几个标准库里常用的字符串处理函数
#include <string.h>
int strlen(const char *s);
@s : 要计算长度的那一个字符串的首地址
const char * 类型: 表示在程序运行的过程中,指针指向的字符串不能被修改
返回值: 返回字符串的有效字符个数(不包括‘\0’);
例子:
strlen("abcde") == 5
char s[4] = {'1','0'};
sizeof(s) == 4
strlen(s) == 2
strlen("abcd\nabc") == 8
strlen("123\123abc\0abc") == 7
strlen("123\01a3abc\0abc") == 9
strlen("123\000abc") ==3
strlen("123\0x123") == 3
strlen("123\x123456gad") == 7
(2)strcpy/strncpy
字符串拷贝函数
NAME
strcpy, strncpy - copy a string
SYNOPSIS
#include <string.h>
char *strcpy(char *dest, const char *src);
strcpy: 用来把src指向的字符串,拷贝到dest指向的空间中去,直到遇到'\0'结束。
@dest: 目的地(dest必须是一个可写的空间)
@src : 从哪里来
返回值:
返回拷贝之后,目的地字符串的首地址
例子:
char s[6];
strcpy(s,"12345");
printf("%s\n",s); //12345
char *p = "abcde";
char *q = "12345";
strcpy(p,q); //ERROR
strcpy 有一个小小的BUG!!!
因为他没有考虑到数组越界的问题,有可能会导致内存的非法访问
char *strncpy(char *dest, const char *src, size_t n);
strncpy: 为了解决strcpy的这个bug的情况,功能是类似的,只不过
它顶多拷贝n个字符到 dest
到底拷贝多少字符?(<=n)
(1)遇到了\0拷贝结束的,此时\0也会被拷贝
(2)已经拷贝了n个字符(后面的\0不会自动拷贝,除非最后一个字符是\0)
例子:
char s[10];
strncpy(s,"1234567890",10);
strncpy(s,"123",10);
(3)strcmp/strncmp
字符串的比较函数
那么字符串该如何比较勒?
一个一个字符进行PK对应的ASCII的值
if c1 >c2
返回 >0
if c1 < c2
返回 < 0
if c1 == c2
则继续比较下一个字符,
如果全部相等则返回0
strcmp("123","ABC"); <0
strcmp("123","123\0ABC"); ==0
strcmp("1234","123"); >0
char *s = "GONG";
strncmp(s,"GONGJIANWEN",4) ==0
#include <string.h>
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
strncmp它的功能与strcmp类似,
只不过它只比较s1,s2前面的n个字符
(4)strcat/strncat
NAME
strcat, strncat - concatenate(连接) two strings
SYNOPSIS
#include <string.h>
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
strcat: 用来把src指向的字符串,拷贝到dest指向的字符串的末尾
@dest :指向目标字符串(一段可写的空间)
dest指向的存储空间必须要足够的大,why?(越界)
@src :指向原始字符串(表示将要被拷贝的那一个字符串)
返回值:
如果成功,返回连接后的字符串的首地址dest
如果失败,返回NULL
例子:
char s1[12] = {"ABCD"};
strcat(s1,"12345");
printf("%s\n",s1);//ABCD12345
当然这个函数也有一个小小的BUG,你懂的
so strncat 就是用来修复strcat的这个bug的
strncat :
把src指向的字符串拷贝到dest指定的字符串的末尾
但是最多拷贝n个字符
(1)遇到\0拷贝结束,此时\0也会被拷贝
char s1[12] = {"ABCD"};
strncat(s1,"12345",8);//ABCD12345
(2)如果要是没有遇到\0,但是以及拷贝了n个字符了,也结束
char s1[12]={"ABCD"};
strncat(s1,"123456789",8); //ABCD12345678
练习:
分析如下程序的输出结果
char s[12];
strcat(s,"12345");
printf("%s\n",s); //随机的值
写一个函数,把一个十进制的数字字符串,转成一个整数
"12345"
=>12345
"-12345"
=>-12345
"+12345"
=>12345
/*
my_atoi : 将一个十进制的数组字符串,转换成一个整数
@s :指向十进制数组字符串的 const char *s
返回值: 整数值
*/
int my_atoi(const char *s)
{
int minux = 0; //符号 0 --负数 1 -- 正数
int d = 0; // 保存当前位的数值
int num = 0; //整数的值
if(*s == '-')
{
minus = 0;
s++;
}
else if(*s == '+')
{
minus = 1;
s++;
}
else
{
minus = 1;
}
while(*s) // *s!='\0'
{
d = *s-'0';
num = num *10 +d;
s++;
}
if(minus==0)
{
num = num *(-1);
}
return num;
}
int main()
{
char s[256];
scanf("%s",s);
printf("%d\n",my_atoi((const char *)s);
//printf("%d\n",atoi());
}
其实呢,atoi这个函数是标准库中的一个函数,大家是可以直接去用的,
NAME
atoi, atol, atoll - convert a string to an integer
SYNOPSIS
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
在c语言中,不仅是变量,数组有地址,其实我们的函数也是有地址的。
只要是有地址的东西,那么我们就可以定义一个指针变量去保存这个地址
并且可以通过这个这个指针去访问指向的对象。
函数地址 ----> 函数指针
11.函数指针
int sum(int a,int b);
int (int , int)===>这个是一个类型,是用来描述一个类似与sum函数的!
int * abc(int a,float b)
{}
描述abc的类型:
函数的返回值类型 (函数的参数类型列表)
int * (int,float)
====> 是一个返回int*型,带一个int和float的函数类型
定义一个指针变量q,用来保存abc的地址:
指向对象的类型 *q;
typeof(abc) *q;
===>int * (int ,float) *q;
===>int * *q(int , float);
===>int * (*q)(int,float);
需要定义一个指针变量p,来保存函数sum的地址,该如何定义呢?
int (*p)(int,int);
指向对象的类型 *p;
typeof(sum)* p;
===>int (int,int)* p;
===>int (*p)(int ,int);
函数指针的定义方法;
指向函数的返回值类型 (*指针变量名)(指向函数的形参类型列表);
例子;
1. char ** func(void)
{}
请大家定义一个函数指针变量p,来保存函数func的地址
char ** (*p)(void);
2.请各位大佬定义一个指针变量q,来保存如下函数的地址
数组名a作为函数的参数,是当作指针来看的
=> &a[0] typeof(&a[0]) ==> int *
//int arr_sum(int * a,int n)
int arr_sum(int a[],int n);
{}
=> int (*q)(int *,int);
(2)该怎么将函数的地址赋值给函数指针变量呢?
p -> 函数指针
p = 函数的地址
函数的地址怎么去获取呢?
&对象名 ==> 取对象的地址
函数的地址:
&函数名
or
函数名:在c语言中,函数名本身就代表函数的首地址
例子:
int sum_array(int *a,int n)
{}
//定义一个函数指针p
int (*p)(int *,int);
//将函数sum_array的地址赋值给p;
p = &sum_array;
//or
p = sum_array;
//此时p指向函数sum_array
(3)怎么通过函数指针取访问这一个函数呢?
函数调用:
函数名(实参列表);
a,
p = sum_array;
sum_array(a,5);
<==> p(a,5);
b,
p = &sum_array;
*p = *&sum_array;
sum_array(a,5);
===>(*p)(a,5);
结论: 通过函数指针去调用指向的函数,有两种方式
p为函数的指针
(1)p(实参列表)
(2)(*p)(实参列表)
练习;
1.首先写一个函数用来求一维数组中所有元素之和
然后在main中定义一个指针p,通过p去调用sum_array.
#include <stdio.h>
//int sum_array(int a[],int n)
int sum_array(int *a,int n)
{
int i,sum=0;
for(i=0;i<n;i++)
{
sum += a[i];
}
return sum;
}
int main()
{
int m[10] = {0,1,2,3,4,5,6,7,8,9};
int (*p)(int *,int);
p = &sum_array;
int n = *p(m,10);
printf("%d\n",n);
}
12.数组作为函数参数问题
数组作为函数的参数的话,数组名都是当作指针来看!!!
把数组作为函数的形参:
一维数组
a,数组元素类型 数组名[] , 元素个数
b,数组元素类型 * 指针变量名 ,元素个数
二维数组
a,数组元素类型 数组名[][列数] ,行数
b,数组元素类型 (*指针变量名)[列数],行数
练习:
首先写一个函数(sum_2_array)用来求二维数组中所有元素之和,
然后,在main中定义一个函数指针q,通过q来调用sum_2_array.
int sum_2_array(int (*a)[4], int m)
{
int i, j ;
int sum = 0;
for(i = 0; i < m; i++)
{
for(j = 0; j < 4; j++)
{
sum += a[i][j];
}
}
return sum;
}
int main()
{
int m[10] = {0,1,2,3,4,5,6,7,8,9};
//定义一个函数指针p,来保存sum_array
int (* p)(int *, int );
//将函数的地址赋值给 p
p = &sum_array; //p = sum_array
//通过p来调用sum_array函数
int n = p(m, 10);
printf("%d\n", n);
int x = (*p)(m, 10);
printf("%d\n", n);
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
//定义一个函数指针q,来保存sum_2_array
int (*q)(int (*)[4], int);
//将函数的地址赋值给 q
q = &sum_2_array; //q = sum_2_array
//通过函数指针q来调用它所指向的那一个函数
int y = q(a, 3);
//int y = (*q)(a, 3);
printf("%d\n", y);
}
保存普通变量地址的 变量 ==> 指针变量
指针变量也有地址,那么保存指针变量的地址的 变量 ==> 二级指针
13.二级指针以及多级指针
int a= 1024;
可以定义一个指针变量p来保存a的地址
int *p;
p = &a; //p指向a
p本身也有地址,我们可以定义一个指针变量p2来保存p的地址:
typeof(p) *p2;
int * *p2;
// p2二级指针,它保存的是一个一级指针的地址
//要分清楚到底是几级指针,怎么去区分?
// QTMD,你只要知道他是一个指针,(保存的是谁的地址)就可以了
p2 = &p; //p2指向p
**p2 = **&p = *p = *&a = a