【C语言】指针(二)

05-28 1083阅读

目录

一、传值调用和传址调用

二、数组名的理解

三、通过指针访问数组

四、一维数组传参的本质

五、指针数组

六、指针数组模拟实现二维数组


一、传值调用和传址调用

指针可以用在哪里呢?我们看下面一段代码:

#include 
void Swap(int x, int y)
{
	int z = x;
	x = y;
	y = z;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d b=%d\n", a, b);
	Swap(a, b);
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

运行结果如下:

【C语言】指针(二)

我们发现,并没有完成交换,调试发现:

【C语言】指针(二)

出现这样的原因:a和b是实参(是真实传递给函数的),x和y是形参,当实参ab传给形参xy时候,形参把实参数据临时拷贝了一份,x,y创建自己的独立空间,因为有自己独立的空间,跟a,b无关,所以修改形参不会影响实参。这种交换方式也叫传值调用。

所以说:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

措施:可以将a,b的地址传给Swap函数,Swap函数里通过地址间接的操作main函数中的a和b,达到交换的效果。也就是传址调用。

代码如下:

#include 
void Swap(int* pa, int* pb)//用指针变量来接收
{
	int z = *pa;
	*pa = *pb;
	*pb = z;
}
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d b=%d\n", a, b);
	Swap(&a, &b);//传地址
	printf("交换后:a=%d b=%d\n", a, b);
	return 0;
}

【C语言】指针(二)

传址调用,可以让函数和主调函数之间建立真正的联系,在函数内部可以修改主调函数中的变量。

  • 未来函数中只是需要主调函数中的变量值来实现计算,就可以采用传值调用。
  • 如果函数内部要修改主调函数中的变量的值,就需要传址调用。

    二、数组名的理解

    数组名就是数组首元素(第一个元素)的地址。

    代码如下:

    【C语言】指针(二)

    但有两个例外:

    1. sizeof(数组名),sizeof中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节。
    2. &数组名,这里的数组名表示整个数组,所以&数组名取出的是整个数组的地址

    除此之外,任何地方使用数组名,数组名都表示首元素的地址。

    可能会有疑问,为什么首元素地址(arr)跟整个数组地址(&arr)相同呢?

    代码如下:

    【C语言】指针(二)

    从值的角度来讲,地址就是一个编号,它们打印出来的地址一模一样,但是意义却不相同,操作arr和&arr带来的结果也是不一样的。

    如下代码:

    【C语言】指针(二)

    可以看出,&arr[0]和&arr[0]+1相差4个字节,arr和arr+1相差4个字节,是因为&arr[0]和arr都是首元素的地址,类型是int*,+1就是跳过一个整型(元素)。
    但是&arr和&arr+1相差40个字节,这就是因为&arr是数组的地址,+1操作是跳过整个数组的。

    也可以这么理解:

    arr与&arr都是指针,指针有两个要素:

    • 第一个是地址值,也就指向的位置,打印出来的就是地址值,arr与&arr的地址值是一样的。
    • 第二个是类型(所指向的数据类型),arr指向数组第一个元素,&arr指向数组arr,arr+1后的地址值会偏移一个元素的长度,&arr+1后的地址值会偏移一整个数组的长度,所以arr与&arr类型是不一样的。

      三、通过指针访问数组

      #include 
      int main()
      {
      	int arr[10] = { 0 };
      	int i = 0;
      	int sz = sizeof(arr) / sizeof(arr[0]);
      	int* p = arr;//p存放的数组首元素的地址
      	for (i = 0; i  
      

      可以发现,数组的下标引用操作符([ ])效果相当于解引用操作符(*)

      其实数组元素的访问在编译器处理的时候,也是转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的。

      四、一维数组传参的本质

      之前我们都是在函数外部计算数组的元素个数,那可以把函数传给一个函数后,在函数内部求数组的元素个数吗?

      我们来看一段代码:

      #include 
      void test(int arr[])
      {
      	int sz2 = sizeof(arr) / sizeof(arr[0]);
      	printf("sz2 = %d\n", sz2);
      }
      int main()
      {
      	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
      	int sz1 = sizeof(arr) / sizeof(arr[0]);
      	printf("sz1 = %d\n", sz1);
      	test(arr);
      	return 0;
      }

      运行结果如下:

      【C语言】指针(二)

      发现sz1计算错误,函数内部没有正确获得数组的元素个数。

      原因:

      数组名是数组首元素地址,传递的数组名,本质上数组传参传递的是数组首元素地址。

      所以函数的形参部分理论上应该用指针来接收首元素的地址。那么在函数内部写sizeof(arr)计算的是一个地址的大小(单位字节),而不是数组的大小(单位字节)。

      正因为函数的参数部分本质是指针,所以在函数内部是没办法求数组元素个数的。

      代码如下:

      void test(int arr[10]) //==>arr[]  参数写成数组形式,本质上还是指针
      {
      printf("%d\n", sizeof(arr)); //计算⼀个指针变量的⼤⼩
      }
      void test(int* arr) //参数写成指针形式
      {
      printf("%d\n", sizeof(arr));
      }
      int main()
      {
      int arr[10] = {1,2,3,4,5,6,7,8,9,10};
      test(arr);
      return 0;
      }

      上面数组传参的本质是传递了数组首元素的地址,所以形参访问的数组和实参的数组是同一个数组。

      形参的数组是不会单独再创建数组空间的,所以形参的数组是可以省略掉数组大小的。

      总结:一维数组传参,形参的部分可以写成数组的形式,也可以写成指针的形式。

      五、指针数组

      数组是一组相同类型元素的集合。数组有整形数组,字符数组.....

      指针数组:存放指针(地址)的数组。

      int* arr1[6];//存放整型指针的数组
      char* arr2[10];//存放字符指针的数组
      

      如下图:

      【C语言】指针(二)

      指针数组的每个元素是地址,又可以指向一块区域。

      六、指针数组模拟实现二维数组

      代码如下:

      #include 
      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数组中
      	int* parr[3] = { arr1, arr2, arr3 };
      	
      	int i = 0;
      	int j = 0;
      	for (i = 0; i  
       
      

      【C语言】指针(二)

      parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型一维数组,parr[i][j]就是整型一维数组中的元素。

      上述的代码模拟出二维数组的效果,本质上其实不是二维数组。

      因为二维数组在内存中是连续存放的,而这三个数组在内存可不一定连续存放。

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]