LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

c复习笔记(指针)

2021/4/13

呜呜呜到学cpp的时候发现c语言就学了个语法,内存编译那些还不是很熟,真对不起自己

还会再写一篇关于内存和编译的(大概)


数组语法(这东西有时候刷题都能写错)

int a[4] = {20, 345, 700, 22};
int nums[10] = {0};//将数组的所有元素初始化为 0
int a[] = {1, 2, 3, 4, 5};//如给全部元素赋值,那么在定义数组时可以不给出数组长度

C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。

1. char a[10];  //一维字符数组

2. char b[5][10];  //二维字符数组

3. char c[20]={'c', '  ', 'p', 'r', 'o', 'g', 'r', 'a','m'};  // 给部分数组元素赋值

4. char d[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' };  //对全体元素赋值时可以省去长度

C语言规定,可以将字符串直接赋值给字符数组,例如:

char str[30] = {"c.biancheng.net"};
char str[30] = "c.biancheng.net";  //这种形式更加简洁,实际开发中常用
char str[] = {"c.biancheng.net"};
char str[] = "c.biancheng.net";  //这种形式更加简洁,实际开发中常用

这里需要留意一个坑,字符数组只有在定义时才能将整个字符串一次性地赋值给它,一旦定义完了,就只能一个字符一个字符地赋值了。请看下面的例子:

char str[7];
str = "abc123";  //错误
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
str[3] = '1'; str[4] = '2'; str[5] = '3';
//正确

由” “包围的字符串会自动在末尾添加’\0’。例如,”abc123”从表面看起来只包含了 6 个字符,其实不然,C语言会在最后隐式地添加一个’\0’,这个过程是在后台默默地进行的,所以我们感受不到。


指针!!!(来了来了抓大头)

1.数组名本身表示数组的首地址

2.变量名和函数名是地址的一种助记符。CPU 访问内存时需要的是地址,而不是变量名和函数名。当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。

3.指针也没那么难那么绕啦,只不过就是储存了某个p的地址而已,这个我们就叫指针指向了p或是它为p的指针

4.数组指针指向的是数组中的一个具体元素,而不是整个数组。

5.字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的数据有读取和写入的权限,而常量区的数据只有读取权限,没有写入权限。因此,字符数组在定义后可以读取和修改每个字符而字符串一旦被定义后就只能读取不能修改,任何对它的赋值都是错误的。

6.二维数组指针:
int (*p)[4] = a;

括号中的*表明 p 是一个指针,它指向一个数组,数组的类型为int [4],这正是 a 所包含的每个一维数组的类型。p 指向的数据类型是int [4],那么p+1就前进 4×4 = 16 个字节,p-1就后退 16 个字节。*(p+1)+1表示第 1 行第 1 个元素的地址。

7.指针数组和二维数组指针在定义时非常相似,只是括号的位置不同:

int *(p1[5]);  //指针数组,可以去掉括号直接写作 int *p1[5];
int (*p2)[5];  //二维数组指针,不能去掉括号

指针数组和二维数组指针有着本质上的区别:指针数组是一个数组,只是每个元素保存的都是指针,以上面的 p1 为例,在32位环境下它占用 4×5 = 20 个字节的内存。二维数组指针是一个指针,它指向一个二维数组,以上面的 p2 为例,它占用 4 个字节的内存。

8:函数指针的定义形式为:
returnType (*pointerName)(param list);

注意( )的优先级高于*,第一个括号不能省略,如果写作returnType *pointerName(param list);就成了函数原型,它表明函数的返回值类型为returnType *


指针理解小练习

1. int *p1[6];  //指针数组
2. int *(p2[6]);  //指针数组,和上面的形式等价
3. int (*p3)[6];  //二维数组指针
4. int (*p4)(int, int);  //函数指针

运算符的优先级非常容易混淆,它们的优先级从高到低依次是:

  • 定义中被括号( )括起来的那部分。
  • 后缀操作符:括号( )表示这是一个函数,方括号[ ]表示这是一个数组。
  • 前缀操作符:星号*表示“指向xxx的指针”。

答案:

1) int *p1[6];

从 p1 开始理解,它的左边是 *,右边是 [ ],[ ] 的优先级高于 *,所以编译器先解析p1[6],p1 首先是一个拥有 6 个元素的数组,然后再解析int *,它用来说明数组元素的类型。从整体上讲,p1 是一个拥有 6 个 int * 元素的数组,也即指针数组。

2) int (*p3)[6];

从 p3 开始理解,( ) 的优先级最高,编译器先解析(*p3),p3 首先是一个指针,剩下的int [6]是 p3 指向的数据的类型,它是一个拥有 6 个元素的一维数组。从整体上讲,p3 是一个指向拥有 6 个 int 元素数组的指针,也即二维数组指针。

为了能够通过指针来遍历数组元素,在定义数组指针时需要进行降维处理,例如三维数组指针实际指向的数据类型是二维数组,二维数组指针实际指向的数据类型是一维数组,一维数组指针实际指向的是一个基本类型;在表达式中,数组名也会进行同样的转换(下降一维)。

3) int (*p4)(int, int);

从 p4 开始理解,( ) 的优先级最高,编译器先解析(*p4),p4 首先是一个指针,它后边的 ( ) 说明 p4 指向的是一个函数,括号中的int, int是参数列表,开头的int用来说明函数的返回值类型。整体来看,p4 是一个指向原型为int func(int, int);的函数的指针。

4) char ( c[10])(int **p);

这个定义有两个名字,分别是 c 和 p,乍看起来 p 是指针变量的名字,不过很遗憾这是错误的。如果 p 是指针变量名,c[10]这种写法就又定义了一个新的名字,这让人匪夷所思。

以 c 作为变量的名字,先来看括号内部(绿色粗体):

char * (* c[10]) (int **p);

[ ] 的优先级高于 *,编译器先解析c[10],c 首先是一个数组,它前面的*表明每个数组元素都是一个指针,只是还不知道指向什么类型的数据。整体上来看,(* c[10])说明 c 是一个指针数组,只是指针指向的数据类型尚未确定。

跳出括号,根据优先级规则(() 的优先级高于 *)应该先看右边(红色粗体):

char * **(* c[10]) (int **p)**;

( )说明是一个函数,int **p是函数参数。

再看左边(橘黄色粗体):

char * **(* c[10]) (int **p)**;

char *是函数的返回值类型。

从整体上看,我们可以将定义分成两部分:

char * **(* c[10]) (int **p)**;

绿色粗体表明 c 是一个指针数组,红色粗体表明指针指向的数据类型,合起来就是:c 是一个拥有 10 个元素的指针数组,每个指针指向一个原型为char *func(int **p);的函数。

5) int (((*pfunc)(int *))[5])(int *);

从 pfunc 开始理解,先看括号内部(绿色粗体):

int ((**(*pfunc)**(int *))[5])(int *);

pfunc 是一个指针。

跳出括号,看它的两边(红色粗体):

int ((**(*pfunc)(int *)**)[5])(int *);

根据优先级规则应该先看右边的(int *),它表明这是一个函数,int *是参数列表。再看左边的*,它表明函数的返回值是一个指针,只是指针指向的数据类型尚未确定。

将上面的两部分合成一个整体,如下面的蓝色粗体所示,它表明 pfunc 是一个指向函数的指针,现在函数的参数列表确定了,也知道返回值是一个指针了(只是不知道它指向什么类型的数据)。

int (* (*(*pfunc)(int *)) [5])(int *);

蓝色粗体以外的部分,就用来说明函数返回什么类型的指针。

我们接着分析,再向外跳一层括号(红色粗体):

int (***** (*(*pfunc)(int *)) [5])(int *);

[ ] 的优先级高于 ,先看右边,[5] 表示这是一个数组,再看左边, 表示数组的每个元素都是指针。也就是说,* [5] 是一个指针数组,函数返回的指针就指向这样一个数组。

那么,指针数组中的指针又指向什么类型的数据呢?再向外跳一层括号(橘黄色粗体):

int (* (*(*pfunc)(int *)) [5]) (int *);

先看橘黄色部分的右边,它是一个函数,再看左边,它是函数的返回值类型。也就是说,指针数组中的指针指向原型为int func(int *);的函数。

将上面的三部分合起来就是:pfunc 是一个函数指针(蓝色部分),该函数的返回值是一个指针,它指向一个指针数组(红色部分),指针数组中的指针指向原型为int func(int *);的函数(橘黄色部分)。


写到这里就暂时完结啦~