C语言入门---第九章 C语言指针

时间:2023-03-09 05:10:36
C语言入门---第九章 C语言指针

  没学指针就是没学C语言! 指针是C语言的精华,也是C语言的难点。

  所谓指针,也就是内存的地址,所谓指针变量,也就是保存了内存地址的变量。不过人们往往不会区分两者的概念,而是混淆在一起使用。

 =====指针的概念=======

  计算机中的所有数据都必须放在内存中,不同类型的数据占用的字节数不一样,例如int占用4个字节,char 占用1个字节。

  我们将内存中字节的编号称为地址或指针。

  一切都是地址

  C 语言用变量来存储数据,用函数来定义一段可以重复使用的代码,他们最终都要放到内存中才能供CPU使用。

  数据和代码都以二进制的形式存储在内存中,计算机无法从格式上区分某块内存中存储的是数据还是代码。当程序被加载到内存后,操作系统会给不同的内存块指定不同的权限,拥有读取和执行权限的内存块就是代码,而拥有读取和写入权限,也可能只有读取权限的内存块就是数据。

  CPU只能通过地址来取得内存中的代码和数据,程序在执行过程中会告知CPU要执行的代码以及要读写的数据的地址,如果程序不小心出错,在CPU要写入数据时给它一个代码区域的地址,就会发生内存访问错误。这种内存访问错误会被硬件和操作系统拦截,强制程序奔溃,程序员没有挽回的机会。

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

  需要注意的是,虽然变量名、函数名、字符串名和数组名在本质上是一样的,他们都是地址的助记符,但在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名,字符串名和数组名则表示的是代码块或数据块的首地址。

=====C指针变量的定义和使用(精华)=====

  1. 数据在内存中的地址也成为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。

  在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数,也可以是另外的一个普通变量或指针变量。

   例如:

    int a = 100;

    int *p_a = &a;

    在定义指针变量p_a 的同时对它进行初始化,并将变量a 的地址赋予它,此时p_a就指向了a.值得注意的是,p_a需要的一个地址,a 前面必须要加取地址符&,否则是不对

  * 是一个特殊符号,表明一个变量是指针变量,定义p1、p2 时必须带*。而给p1、p2 赋值时,因为已经知道了它是一个指针变量,就没有必要再加上*。

  也就是说,在定义指针变量时必须带*,给指针变量赋值时不能带*。

=====关于 * 和 & 的谜题

  假设有一个int 类型的变量a ,pa 是指向它的指针,那么*&a 和&*pa 分别是什么意思呢?

  &a : 取变量a 的地址(等价于pa),

  *(&a): 表示取这个地址上的数据,等价于 *pa, 仍然等于a

=======指针变量的运算(加、减和比较运算)====

  指针变量保存的是地址,地址本质上是一个整数,所以指针变量可以进行部分于是奶奶。例如:加法、减法、比较等。

===9.4 C语言数组指针 (指向数组的指针)====

  数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素。数组中的所有元素在内存中是连续排列的,震哥哥数组占用的是一块内存。

  定义数组时,要给出数组名和数组长度,数组名可以认为是一个指针,它指向数组的第0个元素。在C语言中,我们将第0个元素的地址称为数组的首地址。

===========

  求数组长度: int len = sizeof(arr) / sizeof(int);

  sizeof(arr) 会获得挣个数组所占用的字节数,sizeof(int) 会获得一个数组元素所占用的字节数,他们相除的结果就是数组包含的元素的个数,即数组长度。

============

  如果一个指针指向了数组,我们就称它为数组指针。

  数组指针指向的是数组中的一个具体元素,而不是整个数组,所以数组指针的类型和数组元素的类型有关。

======C语言字符串指针(指向字符串的指针)=====

    C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中。

  字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串只有读取权限,没有写入权限。

  除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如: char *str = “hhhhhhhhhhh”;

 字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第0个字符,我们通常将第0个字符的地址称为字符串的首地址。字符串中的每个字符的类型都是char ,所以 str 的类型也必须是 char*

===到底使用字符数组还是字符串常量===

 在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。

  获取用户输入的字符串就是一个典型的写入操作,只能使用字符数组,不能使用字符串常量。

  #include<stdio.h>

  int main() {

    char str[30];

    gets(str);

    printf("%s\n", str);

    return 0;

  }

========== 指针变量作为函数参数====

  在C语言中,函数的参数不仅可以是整数、小数、字符等具体的数据,还可以是指向他们的指针。用指针变量作函数参数可以将函数外部的地址传递到函数内部,使得在函数内部可以操作函数外部的数据,并且这些数据不会随着函数的结束而被销毁。

  像数组、字符串、动态分配的内存等都是一系列数据的集合,没有办法通过一个参数全部传入函数内部,只能传它们的指针,在函数内部通过指针来影响这些数据集合。

====代码示例:

  #include<stdio.h>

    void swap(int *p1, int *p2){

      int temp;  //临时变量

      temp = *p1;

      *p1 = *p2;

      *p2 = temp;

    }

  C语言为什么不允许直接传递数组的所有元素,而必须传递数组指针呢?

  参数的传递本质上是一次赋值的过程,赋值就是对内存进行拷贝。所谓内存拷贝,是将一块内存上的数据赋值到另一块内存上。

=======指针作为函数返回值===

  C语言允许函数的返回值是一个指针,我们将这样的函数称为指针函数。

  用指针作为函数返回值时需要注意的一点是,函数运行结束后会销毁在它内部定义的所有局部数据,包括局部变量、局部数组和形式参数,函数返回的指针请尽量不要指向这些数据,C语言没有任何机制来保证这些数据会一直有效,它们会在后续使用过程中可能会引发运行时错误。

====二级指针(指向指针的指针)====

  指针可以指向一份普通类型的数据,例如:int 、double 、char 等,也可以指向一份指针类型的数据,例如:int*、double* 、char*等。

  如果一个指针指向的是另外一个指针,我们就称它为二级指针,或者指向指针的指针。

=====C语言指针数组(数组每个元素都是指针)=====

  如果一个数组中的所有元素保存的都是指针,那么我们就称它为指针数组。指针数组的定义形式一般为:

  int *arr[] = {&a,&b,&c};

===

  #include<stdio.h>

  int main() {

    char  *str[3] = {"111","222"."333"};

  }

  需要注意的是,字符数组str中存放的是字符串的首地址,不是字符串本身,字符串本身位于其他的内存区域,字符数组是分开的。

  也只有当指针数组中每个元素的类型都是 char * 时,才能像上面那样给指针数组赋值,其他类型不同。

=========二维数组指针(指向二维数组的指针)====

  二维数组在概念上是二维的,有行有列,但在内存中所有的数组元素都是连续排列的,它们之间没有“缝隙”。

  为了更好的理解指针和二维数组的关系,我们先来定义一个指向a 的指针变量P:

  int(*p)[4] = a;

  括号中的*表明p 是一个指针,它指向一个数组,数组类型为int[4],这正是a 所包含的每个一维数组的类型。[]的优先级高于*,()是必须要加的,如果赤裸裸的写作int *p[4] ,那么应该理解为int *(p[4]),p就成了一个指针数组,并不是二维数组指针。

=====函数指针(指向函数的指针)====

  一个函数总是占用一段连续的内存区域,函数名在表达式中有时会被转换为该函数所在内存区域的首地址,这和数组名非常相似,我们可以把函数的首地址(或称为入口地址)赋予一个指针变量,使指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数。这种指针就是函数指针。

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

  returnType 为函数返回值类型。

====对C语言指针的总结====

  指针就是内存地址,C预压允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组,函数以及其他指针变量的地址。

  程序在运行过程中需要的是数据和指令的地址,变量名、函数名、字符串名和数组名在本质上是一样的,他们都是地址的助记符,在编写代码的过程中,我们认为变量名表示的是数据本身,而函数名、字符串名和数组名表示的是代码块或数据块的首地址,程序被编译和链接后,这些名字都会消失,取而代之的是他们对应的地址。

  定义:int *p: p 可以指向int 类型的数据,也可以指向类似 int arr[n]的数组。

     int **P : p为二级指针,指向 int * 类型的数据。

     int *p[n] : p为指针数组。[]的优先级高于*,所以应该理解为int *(p[n]);

====

  1.使用指针变量之前一定要初始化,否则就不能确定指针指向哪里,如果它指向的内存没有使用权限,程序就崩溃了。对于暂时没有指向的指针,建议赋值NULL。

  2.两个指针变量可以相减。如果两个指针变量指向同一个数组中的某个元素,那么相减的结果就是两个指针之间相差的元素个数。

  3.数组也是有类型的,数组名的本意是表示一组类型相同的数据。在定义数组时,或者和sizeof、&运算符一起使用时数组名才表示挣个数组,表达式中的数组名会被转换为一个指向数组的指针。

  

  

  

  

  .

  

  

  

  

    

的。