C语言指针_06

本文深入探讨了C语言中的指针概念,包括地址与指针的基本定义、指针变量的定义与使用、指针与数组的关系,以及如何利用指针进行数组元素的访问和操作。此外,还详细介绍了指针在函数参数传递中的应用,特别是如何通过指针修改数组内容。
摘要由CSDN通过智能技术生成

地址和指针的概念:

内存区的每一个字节有一个编号,这就是“地址”。如果在程序中定义了一个变量,对程序进行编译时,系统就会给这个变量分配内存单元。

在C语言中,对变量的访问有两种方式:

1)直接访问 例如:a = 6;

2)间接访问 例如:scanf("%d", &a);

调用函数时,把变量a的地址传给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到a变量中。

1     /*
2      *:取值操作符;
3      &:取址操作符
4      */
5     int i = 66;
6     int *pointer; // 注意:此处*,不是取值,而是定义指针变量!!
7     pointer = &i; // &取址操作符
8     printf("%d\n", *pointer); // *pointer 取值操作符

 

指针与指针变量

知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把变量的地址称为该变量的“指针”。

C语言中可以定义一类特殊的变量,这些变量专门用来存放变量的地址,称为指针变量

在定义指针变量是要注意两点:

1)指针变量前面的*,表示该变量的类型为指针型变量,其一般形式为:类型说明符 *变量名;

其中,*表示这个一个指针变量,变量名即为定义的指针变量名,类型说明符表示该指针变量所指向的变量的数据类型。

2)定义指针变量是“必须”指定基类型。注意:只有整型变量的地址才能放到指向整型变量的指针变量中;例如:

1    // 错误!!
2     float a;
3     int *pointer_1;
4     pointer_1 = &a;

指针变量只能存放地址!!

 1     // 通过指针变量访问整型变量
 2     int a = 88, b = 66;
 3     int *pointer_1, *pointer_2;
 4     pointer_1 = &a;
 5     pointer_2 = &b;
 6     
 7     printf("%d %d\n", a, b);
 8     printf("%d %d\n", *pointer_1, *pointer_2);
 9     /*
10      输出结果:
11      88 66
12      88 66
13      */

 关于“&” “*”说明:‘&’‘*’两个运算符的优先级别相同,但按自右向左方向结合;

定义一个指针变量 int *p;  p = &a[i]

引用一个数组元素,可以用:

1)下标法:如 a[i]

2)指针法:如*(a + i) 或 *(p + i)

 注意:数组名即“翻译成数组的第一个元素的地址”(编译器会把数组名编译为地址,数组第一个元素的地址)

 

对”&“和”*“运算符再做些说明:

1     int a = 100;
2     int *pointer_1;
3     pointer_1 = &a;

1)&*pointer_1 的含义是什么呢 ?

解析:‘&’和‘*’两个运算符的优先级别相同,但它俩都是右结合性(自右向左方向结合),因此先进行 *pointer_1的运算,即:*pointer_1 = a;在执行&运算,即:&a,为变量a的地址。

2)*&a 的含义是什么?

先进行 &a,为变量a的地址;在*运算,即取值运算,最终结果就是变量a;

3)(*pointer_1) ++ 相当于  a ++;  括号是必要的,因为’++‘ 和 ’*‘ 同一优先级别,而且也是右结合性。

数组,一个数组包含若干个元素,每个数组元素在内存中都占用存储单元,他们都有相应的地址,地址是连续的。

 

用数组名作为函数参数

1 // 编译时,是将arr按指针变量处理的,相当于将函数首部写成
2 // input(int *arr, 10);
3 // void input(int arr[], int n) 等价于 void input(int *arr, int n)
4 void input(int arr[], int n) {
5     printf("do something");
6 }

需要说明的是:C语言调用函数时,虚实结合的方法都是采用”值传递“方式,当用变量名作为函数参数时传递的是变量的值;当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传值的值是地址,所以要求形参为指针变量。

关于数组的一个常见错误:

 1 char *c_strcpy (char destination[],const char source[]);
 2 int main(void) {
 3     char str1[] = "this is a very long string";
 4     char str2[] = "this is a short string";
 5     strcpy(str2, str1);
 6     puts(str2);
 7     return 0;
 8 }
 9 
10 char *c_strcpy (char destination[], const char source[]) {
11     int size_of_array = sizeof source / sizeof source[0];
12     for (int i = 0; i < size_of_array; i++) {
13         destination[i] = source[i];
14     }
15     return destination;
16 }

出现错误的是第 11 行代码。我们知道,数组作为函数的参数,在本函数中 source 只是一个指针(地址,系统在本函数运行时,是不知道a所表示的地址有多大的数据存储空间,这里只是告诉函数:一个数据存储空间首地址),所以,sizeof source 的结果是指针变量 source 占内存的大小。一般在32位机上是4个字节。source[0] 是 int 类型,sizeof source 也是4个字节,所以,结果永远是1。
结论:获得数组长度,只能在数组定义所在的代码区中获取数组长度,才能获取正确的数组长度。

正确代码:

 1 char *c_strcpy (char destination[], const char source[], int len);
 2 int main(void) {
 3     char str1[] = "this is a very long string";
 4     char str2[] = "this is a short string";
 5     int len = sizeof(str1) / sizeof(str1[0]);
 6     c_strcpy(str2, str1, len);
 7     puts(str2);
 8     return 0;
 9 }
10 
11 char *c_strcpy (char destination[], const char source[], int len) {
12     for (int i = 0; i < len; i++) {
13         destination[i] = source[i];
14     }
15     return destination;
16 }

 

倒序输出数组中的值:

 1 int main(int argc, const char * argv[]) {
 2     int a[] = {34, 56, 89, 87, 1, 34, 67};
 3     int count = sizeof(a)/sizeof(int);
 4     int i;
 5     reverse_pointer(a, count);
 6 //    reverse_arr(a, count);
 7     for (i = 0; i < count; i ++) {
 8         printf("%d\n", a[i]);
 9     }
10     return 0;
11 }
12 
13 // 方法一:倒序输出数组中的值(指针作为参数)
14 void reverse_pointer(int *x, int n) {
15     int *p, *i, *j, temp, m;
16     m = (n - 1) / 2;
17     i = x;          // i指向数组的第一个元素
18     j = x - 1 + n;  // j指向数组的最后一个元素
19     p = x + m;      // p指向数组的中间一个元素,配对
20     for (; i < p; i ++ , j --) {
21         temp = *i;
22         *i = *j;
23         *j = temp;
24     }
25 }
26 
27 // 方法二:倒序输出数组中的值(数组作为参数)
28 void reverse_arr(int arr[], int len) { // 形参arr是数组名,在此处仅是指针
29     int temp, i, j, m;
30     m = (len -1 ) / 2;
31     for (i = 0; i < m; i ++) {
32         j = len - 1 - i;
33         temp = arr[i];
34         arr[i] = arr[j];
35         arr[j] = temp;
36     }
37 }

 

 1 // 找出数组中的最大值和最小值
 2 void max_min_value(int arr[], int n) {
 3     int *p, *array_end;
 4     array_end = arr + n;
 5     int max, min;
 6     max = min = *arr;
 7     for (p = arr + 1; p < array_end; p ++) {
 8         if (*p > max) {
 9             max = *p;
10         } else if (*p < min) {
11             min = *p;
12         }
13     }
14 }

 

归纳起来,如果有一个实参数组,想在函数中改变此数组中元素的值,实参与形参的对应关系有以下4种情况:

1) 形参和实参都用数组名,如

1 void func(int x[], int n) {
2     printf("do something");
3 }
4 
5 void main(void) {
6     int a[10];
7     func(a, 10);
8 }

2) 实参用数组名,形参用指针变量,如

1 void func(int *p, int n) {
2     printf("do something");
3 }
4 
5 void main(void) {
6     int a[10];
7     func(a, 10);
8 }

3) 实参形参都采用指针变量,如

1 void func(int *p, int n) {
2     printf("do something");
3 }
4 
5 void main(void) {
6     int a[10], *p;
7     func(p, 10);
8 }

4) 实参为指针变量,形参为数组名,如

1 void func(int x[], int n) {
2     printf("do something");
3 }
4 
5 void main(void) {
6     int a[10], *p;
7     func(p, 10);
8 }

 

 多维数组与指针:

多维数组元素的地址,定义一个二维数组

1 int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};

设二维数组的首行的首地址为2000,则:

 

 

一维数组里面存放的是二维数组元素的地址,二维数组里面存放的是二维数组元素的值!

 

 1     // 定义二维数组,三行四列
 2     int a[3][4] = {0, 1, 2, 3 , 4, 5, 6, 7, 8, 9, 10, 11};
 3     
 4     // 打印 a[0][0] 首元素地址
 5     printf("a=%p\n", a);
 6     printf("*a=%p\n", *a);
 7     printf("a[0]=%p\n", a[0]);
 8     printf("&a[0]=%p\n", &a[0]);
 9     printf("&a[0][0]=%p\n", &a[0][0]);
10     
11     printf("\n");
12     
13     // 打印 a[1][0] 的地址,第二行
14     printf("a+1=%p\n", a + 1);
15     printf("*(a+1)=%p\n", *(a + 1));
16     
17     printf("\n");
18     
19     // 打印 a[1][1] 的值
20     printf("*(a[1]+1)=%d\n", *(a[1]+1));
21     printf("*(*(a+1)+1)=%d\n", *(*(a+1)+1));
22     
23     // 把二维数组 a 分解为一维数组 a[0],a[1],a[2]之后,设 p 为指向二维数组的指针变量,可定义为:
24     int (*p)[4];
25     // 它表示 p 是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组 a[0],其值等于 a,a[0] 或 &a[0][0]
26     
27     // 而 p + i 则指向一维数组 a[i]
28     
29     // 结论: *(p + i) + j 是二维数组 i 行 j 列的元素的地址
30     // 而 *(*(p + i) + j) 是 i 行 j 列元素的值
31     
32     // 二维数组指针变量说明的一般形式:
33     类型说明符 (*指针变量名)[长度]
34     
35     // 其中“类型说明符”为所指数组的数据类型。
36     //    “*”表示其后的变量是指指针类型。
37     //    “长度”表示二维数组分别为多个一维数组时,一维数组的长度,也就是二维数组的列数

 

 1     // 用指针输出二维数组的值
 2     int a[3][4] = {0, 1, 2, 3 , 4, 5, 6, 7, 8, 9, 10, 11};
 3     int (*p)[4];
 4     int i, j;
 5     p = a;
 6     for (i = 0; i < 3; i ++) {
 7         for (j = 0; j < 4; j ++) {
 8             printf("%2d", *(*(p + i) + j));
 9         }
10     }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值