指針做函數參數的兩種情況
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這篇文章講的還可以