指針做函數參數的兩種情況

指針做函數參數的兩種情況

1.問題

不理解下面的代碼爲什麼可以用二級指針來接,不是應該用三級指針來接嗎??

映像中一直覺得,一級指針要用二級指針來接。

既n級指針要用n加1級指針來接。

所以第一反應是二級指針做實參,形參要三級指針來接。
在這裏插入圖片描述
!!!!!!!
在這裏插入圖片描述

!!!!!!

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

void print_array(char ** p, int n)
//void print_array(char * p[100], int n)
{
	//打印元素
	for (int i = 0; i < n; i++)
	{
		printf("%s, ", p[i]);
	}
	printf("\n");
}

void sort_array(char ** p, int n)
//void sort_array(char * p[200], int n)
{
	char *tmp;

	//排序,降序
	for (int i = 0; i < n - 1; i++)
	{
		for (int j = i + 1; j < n; j++)
		{
			if (strcmp(p[i], p[j]) < 0)
			{//字符串比較用strcmp
				tmp = p[i];
				p[i] = p[j];
				p[j] = tmp;
			}
		}
	}
}

//在函數內部,處理內存分配
char ** get_mem(int n)
{
	//堆區, 產生這麼一個數組 char * buf[3]
	//	一位數組,buf[3], 每個元素都是char *,    3 * sizeof(char *)
	//需要一個指針,指向首元素的地址,首元素是char *, 需要char **
	//之前是定義一個在棧區的數組, char *p[n], 現在數組在堆區
	char ** p = (char **)malloc(n * sizeof(char *));

	// char *p[]
	//讓堆區指針數組的每一個元素,都指向一塊堆區內存
	for (int i = 0; i < n; i++)
	{
		p[i] = (char *)malloc(10);
		sprintf(p[i], "%c%c%c%c", 'a' + i, 'a' + i, 'a' + i, 'a' + i);
	}

	return p; //返回值只能返回一個,如果通過參數修改,可以同時修改多個
}

void get_mem2(char *** tmp, int n)
{
	//堆區, 產生這麼一個數組 char * buf[3]
	//一位數組,buf[3], 每個元素都是char *,    3 * sizeof(char *)
	//需要一個指針,指向首元素的地址,首元素是char *, 需要char **
	//之前是定義一個在棧區的數組, char *p[n], 現在數組在堆區
	char ** p = (char **)malloc(n * sizeof(char *));

	// char *p[]
	//讓堆區指針數組的每一個元素,都指向一塊堆區內存
	for (int i = 0; i < n; i++)
	{
		p[i] = (char *)malloc(10);
		sprintf(p[i], "%c%c%c%c", 'a' + i, 'a' + i, 'a' + i, 'a' + i);
	}

	//間接操作內存
	*tmp = p;
}

void free_mem(char **p, int n)
{
	for (int i = 0; i < n; i++)
	{
		free(p[i]); //釋放的不是p[i]空間,是p[i]所指向的空間
		p[i] = NULL;//設置爲NULL, 說明這個指針沒有指向,空閒的
	}

	free(p);
	p = NULL;
}

void free_mem2(char ***tmp, int n)
{
	char ** p = *tmp; //把首元素地址拿過來
	for (int i = 0; i < n; i++)
	{
		free(p[i]); //釋放的不是p[i]空間,是p[i]所指向的空間
		p[i] = NULL;//設置爲NULL, 說明這個指針沒有指向,空閒的
	}

	free(p);
	//間接操作內存
	*tmp = NULL;
}

int main()
{
	//堆區, 產生這麼一個數組 char * buf[3]
	//	一位數組,buf[3], 每個元素都是char *,    3 * sizeof(char *)
	//需要一個指針,指向首元素的地址,首元素是char *, 需要char **
	//之前是定義一個在棧區的數組, char *p[n], 現在數組在堆區
	char ** p = NULL;
	int n = 3;
	//p = get_mem(n);
	get_mem2(&p, n); //地址傳遞, p是char **, &p就是char ***

	print_array(p, n); //打印

	sort_array(p, n); //排序

	print_array(p, n);

	//free_mem(p, n);
	//p = NULL;

	free_mem2(&p, n);//釋放內存,有沒有必要地址傳遞,好處在哪

	if (NULL == p)
	{
		printf("說明指針空閒可用\n");
	}

/*
	int a = func(); //通過返回值改變a的值

	int a, b, c, d;
	func(&a, &b, &c, &d);
*/


	printf("\n");
	system("pause");
	return 0;
}

2.理解

老師的答案是:主要看是不是要改變實參的值

當時不理解,因爲是老師一直強調要用多一級的指針來接的。
在這裏插入圖片描述

所以一直轉不過彎來。

主要的不同點在於:

不能把所有要接上一級指針都理解成1.定義的時候和2.當參數時要改變實參的值的情況

兩種情況:

1.定義指針變量時

當定義指針變量來接指針變量(看首元素類型)時,是需要用n加1級指針來接n級指針

2.指針作爲函數參數時

分爲2種情況

  • 當子函數要改變主函數實參的值時,需要用多一級指針來接。是指針傳遞

  • 當子函數不用改變主函數實參的值時,用同級指針來接。是值傳遞

    注意:傳值進去也沒問題,

    例子:值傳遞

    void my_add(int a, int b)
    {
    	int tmp = a;
    	a = b;
    	b = tmp;
    	printf("add.a=%d,add.b=%d\n", a, b);
    	printf("add.a=%p,add.b=%p\n", &a, &b);
    }
    
    int main(void)
    {
    	int a = 10;
    	int b = 20;
    	my_add(a, b);
    	printf("main.a=%d,main.b=%d\n", a, b);
    	printf("main.a=%p,main.b=%p\n", &a, &b);
    	
    	return 0;
    }
    

結果:主函數實參的值沒改變。

在這裏插入圖片描述
[外鏈圖片轉存失敗(img-4GdXoUhA-1564726800887)(vvvvvvvv.png)]

函數裏的a和b跟主函數的a和b的內存地址不一樣,證明他們不是操作同一塊內存,不是同一個數。

子函數裏的a和b只是拷貝了主函數的值。

例子:值傳遞

void my_add (int* a, int* b)
{
	int tmp = a;
	a = b;
	b = tmp;
	//printf("add.a=%p,add.b=%p\n", &a, &b);//err,變量a和b本來就是地址
	printf("add.a=%p,add.b=%p\n", a, b)
}

int main(void)
{
	int a = 10;
	int b = 20;
	my_add(&a,&b);
	printf("main.a=%d,main.b=%d\n", a, b);
	printf("main.a=%p,main.b=%p\n", &a, &b);
	
	return 0;
}

結果,主函數實參的值改變了。
在這裏插入圖片描述

子函數跟主函數的內存地址一樣,操作同一個內存。

結果,主函數實參的值改變了。

[外鏈圖片轉存中…(img-WXLnvBHZ-1564726800888)]

子函數跟主函數的內存地址一樣,操作同一個內存。

3.函數傳值的情況

https://blog.csdn.net/qq_33706673/article/details/84669784這篇文章講的還可以

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章