C进阶语法(九)指针的进阶
指针基础
指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:
1.指针就是变量,是用来存地址的,地址是唯一标识一块内存空间
2.指针的大小是固定的4/8个字节(由平台决定,32位/64位)
3.指针是有类型的,通常类型有char、int 、short、long、float、double
4.指针类型决定了指针±整数的步长,指针解引用操作的时候的能够访问空间的大小
5.指针的运算。
- 指针±整数(==>指针指向的是该地址的上一个或下一个地址)
- 指针-指针(指向同一内存空间,可得指针之间的元素个数)
- 指针的关系运算(比较地址大小)
字符指针
指针类型中存在一种字符指针char*
一般使用:
int main() |
另一种使用方式:
int main() |
注:
(一)常量指针int const* p 和 const int* p
const 放在指针变量的 * 左边时,表示该指针所指向的数据(地址内的内容)是常量,即不能通过该指针修改所指向的数据。但p所指向的地址可以变。
>int num = 10;
>const int *p = # // 将const放在*的左边,表示所指向的数据是常量
>*p = 20; // 编译错误,因为p所指向的数据是常量
>p++; // 合法,p本身不是常量在上面的例子中,p 是一个指向 num的常量指针,即不能通过 p修改 num的值。但是可以通过 p 修改其指向的地址。
注**:int const* p定义了一个指向 const int类型数据的指针变量 p,也就是说,p 所指向的数据是一个常量,不能通过 p修改其所指向的数据**。但是 p本身不是常量,可以指向其他的 const int类型数据。
(二)指针常量int* const p
const放在指针变量的*右边时,表示指针本身是常量。它指向的地址是不可改变的,但地址里的内容可以通过指针改变。
>int num = 10;
>int *const p = # // 将const放在*的左边,并且指针本身也是常量
>*p = 20; // 合法,可以通过p来修改所指向的数据
>p++; // 编译错误,因为p本身是常量,不能修改其指向的地址在上面的例子中,int* const p就是一个指针常量,表示定义了一个指向 int 类型的指针变量 p,且这个指针变量是一个常量,其值(地址)不能被改变,但它所指向的 int类型数据(地址内的内容)可以被修改。
总结:
一般是根据靠近原则来看,const修饰p那么就是指针本身值(即指向的地址)不变,const修饰*p那么就是指指针指向的变量值(指向的值)不变
加深记忆记住三句话:
指针和 const 谁在前先读谁 ;
*象征着地址,const象征着内容;
谁在前面谁就不允许改变。
int main() |
这里p1和p2指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以arr11和arr2不同,p1和p2不同。 来源《剑指offer》
指针数组
预备知识:
1.&arr-数组名-此时数组名不是首元素的地址–数组名表示整个数组–&数组名 取出的是整个数组的地址。
2.sizeof(arr)-sizeof(数组名)-数组名表示整个数组-sizeof(数组名)计算的是整个数组的大小。
除1、2以外,数组名表示数组的首元素的地址
>int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组```
```c
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
int* parr[] = { arr1,arr2,arr3 };
return 0;
}
指针数组用法:
int main() |
数组指针
数组指针的定义
数组指针是指针
整形指针:int * pint; 能够指向整形数据的指针。浮点型指针:float * pf; 能够指向浮点型数据的指针。数组指针:能够指向数组的指针
int *p1[10];//==int* p1[10] 指针数组 |
指针数组,对于语句“int* p1[10]”,因为“[]”的优先级要比“*”要高,所以 p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,而“int*”修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 10个指向 int 类型数据的指针,如图 1 所示,因此,它是一个指针数组。
图 1
数组指针,对于语句“int(*p2)[5]”,“()”的优先级比“[]”高,“*”号和 p2 构成一个指针的定义,指针变量名为 p2,**而 int 修饰的是数组的内容,即数组的每个元素。**也就是说,p2 是一个指针,它指向一个包含 10 个 int 类型数据的数组,如图 2 所示。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。
图 2
由此可见,
对指针数组来说,首先它是一个数组,数组的元素都是指针,也就是说该数组存储的是指针,数组占多少个字节由数组本身决定;而对数组指针来说,首先它是一个指针,**它指向一个数组,**也就是说它是指向数组的指针,>在 32 位系统下永远占 4 字节,至于它指向的数组占多少字节,这个不能够确定,要看具体情况。
int main() |
例:
int main() |
&数组名VS数组名
&arr是取的整个数组的地址
数组指针用法
用法1:
int(*pa)[10] = &arr; |
用法2:地址传参
二维数组看作一维数组,则其数组名就是首元素的地址
arr[i] == *(a+i) == *(p+i) == p[i] |
例:
(*(p + i) + j);//(p + i)找到n维数组第i行的地址,(*(p + i) + j)找到n维数组第i行的第j列元素的地址 |
// 参数是数组的形式 |
练习:
int arr[5];//arr是一个含5个元素的整形数组 |
数组参数、指针参数
一维数组传参
|
二维数组传参
二维数组的数组名是首元素的地址,传参时,传入的是数组第一行地址
void test(int arr[3][5]) |
一级指针传参
|
思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
1.void test1(int *p)
{}
//test1函数能接收什么参数? test1(&a);或test1(p1);
int main()
{ int a=10;
int* p1=&a;
test1(&a);
test1(p1);
return 0;
}
2. void test2(char* p)
{}
//test2函数能接收什么参数? test2(&ch);或test2(str);
int main()
{ char ch='w';
char* str=&ch;
test2(&ch);
test2(str);
return 0;
}
二级指针传参
|
思考:
当函数的参数为二级指针的时候,可以接收什么参数?
>void test(char **p)
>{
>}
>int main()
>{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char* arr[10];//指针数组,数组里每个元素都是一级指针
test(&pc);
test(ppc);
test(arr);//Ok?//ok//传过去的是arr数组首元素的地址
return 0;
>}
函数指针
&函数名 和 函数名 都是函数的地址
函数指针定义示例:
int Add(int x, int y)
{}
int (*pa)(int ,int ) = Add;
printf("%d\n", (*pa)(2, 3));
//函数指针 - 是指向函数的指针 -存放函数地址的指针 |
用法:
void Print(char* str) |
课件:
void test() |